Two Login Pages with Spring Security – 使用Spring Security的两个登录页面

最后修改: 2017年 2月 11日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.介绍

In this tutorial, we will see how we can configure Spring Security to work with two different login pages using two different Spring Security http elements in the configuration.

在本教程中,我们将看到如何在配置中使用两个不同的Spring Security http元素,配置Spring Security,使其与两个不同的登录页面一起工作。

2. Configuring 2 Http Elements

2.配置2个Http元素

One of the situations in which we may need two login pages is when we have one page for administrators of an application and a different page for normal users.

我们可能需要两个登录页面的情况之一是,当我们有一个页面用于应用程序的管理员,而另一个页面用于普通用户。

We will configure two http elements that will be differentiated by the URL pattern associated with each:

我们将配置两个http元素,它们将由与每个元素相关的URL模式来区分。

  • /user* for pages that will need a normal user authentication to be accessed
  • /admin* for pages that will be accessed by an administrator

Each http element will have a different login page and a different login processing URL.

每个http元素将有一个不同的登录页面和一个不同的登录处理URL。

In order to configure two different http elements, let’s create two static classes annotated with @Configuration.

为了配置两个不同的http元素,让我们创建两个用@Configuration注释的静态类。

Both will be placed inside a regular @Configuration class:

两者都将被放在一个普通的@Configuration类里面。

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    ...
}

Let’s define the ConfigurerAdapter for the “ADMIN” users:

让我们为“ADMIN”/em>用户定义ConfigurerAdapter

@Configuration
@Order(1)
public static class App1ConfigurationAdapter {

    @Bean
    public SecurityFilterChain filterChainApp1(HttpSecurity http) throws Exception {
        http.antMatcher("/admin*")
          .authorizeRequests()
          .anyRequest()
          .hasRole("ADMIN")
          
          .and()
          .formLogin()
          .loginPage("/loginAdmin")
          .loginProcessingUrl("/admin_login")
          .failureUrl("/loginAdmin?error=loginError")
          .defaultSuccessUrl("/adminPage")
          
          .and()
          .logout()
          .logoutUrl("/admin_logout")
          .logoutSuccessUrl("/protectedLinks")
          .deleteCookies("JSESSIONID")
          
          .and()
          .exceptionHandling()
          .accessDeniedPage("/403")
          
          .and()
          .csrf().disable();
       return http.build();
    }
}

And now, let’s define the ConfigurerAdapter for normal users:

而现在,让我们为普通用户定义ConfigurerAdapter

@Configuration
@Order(2)
public static class App2ConfigurationAdapter {

    @Bean 
    public SecurityFilterChain filterChainApp2(HttpSecurity http) throws Exception {
        http.antMatcher("/user*")
          .authorizeRequests()
          .anyRequest()
          .hasRole("USER")
          
          .and()
          .formLogin()
          .loginPage("/loginUser")
          .loginProcessingUrl("/user_login")
          .failureUrl("/loginUser?error=loginError")
          .defaultSuccessUrl("/userPage")
          
          .and()
          .logout()
          .logoutUrl("/user_logout")
          .logoutSuccessUrl("/protectedLinks")
          .deleteCookies("JSESSIONID")
          
          .and()
          .exceptionHandling()
          .accessDeniedPage("/403")
          
          .and()
          .csrf().disable();
       return http.build();
    }
}

Note that by placing the @Order annotation on each static class, we are specifying the order in which the two classes will be considered based on the pattern matching when a URL is requested.

请注意,通过在每个静态类上放置@Order注解,我们指定了在请求URL时根据模式匹配考虑这两个类的顺序。

Two configuration classes cannot have the same order.

两个配置类不能有相同的顺序。

3. Custom Login Pages

3.自定义登录页面

We will create our own custom login pages for each type of user. For the administrator user, the login form will have a “user_login” action, as defined in the configuration:

我们将为每种类型的用户创建自己的自定义登录页面。对于管理员用户,登录表单将有一个“user_login”动作,如配置中所定义。

<p>User login page</p>
<form name="f" action="user_login" method="POST">
    <table>
        <tr>
            <td>User:</td>
            <td><input type="text" name="username" value=""></td>
        </tr>
        <tr>
            <td>Password:</td>
            <td><input type="password" name="password" /></td>
        </tr>
        <tr>
            <td><input name="submit" type="submit" value="submit" /></td>
        </tr>
    </table>
</form>

The administrator login page is similar, except the form will have an action of “admin_login” as per the java configuration.

管理员的登录页面也是类似的,只是根据java的配置,表单将有一个“admin_login”的动作。

4. Authentication Configuration

4.认证配置

Now we need to configure authentication for our application. Let’s look at two ways to accomplish this — one using a common source for user authentication, and the other using two separate sources.

现在我们需要为我们的应用程序配置认证。让我们看一下完成这一任务的两种方法–一种是使用一个共同的用户认证源,另一种是使用两个独立的源。

4.1. Using a Common User Authentication Source

4.1.使用普通用户认证源

If both login pages share a common source for authenticating users, you can create a single bean of type UserDetailsService that will handle the authentication.

如果两个登录页面共享一个用于验证用户的共同来源,你可以创建一个类型为UserDetailsService的单一Bean来处理验证。

Let’s demonstrate this scenario using an InMemoryUserDetailsManager that defines two users — one with a role of “USER” and one with a role of “ADMIN”:

让我们用一个InMemoryUserDetailsManager来演示这个场景,它定义了两个用户–一个角色为“USER”,一个角色为“ADMIN”

@Bean
public UserDetailsService userDetailsService() throws Exception {
    InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
    manager.createUser(User
      .withUsername("user")
      .password(encoder().encode("userPass"))
      .roles("USER")
      .build());
    
    manager.createUser(User
      .withUsername("admin")
      .password(encoder().encode("adminPass"))
      .roles("ADMIN")
      .build());
    
    return manager;
}

@Bean
public static PasswordEncoder encoder() {
    return new BCryptPasswordEncoder();
}

4.2. Using Two Different User Authentication Sources

4.2.使用两个不同的用户认证来源

If you have different sources for user authentication — one for administrators and one for normal users — you can configure an AuthenticationManagerBuilder inside each static @Configuration class. Let’s look at an example of an authentication manager for an “ADMIN” user:

如果你有不同的用户认证来源–一个用于管理员,一个用于普通用户–你可以在每个静态@Configuration类里面配置一个AuthenticationManagerBuilder。让我们看看一个“ADMIN”用户的认证管理器的例子。

@Configuration
@Order(1)
public static class App1ConfigurationAdapter {

    @Bean
    public UserDetailsService userDetailsServiceApp1() {
         UserDetails user = User.withUsername("admin")
             .password(encoder().encode("admin"))
             .roles("ADMIN")
             .build();
         return new InMemoryUserDetailsManager(user);
    }
}

In this case, the UserDetailsService bean from the previous section will no longer be used.

在这种情况下,上一节的UserDetailsService Bean将不再被使用。

6. Conclusion

6.结论

In this quick tutorial, we’ve shown how to implement two different login pages in the same Spring Security application.

在这个快速教程中,我们展示了如何在同一个Spring Security应用程序中实现两个不同的登录页面。

The complete code for this article can be found in the GitHub project.

本文的完整代码可以在GitHub项目中找到。

When you run the application, you can access the examples above on the /protectedLinks URI.

当你运行应用程序时,你可以在/protectedLinks URI上访问上述例子。