• 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.bootgroupId>
        <artifactId>spring-boot-starter-data-jpaartifactId>
    dependency>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-securityartifactId>
    dependency>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-thymeleafartifactId>
    dependency>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-webartifactId>
    dependency>
    <dependency>
        <groupId>org.thymeleaf.extrasgroupId>
        <artifactId>thymeleaf-extras-springsecurity5artifactId>
    dependency>
    <dependency>
        <groupId>com.h2databasegroupId>
        <artifactId>h2artifactId>
        <scope>runtimescope>
    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 {
        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("david@gmail.com""david123", Role.ADMIN);
                User user2 = new User("john@yahoo.com""john2020", Role.ADMIN);
                User user3 = new User("nam@codejava.net""nam2022", Role.USER);
                User user4 = new User("ravi@gmail.com""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 Collectionextends GrantedAuthority> getAuthorities() {
            List 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,类型分别为UserDetailsServicePasswordEncoder,身份验证提供程序将使用它们来对用户进行身份验证。以下语句允许对应用程序主页的公共访问(所有人):因为在主页上,我们显示如下所示的登录选项:下面是主页的代码(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.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.3.4.RELEASEversion>
        <relativePath/> 
    parent>

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <html>
    <head>
    <meta charset="ISO-8859-1">
    <title>Spring Multiple Login Pages Demotitle>
    head>
    <body>
        <div align="center">
            <h2>Welcome to CodeJava.neth2>
            <h4><a th:href="/@{/admin/login}">Admin Logina>h4>
            <p/>
            <h4><a th:href="/@{/user/login}">User Logina>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
    <html xmlns:th="http://www.thymeleaf.org">
    <head>
    <meta charset="ISO-8859-1">
    <title>Admin Login - CodeJava.nettitle>
    head>
    <body>
    <form th:action="@{/admin/login}" method="post" style="max-width: 400px; margin: 0 auto;">
        <h2>Admin Login - CodeJava.neth2>
         
        <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><tdtd>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
    <html xmlns:th="http://www.thymeleaf.org">
    <head>
    <meta charset="ISO-8859-1">
    <title>Welcome to CodeJava.net Admin Control Paneltitle>
    head>
    <body>
        <div align="center">
            <h2>Welcome to CodeJava.net Admin Control Panelh2>
            <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
    <html xmlns:th="http://www.thymeleaf.org">
    <head>
    <meta charset="ISO-8859-1">
    <title>User Login - CodeJava.nettitle>
    head>
    <body>
    <form th:action="@{/user/login}" method="post" style="max-width: 400px; margin: 0 auto;">
        <h2>User Login - CodeJava.neth2>
         
        <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><tdtd>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
    <html xmlns:th="http://www.thymeleaf.org">
    <head>
    <meta charset="ISO-8859-1">
    <title>Welcome to CodeJava.nettitle>
    head>
    <body>
        <div align="center">
            <h2>Welcome to CodeJava.net User Homeh2>
            <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 Collectionextends 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 {
        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("david@gmail.com""david123");
                User user2 = new User("john@yahoo.com""john2020");
                 
                userRepo.saveAll(List.of(user1, user2));
                 
                Customer customer1 = new Customer("alex@gmail.com""alex123""Alex Stevenson");
                Customer customer2 = new Customer("peter@mail.ru""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类使用以下代码:如您所见,我们定义了不同的UserDetailsServicePasswordEncoderAuthenticationProvider,以便为客户登录模块使用单独的身份验证源。并且视图页面和控制器类的代码与前面的代码类似。这是一些代码示例,向您展示如何使用 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 Collectionextends 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 千字节
  • 相关阅读:
    入职阿里必会199道SpringCloud面试题,你能掌握多少?
    Pyside6 QFile
    华为云云耀云服务器L实例评测|使用华为云耀云服务器L实例的CentOS部署Docker并运行Tomcat应用
    Maven(8) 实战总结
    logback 日志基础配置
    Java游戏制作——王者荣耀
    分享大数据培训班班型
    ‘mysql‘不是内部或外部命令,也不是可运行的程序或批处理文件
    微前端的暗位面
    (九)socket·NIO socket 实现 服务端广播+客户端群聊 -不限制次序,不限制交互次数
  • 原文地址:https://blog.csdn.net/allway2/article/details/127714415