0


Spring Security多登录页面示例

在 Web 应用程序开发中,有两个单独的模块是很常见的 - 一个用于管理员用户,一个用于普通用户。每个模块都有一个单独的登录页面,并且可以与相同或不同的身份验证源相关联。换句话说,应用程序为不同类型的用户提供了多个登录页面:管理员和用户,或管理员和客户。在这篇 Spring Boot 文章中,我想与您分享如何使用 Spring Security 编写此类身份验证代码。在详细信息中,您将了解:

  • 如何为具有相同身份验证源的管理员用户和普通用户实现登录页面
  • 如何为具有不同身份验证源的管理员和客户实现登录页面

在这两种情况下,我还展示了不同 Spring Boot 和 Spring Security 版本的代码示例,即 Spring Boot 版本 2.7.0 及更早版本:Spring Boot 2.7.0+ 附带 Spring Security 5.7.1+,它不推荐使用WebSecurityConfigurerAdapter和仍然支持它的旧版本。请注意,为了简单起见,我在下面的代码示例中使用了 H2 内存中数据库和纯文本密码。实际上,您可以轻松地切换到物理数据库,例如MySQL。

1. 设置项目的依赖关系

如果使用 Maven,请确保为项目包含以下依赖项:这意味着我们使用 Spring Web、Spring Data JPA、Spring Security、Thymeleaf、Thymeleaf Extras for Spring Security 和 H2 数据库。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

<
dependency
>
    
<
groupId
>org.springframework.boot</
groupId
>
    
<
artifactId
>spring-boot-starter-data-jpa</
artifactId
>
</
dependency
>
<
dependency
>
    
<
groupId
>org.springframework.boot</
groupId
>
    
<
artifactId
>spring-boot-starter-security</
artifactId
>
</
dependency
>
<
dependency
>
    
<
groupId
>org.springframework.boot</
groupId
>
    
<
artifactId
>spring-boot-starter-thymeleaf</
artifactId
>
</
dependency
>
<
dependency
>
    
<
groupId
>org.springframework.boot</
groupId
>
    
<
artifactId
>spring-boot-starter-web</
artifactId
>
</
dependency
>
<
dependency
>
    
<
groupId
>org.thymeleaf.extras</
groupId
>
    
<
artifactId
>thymeleaf-extras-springsecurity5</
artifactId
>
</
dependency
>
<
dependency
>
    
<
groupId
>com.h2database</
groupId
>
    
<
artifactId
>h2</
artifactId
>
    
<
scope
>runtime</
scope
>
</
dependency
>

2. 使用相同的身份验证提供程序实现多个登录页面

在此方案中,我们将对管理员登录页面和用户登录页面进行编码 - 两者都共享相同的身份验证源(用户凭据存储在单个表中)。角色名称用于区分管理员用户 (ADMIN) 或普通用户 (USER)。因此,按如下方式定义角色枚举类型:并按如下方式对用户实体类进行编码:

1

2

3

package
net.codejava;
public
enum
Role { ADMIN, USER }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

package net.codejava;
import javax.persistence.*;
@Entity
@Table(name = "users")
public class User {
    
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    
private Integer id;
    
    
@Column(nullable = false, unique = true, length = 40)  
    
private String email;
    
    
@Column(nullable = false, unique = true, length = 10)
    
private String password;
    
    
@Enumerated(EnumType.STRING)
    
private Role role;
    
public User() {}
    
    
public User(String email, String password, Role role) {
        
this.email = email;
        
this.password = password;
        
this.role = role;
    
}
    
// getters and setters are not shown for brevity   
    
}

接下来,为数据访问层编写UserRepository接口代码:我们需要一些示例数据用于测试目的。因此,编写以下类,该类将在应用程序启动时初始化数据库:您会看到,它将 4 个用户保存到数据库中:其中两个具有角色 ADMIN,两个具有角色 USER - 密码为纯文本。要实现身份验证,请创建一个类型为UserDetails的类,如下所示:并按如下方式编写UserDetailsService类型的类:

1

2

3

4

5

6

7

8

9

package
net.codejava;
import
org.springframework.data.repository.CrudRepository;
import
org.springframework.stereotype.Repository;
@Repository
public
interface
UserRepository 
extends
CrudRepository<User, Integer> {
    
public
User findByEmail(String email); 
}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

package
net.codejava;
import
java.util.List;
import
org.springframework.boot.CommandLineRunner;
import
org.springframework.context.annotation.Bean;
import
org.springframework.context.annotation.Configuration;
@Configuration
public
class
DatabaseLoader {
    
private
UserRepository repo;
    
    
public
DatabaseLoader(UserRepository repo) {
        
this
.repo = repo;
    
}
    
@Bean
    
public
CommandLineRunner initializeDatabase() {
        
return
args -> {
            
User user1 = 
new
User(
"[email protected]"
, 
"david123"
, Role.ADMIN);
            
User user2 = 
new
User(
"[email protected]"
, 
"john2020"
, Role.ADMIN);
            
User user3 = 
new
User(
"[email protected]"
, 
"nam2022"
, Role.USER);
            
User user4 = 
new
User(
"[email protected]"
, 
"ravi2121"
, Role.USER);
            
            
repo.saveAll(List.of(user1, user2, user3, user4));
            
            
System.out.println(
"Database initialized"
);
        
};
    
}
}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

package
net.codejava;
import
java.util.ArrayList;
import
java.util.Collection;
import
java.util.List;
import
org.springframework.security.core.GrantedAuthority;
import
org.springframework.security.core.authority.SimpleGrantedAuthority;
import
org.springframework.security.core.userdetails.UserDetails;
public
class
CustomUserDetails 
implements
UserDetails {
    
private
User user;
    
    
public
CustomUserDetails(User user) {
        
this
.user = user;
    
}
    
@Override
    
public
Collection<? 
extends
GrantedAuthority> getAuthorities() {
        
List<SimpleGrantedAuthority> authorities = 
new
ArrayList<>();
        
        
authorities.add(
new
SimpleGrantedAuthority(user.getRole().toString()));
        
        
return
authorities;
    
}
    
@Override
    
public
String getPassword() {
        
return
user.getPassword();
    
}
    
@Override
    
public
String getUsername() {
        
return
user.getEmail();
    
}
    
@Override
    
public
boolean
isAccountNonExpired() {
        
return
true
;
    
}
    
@Override
    
public
boolean
isAccountNonLocked() {
        
return
true
;
    
}
    
@Override
    
public
boolean
isCredentialsNonExpired() {
        
return
true
;
    
}
    
@Override
    
public
boolean
isEnabled() {
        
return
true
;
    
}
}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

package
net.codejava;
import
org.springframework.beans.factory.annotation.Autowired;
import
org.springframework.security.core.userdetails.*s;
public
class
CustomUserDetailsService 
implements
UserDetailsService {
    
@Autowired
private
UserRepository repo;
    
@Override
    
public
UserDetails loadUserByUsername(String email) 
throws
UsernameNotFoundException {
        
User user = repo.findByEmail(email);
        
if
(user == 
null
) {
            
throw
new
UsernameNotFoundException(
"No user found with the given email"
);
        
}
        
        
return
new
CustomUserDetails(user);
    
}
}

为管理员用户实现登录页面

现在,创建一个 Spring 安全配置类,用于为管理员用户配置身份验证提供程序和登录页面。代码如下:你看,这个安全配置类扩展了WebSecurityConfigurerAdapter类,这意味着它应该与低于 2.7.0 的 Spring 引导版本和低于 5.7.1 的 Spring 安全版本一起使用。例如,Spring Boot 版本 2.3.4:让我解释一下这个配置类中的代码:它声明了两个 Spring bean,类型分别为UserDetailsService和PasswordEncoder,身份验证提供程序将使用它们来对用户进行身份验证。以下语句允许对应用程序主页的公共访问(所有人):因为在主页上,我们显示如下所示的登录选项:下面是主页的代码(src/main/resources/templates/index.html):以下语句将 Spring 安全性过滤器链应用于路径以 /admin/ 开头的所有请求:这意味着我们应该将所有管理页面放在 /admin/ path 下。下一条语句要求对所有请求进行身份验证 - 用户必须具有角色 ADMIN 才能访问管理页面:要了解配置登录页面的其余代码,请参阅本文。供您参考,以下是管理员登录页面 (templates/admin/admin_login.html) 的代码:管理员登录页面如下所示:如果您输入管理员用户的正确凭据(请参阅DatabaseLoader类),您将看到管理员主页:然后单击注销按钮注销。管理员主页 (templates/admin/admin_home.html) 的代码如下:如果您使用的是 Spring Boot 版本 >= 2.70 或 Spring Security 版本 >= 5.7.1,则需要对AdminSecurityConfig类使用以下代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

package
net.codejava.admin;
import
org.springframework.context.annotation.*;
import
org.springframework.core.annotation.Order;
import
org.springframework.security.config.annotation.web.builders.HttpSecurity;
import
org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import
org.springframework.security.core.userdetails.UserDetailsService;
import
org.springframework.security.crypto.password.*;
import
net.codejava.CustomUserDetailsService;
@Configuration
@Order
(
1
)
public
class
AdminSecurityConfig 
extends
WebSecurityConfigurerAdapter {
    
    
@Bean
    
public
UserDetailsService userDetailsService() {
        
return
new
CustomUserDetailsService();
    
}
    
    
@Bean
    
public
PasswordEncoder passwordEncoder() {
        
return
NoOpPasswordEncoder.getInstance();
    
}
    
@Override
    
protected
void
configure(HttpSecurity http) 
throws
Exception {
        
http.authorizeRequests().antMatchers(
"/"
).permitAll();
        
        
http.antMatcher(
"/admin/**"
)
            
.authorizeRequests().anyRequest().hasAuthority(
"ADMIN"
)
            
.and()
            
.formLogin()
                
.loginPage(
"/admin/login"
)
                
.usernameParameter(
"email"
)
                
.loginProcessingUrl(
"/admin/login"
)
                
.defaultSuccessUrl(
"/admin/home"
)
                
.permitAll()
            
.and()
            
.logout()
                
.logoutUrl(
"/admin/logout"
)
                
.logoutSuccessUrl(
"/"
);
    
}  
    
    
}

1

2

3

4

5

6

<
parent
>
    
<
groupId
>org.springframework.boot</
groupId
>
    
<
artifactId
>spring-boot-starter-parent</
artifactId
>
    
<
version
>2.3.4.RELEASE</
version
>
    
<
relativePath
/> 
<!-- lookup parent from repository -->
</
parent
>

1

http.authorizeRequests().antMatchers(
"/"
).permitAll();

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

<!DOCTYPE html>
<
html
>
<
head
>
<
meta
charset
=
"ISO-8859-1"
>
<
title
>Spring Multiple Login Pages Demo</
title
>
</
head
>
<
body
>
    
<
div
align
=
"center"
>
        
<
h2
>Welcome to CodeJava.net</
h2
>
        
<
h4
><
a
th:href
=
"/@{/admin/login}"
>Admin Login</
a
></
h4
>
        
<
p
/>
        
<
h4
><
a
th:href
=
"/@{/user/login}"
>User Login</
a
></
h4
>
    
</
div
>
</
body
>
</
html
>

1

http.antMatcher(
"/admin/**"
)

1

.authorizeRequests().anyRequest().hasAuthority(
"ADMIN"
)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

<!DOCTYPE html>
<
html
xmlns:th
=
"http://www.thymeleaf.org"
>
<
head
>
<
meta
charset
=
"ISO-8859-1"
>
<
title
>Admin Login - CodeJava.net</
title
>
</
head
>
<
body
>
<
form
th:action
=
"@{/admin/login}"
method
=
"post"
style
=
"max-width: 400px; margin: 0 auto;"
>
    
<
h2
>Admin Login - CodeJava.net</
h2
>
    
    
<
div
th:if
=
"${param.error}"
>
        
<
h4
style
=
"color: red"
>[[${session.SPRING_SECURITY_LAST_EXCEPTION.message}]]</
h4
>
    
</
div
>
            
    
<
table
>
        
<
tr
>
            
<
td
>E-mail: </
td
>
            
<
td
><
input
type
=
"email"
name
=
"email"
required /></
td
>
        
</
tr
>
        
<
tr
>
            
<
td
>Password: </
td
>
            
<
td
><
input
type
=
"password"
name
=
"password"
required /></
td
>
        
</
tr
>
        
<
tr
><
td
>&nbsp;</
td
></
tr
>
        
<
tr
>
            
<
td
colspan
=
"2"
align
=
"center"
><
input
type
=
"submit"
value
=
"Login"
/></
td
>
        
</
tr
>
    
</
table
>
</
form
>
</
body
>
</
html
>

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

<!DOCTYPE html>
<
html
xmlns:th
=
"http://www.thymeleaf.org"
>
<
head
>
<
meta
charset
=
"ISO-8859-1"
>
<
title
>Welcome to CodeJava.net Admin Control Panel</
title
>
</
head
>
<
body
>
    
<
div
align
=
"center"
>
        
<
h2
>Welcome to CodeJava.net Admin Control Panel</
h2
>
        
<
p
>Your user name is: <
b
>[[${#request.userPrincipal.principal.username}]]</
b
></
p
>
        
<
form
th:action
=
"@{/admin/logout}"
method
=
"post"
>
            
<
input
type
=
"submit"
value
=
"Logout"
/>
        
</
form
>      
    
</
div
>
</
body
>
</
html
>

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

package
net.codejava.admin;
import
org.springframework.context.annotation.Bean;
import
org.springframework.context.annotation.*;
import
org.springframework.core.annotation.Order;
import
org.springframework.security.config.annotation.web.builders.HttpSecurity;
import
org.springframework.security.core.userdetails.UserDetailsService;
import
org.springframework.security.crypto.password.*;
import
org.springframework.security.web.SecurityFilterChain;
import
net.codejava.CustomUserDetailsService;
@Configuration
@Order
(
1
)
public
class
AdminSecurityConfig {
    
    
@Bean
    
public
UserDetailsService userDetailsService() {
        
return
new
CustomUserDetailsService();
    
}
    
    
@Bean
    
public
PasswordEncoder passwordEncoder() {
        
return
NoOpPasswordEncoder.getInstance();
    
}
    
@Bean
    
public
SecurityFilterChain filterChain1(HttpSecurity http) 
throws
Exception {
        
http.authorizeRequests().antMatchers(
"/"
).permitAll();
        
        
http.antMatcher(
"/admin/**"
)
            
.authorizeRequests().anyRequest().hasAuthority(
"ADMIN"
)
            
.and()
            
.formLogin()
                
.loginPage(
"/admin/login"
)
                
.usernameParameter(
"email"
)
                
.loginProcessingUrl(
"/admin/login"
)
                
.defaultSuccessUrl(
"/admin/home"
)
                
.permitAll()
            
.and()
            
.logout()
                
.logoutUrl(
"/admin/logout"
)
                
.logoutSuccessUrl(
"/"
);
        
        
return
http.build();
    
}     
}

实现普通用户的登录页面

与管理员登录模块类似,我们需要为用户登录模块创建第二个安全配置类,如下所示(下面是 Spring Boot 版本 < 2.7.0 或 Spring Security < 5.7.1 的代码):如果您注意到,我们指定 AdminSecurityConfig 类的过滤顺序为 @Order(1),UserSecurityConfig类为 @Order(2).这意味着将有两个不同的 Spring 安全过滤器:一个用于管理模块,一个用于用户模块。两个筛选器不能具有相同的顺序。供您参考,以下是用户登录页面 (templates/user/user_login.html) 的代码:用户登录页面如以下屏幕截图所示:如果您输入普通用户的正确凭据,您将看到出现用户主页:供您参考,以下是用户主页 (templates/user/user_home.html) 的代码:为了使所有这些工作, 编写控制器类,如下所示:另外,如果您使用 Spring Boot 版本 >= 2.7.0 或 Spring Security >= 5.7.1,则需要按如下方式对UserSecurityConfig类进行编码:请注意,在此方法中,两个 Spring 安全性配置类共享相同的身份验证提供程序和密码编码器。如果您尝试在用户登录页面中使用管理员凭据,您将收到 403 错误,反之亦然。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

package
net.codejava.user;
import
org.springframework.context.annotation.Configuration;
import
org.springframework.core.annotation.Order;
import
org.springframework.security.config.annotation.web.builders.HttpSecurity;
import
org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
@Order
(
2
)
public
class
UserSecurityConfig 
extends
WebSecurityConfigurerAdapter {
    
@Override
    
protected
void
configure(HttpSecurity http) 
throws
Exception {
        
        
http.antMatcher(
"/user/**"
)
            
.authorizeRequests().anyRequest().hasAuthority(
"USER"
)
            
.and()
            
.formLogin()
                
.loginPage(
"/user/login"
)
                
.usernameParameter(
"email"
)
                
.loginProcessingUrl(
"/user/login"
)
                
.defaultSuccessUrl(
"/user/home"
)
                
.permitAll()
            
.and()
            
.logout()
                
.logoutUrl(
"/user/logout"
)
                
.logoutSuccessUrl(
"/"
);    
    
}  
}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

<!DOCTYPE html>
<
html
xmlns:th
=
"http://www.thymeleaf.org"
>
<
head
>
<
meta
charset
=
"ISO-8859-1"
>
<
title
>User Login - CodeJava.net</
title
>
</
head
>
<
body
>
<
form
th:action
=
"@{/user/login}"
method
=
"post"
style
=
"max-width: 400px; margin: 0 auto;"
>
    
<
h2
>User Login - CodeJava.net</
h2
>
    
    
<
div
th:if
=
"${param.error}"
>
        
<
h4
style
=
"color: red"
>[[${session.SPRING_SECURITY_LAST_EXCEPTION.message}]]</
h4
>
    
</
div
>
        
    
<
table
>
        
<
tr
>
            
<
td
>E-mail: </
td
>
            
<
td
><
input
type
=
"email"
name
=
"email"
required /></
td
>
        
</
tr
>
        
<
tr
>
            
<
td
>Password: </
td
>
            
<
td
><
input
type
=
"password"
name
=
"password"
required /></
td
>
        
</
tr
>
        
<
tr
><
td
>&nbsp;</
td
></
tr
>
        
<
tr
>
            
<
td
colspan
=
"2"
align
=
"center"
><
input
type
=
"submit"
value
=
"Login"
/></
td
>
        
</
tr
>
    
</
table
>
</
form
>
</
body
>
</
html
>

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

<!DOCTYPE html>
<
html
xmlns:th
=
"http://www.thymeleaf.org"
>
<
head
>
<
meta
charset
=
"ISO-8859-1"
>
<
title
>Welcome to CodeJava.net</
title
>
</
head
>
<
body
>
    
<
div
align
=
"center"
>
        
<
h2
>Welcome to CodeJava.net User Home</
h2
>
        
<
p
>Your user name is: <
b
>[[${#request.userPrincipal.principal.username}]]</
b
></
p
>
        
<
form
th:action
=
"@{/user/logout}"
method
=
"post"
>
            
<
input
type
=
"submit"
value
=
"Logout"
/>
        
</
form
>      
    
</
div
>
</
body
>
</
html
>

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

package
net.codejava;
import
org.springframework.stereotype.Controller;
import
org.springframework.web.bind.annotation.GetMapping;
@Controller
public
class
MainController {
    
@GetMapping
(
""
)
    
public
String viewHomePage() {
        
return
"index"
;
    
}
    
    
@GetMapping
(
"/admin/login"
)
    
public
String viewAdminLoginPage() {
        
return
"admin/admin_login"
;
    
}
    
    
@GetMapping
(
"/admin/home"
)
    
public
String viewAdminHomePage() {
        
return
"admin/admin_home"
;
    
}
    
    
@GetMapping
(
"/user/login"
)
    
public
String viewUserLoginPage() {
        
return
"user/user_login"
;
    
}
    
    
@GetMapping
(
"/user/home"
)
    
public
String viewUserHomePage() {
        
return
"user/user_home"
;
    
}  
}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

package
net.codejava.user;
import
org.springframework.context.annotation.*;
import
org.springframework.core.annotation.Order;
import
org.springframework.security.config.annotation.web.builders.HttpSecurity;
import
org.springframework.security.web.SecurityFilterChain;
@Configuration
@Order
(
2
)
public
class
UserSecurityConfig {
    
@Bean
    
public
SecurityFilterChain filterChain2(HttpSecurity http) 
throws
Exception {
        
        
http.antMatcher(
"/user/**"
)
            
.authorizeRequests().anyRequest().hasAuthority(
"USER"
)
            
.and()
            
.formLogin()
                
.loginPage(
"/user/login"
)
                
.usernameParameter(
"email"
)
                
.loginProcessingUrl(
"/user/login"
)
                
.defaultSuccessUrl(
"/user/home"
)
                
.permitAll()
            
.and()
            
.logout()
                
.logoutUrl(
"/user/logout"
)
                
.logoutSuccessUrl(
"/"
);
        
        
return
http.build();
    
}  
}

3. 使用不同的身份验证提供程序实现多个登录页面

在此方案中,我们将实现管理员登录页面和客户登录页面 - 每个页面都使用自己的身份验证源。这意味着管理员身份验证使用用户表中的凭据,客户身份验证使用客户表中的凭据。首先,更新用户实体类(删除角色枚举),如下所示:然后更新CustomUserDetails类的getAuthority() 方法:CustomUserDetailsService类的代码保持不变。并添加表示客户用户的 Customer 实体类:并按如下所示创建CustomerRepository接口:对于示例数据库,对DatabaseLoader类进行编码,如下所示:您会看到,此代码保留了两个管理员用户和两个具有纯文本密码的客户。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

package
net.codejava.admin;
import
javax.persistence.*;
@Entity
@Table
(name = 
"users"
)
public
class
User {
    
@Id
@GeneratedValue
(strategy = GenerationType.IDENTITY)
    
private
Integer id;
    
    
@Column
(nullable = 
false
, unique = 
true
, length = 
40
)  
    
private
String email;
    
    
@Column
(nullable = 
false
, length = 
10
)
    
private
String password;
    
public
User() {}
    
    
public
User(String email, String password) {
        
this
.email = email;
        
this
.password = password;
    
}
    
    
// getters and setters are not shown for brevity
}

1

2

3

4

@Override
public
Collection<? 
extends
GrantedAuthority> getAuthorities() {
    
return
null
;
}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

package
net.codejava.customer;
import
javax.persistence.*;
@Entity
@Table
(name = 
"customers"
)
public
class
Customer {
    
@Id
@GeneratedValue
(strategy = GenerationType.IDENTITY)
    
private
Integer id;
    
    
@Column
(nullable = 
false
, unique = 
true
, length = 
40
)  
    
private
String email;
    
    
@Column
(nullable = 
false
, length = 
10
)
    
private
String password;
    
    
@Column
(nullable = 
false
, length = 
30
)
    
private
String fullName;
    
public
Customer() { }
    
    
public
Customer(String email, String password, String fullName) {
        
this
.email = email;
        
this
.password = password;
        
this
.fullName = fullName;
    
}
    
// getters and setters are not shown for brevity   
}

1

2

3

4

5

6

7

8

9

package
net.codejava.customer;
import
org.springframework.data.repository.CrudRepository;
import
org.springframework.stereotype.Repository;
@Repository
public
interface
CustomerRepository 
extends
CrudRepository<Customer, Integer> {
    
public
Customer findByEmail(String email); 
}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

package
net.codejava;
import
java.util.List;
import
org.springframework.boot.CommandLineRunner;
import
org.springframework.context.annotation.*;
import
net.codejava.admin.*;
import
net.codejava.customer.*;
@Configuration
public
class
DatabaseLoader {
    
private
UserRepository userRepo;
    
private
CustomerRepository customerRepo;
    
public
DatabaseLoader(UserRepository userRepo, CustomerRepository customerRepo) {
        
this
.userRepo = userRepo;
        
this
.customerRepo = customerRepo;
    
}
    
@Bean
    
public
CommandLineRunner initializeDatabase() {
        
return
args -> {
            
User user1 = 
new
User(
"[email protected]"
, 
"david123"
);
            
User user2 = 
new
User(
"[email protected]"
, 
"john2020"
);
            
            
userRepo.saveAll(List.of(user1, user2));
            
            
Customer customer1 = 
new
Customer(
"[email protected]"
, 
"alex123"
, 
"Alex Stevenson"
);
            
Customer customer2 = 
new
Customer(
"[email protected]"
, 
"peter246"
, 
"Peter Senkovski"
);
            
            
customerRepo.saveAll(List.of(customer1, customer2));
            
            
System.out.println(
"Database initialized"
);
        
};
    
}
}

为管理员用户实现登录页面

要实现管理模块的身份验证,请按如下方式对AdminSecurityConfig类进行编码(对于 Spring 引导版本 >= 2.7.0 或 Spring Security >= 5.7.1):请注意,HTML 页面的代码与上一节中所示的代码相同。如果您使用 Spring 引导版本 < 2.7.0 或 Spring 安全< 5.7.1,请使用以下代码作为安全配置类:如您所见,我们定义了一个身份验证提供程序,以便为管理员登录提供单独的身份验证源。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

package
net.codejava.admin;
import
org.springframework.context.annotation.*;
import
org.springframework.core.annotation.Order;
import
org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import
org.springframework.security.config.annotation.web.builders.HttpSecurity;
import
org.springframework.security.core.userdetails.UserDetailsService;
import
org.springframework.security.crypto.password.*;
import
org.springframework.security.web.SecurityFilterChain;
@Configuration
@Order
(
1
)
public
class
AdminSecurityConfig {
    
@Bean
    
public
UserDetailsService userDetailsService() {
        
return
new
CustomUserDetailsService();
    
}
    
@Bean
    
public
PasswordEncoder passwordEncoder() {
        
return
NoOpPasswordEncoder.getInstance();
    
}
    
@Bean
    
public
DaoAuthenticationProvider authenticationProvider1() {
        
DaoAuthenticationProvider authProvider = 
new
DaoAuthenticationProvider();
        
authProvider.setUserDetailsService(userDetailsService());
        
authProvider.setPasswordEncoder(passwordEncoder());
        
return
authProvider;
    
}
    
@Bean
    
public
SecurityFilterChain filterChain1(HttpSecurity http) 
throws
Exception {
        
http.authenticationProvider(authenticationProvider1());
        
http.authorizeRequests().antMatchers(
"/"
).permitAll();
        
http.antMatcher(
"/admin/**"
)
            
.authorizeRequests().anyRequest().authenticated()
            
.and()
            
.formLogin()
                
.loginPage(
"/admin/login"
)
                    
.usernameParameter(
"email"
)
                    
.loginProcessingUrl(
"/admin/login"
)
                    
.defaultSuccessUrl(
"/admin/home"
)
                
.permitAll()
            
.and()
            
.logout()
                
.logoutUrl(
"/admin/logout"
)
                
.logoutSuccessUrl(
"/"
);
        
return
http.build();
    
}
}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

package
net.codejava.admin;
import
org.springframework.context.annotation.*;
import
org.springframework.core.annotation.Order;
import
org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import
org.springframework.security.config.annotation.web.builders.HttpSecurity;
import
org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import
org.springframework.security.core.userdetails.UserDetailsService;
import
org.springframework.security.crypto.password.*;
@Configuration
@Order
(
1
)
public
class
AdminSecurityConfig 
extends
WebSecurityConfigurerAdapter {
    
@Bean
    
public
UserDetailsService userDetailsService() {
        
return
new
CustomUserDetailsService();
    
}
    
@Bean
    
public
PasswordEncoder passwordEncoder() {
        
return
NoOpPasswordEncoder.getInstance();
    
}
    
@Bean
    
public
DaoAuthenticationProvider authenticationProvider1() {
        
DaoAuthenticationProvider authProvider = 
new
DaoAuthenticationProvider();
        
authProvider.setUserDetailsService(userDetailsService());
        
authProvider.setPasswordEncoder(passwordEncoder());
        
return
authProvider;
    
}
    
@Override
    
public
void
configure(HttpSecurity http) 
throws
Exception {
        
http.authenticationProvider(authenticationProvider1());
        
http.authorizeRequests().antMatchers(
"/"
).permitAll();
        
http.antMatcher(
"/admin/**"
)
            
.authorizeRequests().anyRequest().authenticated()
            
.and()
            
.formLogin()
                
.loginPage(
"/admin/login"
)
                    
.usernameParameter(
"email"
)
                    
.loginProcessingUrl(
"/admin/login"
)
                    
.defaultSuccessUrl(
"/admin/home"
)
                    
.permitAll()
                
.and()
                    
.logout()
                        
.logoutUrl(
"/admin/logout"
)
                        
.logoutSuccessUrl(
"/"
);
    
}
}

为客户实现登录页面

与管理模块类似,对 CustomerUserDetails类进行编码,如下所示:以及CustomerUserDetailsService类的代码:下面是客户身份验证模块的安全配置类的代码,适用于 Spring 引导版本 >= 2.70 或 Spring 安全 >= 5.7.1:如果您使用 Spring 引导版本 < 2.70 或 Spring 安全版本 < 5.7.1, 对CustomerSecurityConfig类使用以下代码:如您所见,我们定义了不同的UserDetailsService、PasswordEncoder和AuthenticationProvider,以便为客户登录模块使用单独的身份验证源。并且视图页面和控制器类的代码与前面的代码类似。这是一些代码示例,向您展示如何使用 Spring 安全性实现多个登录页面,涵盖了两种场景:管理员用户和普通用户共享同一个身份验证提供程序;以及使用不同身份验证源的管理员用户和客户用户。希望这篇文章对您有所帮助。感谢您 reading.PS:要了解编码的实际效果,我建议您观看以下视频:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

package
net.codejava.customer;
import
java.util.Collection;
import
org.springframework.security.core.GrantedAuthority;
import
org.springframework.security.core.userdetails.UserDetails;
public
class
CustomerUserDetails 
implements
UserDetails {
    
private
Customer customer;
    
public
CustomerUserDetails(Customer customer) {
        
this
.customer = customer;
    
}
    
@Override
    
public
Collection<? 
extends
GrantedAuthority> getAuthorities() {
        
return
null
;
    
}
    
@Override
    
public
String getPassword() {
        
return
customer.getPassword();
    
}
    
@Override
    
public
String getUsername() {
        
return
customer.getEmail();
    
}
    
@Override
    
public
boolean
isAccountNonExpired() {
        
return
true
;
    
}
    
@Override
    
public
boolean
isAccountNonLocked() {
        
return
true
;
    
}
    
@Override
    
public
boolean
isCredentialsNonExpired() {
        
return
true
;
    
}
    
@Override
    
public
boolean
isEnabled() {
        
return
true
;
    
}
}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

package
net.codejava.customer;
import
org.springframework.beans.factory.annotation.Autowired;
import
org.springframework.security.core.userdetails.*;
public
class
CustomerUserDetailsService 
implements
UserDetailsService {
    
@Autowired
private
CustomerRepository repo;
    
    
@Override
    
public
UserDetails loadUserByUsername(String email) 
throws
UsernameNotFoundException {
        
Customer customer = repo.findByEmail(email);
        
if
(customer == 
null
) {
            
throw
new
UsernameNotFoundException(
"No customer found with the given email."
);
        
}
        
        
return
new
CustomerUserDetails(customer);
    
}
}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

package
net.codejava.customer;
import
org.springframework.context.annotation.*;
import
org.springframework.core.annotation.Order;
import
org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import
org.springframework.security.config.annotation.web.builders.HttpSecurity;
import
org.springframework.security.core.userdetails.UserDetailsService;
import
org.springframework.security.crypto.password.*;
import
org.springframework.security.web.SecurityFilterChain;
@Configuration
@Order
(
2
)
public
class
CustomerSecurityConfig {
    
@Bean
    
public
UserDetailsService customerUserDetailsService() {
        
return
new
CustomerUserDetailsService();
    
}
    
@Bean
    
public
PasswordEncoder passwordEncoder2() {
        
return
NoOpPasswordEncoder.getInstance();
    
}
    
@Bean
    
public
DaoAuthenticationProvider authenticationProvider2() {
        
DaoAuthenticationProvider authProvider = 
new
DaoAuthenticationProvider();
        
authProvider.setUserDetailsService(customerUserDetailsService());
        
authProvider.setPasswordEncoder(passwordEncoder2());
        
return
authProvider;
    
}
    
@Bean
    
public
SecurityFilterChain filterChain2(HttpSecurity http) 
throws
Exception {
        
http.authenticationProvider(authenticationProvider2());
        
http.antMatcher(
"/customer/**"
)
            
.authorizeRequests().anyRequest().authenticated()
            
.and()
            
.formLogin()
                
.loginPage(
"/customer/login"
)
                
.usernameParameter(
"email"
)
                
.loginProcessingUrl(
"/customer/login"
)
                
.defaultSuccessUrl(
"/customer/home"
)
                
.permitAll()
            
.and()
                
.logout()
                    
.logoutUrl(
"/customer/logout"
)
                    
.logoutSuccessUrl(
"/"
);
        
return
http.build();
    
}
}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

package
net.codejava.customer;
import
org.springframework.context.annotation.*;
import
org.springframework.core.annotation.Order;
import
org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import
org.springframework.security.config.annotation.web.builders.HttpSecurity;
import
org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import
org.springframework.security.core.userdetails.UserDetailsService;
import
org.springframework.security.crypto.password.*;
@Configuration
@Order
(
2
)
public
class
CustomerSecurityConfig 
extends
WebSecurityConfigurerAdapter {
    
@Bean
    
public
UserDetailsService customerUserDetailsService() {
        
return
new
CustomerUserDetailsService();
    
}
    
@Bean
    
public
PasswordEncoder passwordEncoder2() {
        
return
NoOpPasswordEncoder.getInstance();
    
}
    
@Bean
    
public
DaoAuthenticationProvider authenticationProvider2() {
        
DaoAuthenticationProvider authProvider = 
new
DaoAuthenticationProvider();
        
authProvider.setUserDetailsService(customerUserDetailsService());
        
authProvider.setPasswordEncoder(passwordEncoder2());
        
return
authProvider;
    
}
    
@Override
    
public
void
configure(HttpSecurity http) 
throws
Exception {
        
http.authenticationProvider(authenticationProvider2());
        
http.antMatcher(
"/customer/**"
)
            
.authorizeRequests().anyRequest().authenticated()
            
.and()
                
.formLogin()
                    
.loginPage(
"/customer/login"
)
                    
.usernameParameter(
"email"
)
                    
.loginProcessingUrl(
"/customer/login"
)
                    
.defaultSuccessUrl(
"/customer/home"
)
                
.permitAll()
            
.and()
                
.logout()
                    
.logoutUrl(
"/customer/logout"
)
                    
.logoutSuccessUrl(
"/"
);
    
}
}

附件:

SpringBootMultipleLoginExamples.zip[示例弹簧启动项目]426 千字节

标签: spring java spring boot

本文转载自: https://blog.csdn.net/allway2/article/details/127714415
版权归原作者 allway2 所有, 如有侵权,请联系我们删除。

“Spring Security多登录页面示例”的评论:

还没有评论