1。概述

Web 应用程序的一个常见要求是在登录后将不同类型的用户重定向到不同的页面。例如,将标准用户重定向到 /homepage.html 页面和将管理员用户重定向到 /console.html 页面。

本文将展示如何使用 Spring Security 快速安全地实现此机制。这篇文章也是建立在 Spring MVC 教程 之上的,该教程涉及设置项目所需的核心内容。

2。 Spring 安全配置

Spring Security 提供了一个组件,该组件直接负责决定在成功验证后做什么——AuthenticationSuccessHandler

2.1。基本配置

让我们首先配置一个基本的 @Configuration@Service 类:

@Configuration
@EnableWebSecurity
public class SecSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(final HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
            // ... endpoints
            .formLogin()
                .loginPage("/login.html")
                .loginProcessingUrl("/login")
                .defaultSuccessUrl("/homepage.html", true)
            // ... other configuration       
    }
}

此配置中要关注的部分是 defaultSuccessUrl() 方法。成功登录后,任何用户都将被重定向到 homepage.html

此外,我们需要配置用户及其角色。出于本文的目的,我们将实现一个简单的UserDetailService,其中有两个用户,每个用户都有一个角色。有关此主题的更多信息,请阅读我们的文章 Spring Security – Roles and Privileges

@Service
public class MyUserDetailsService implements UserDetailsService {

    private Map<String, User> roles = new HashMap<>();

    @PostConstruct
    public void init() {
        roles.put("admin2", new User("admin", "{noop}admin1", getAuthority("ROLE_ADMIN")));
        roles.put("user2", new User("user", "{noop}user1", getAuthority("ROLE_USER")));
    }

    @Override
    public UserDetails loadUserByUsername(String username) {
        return roles.get(username);
    }

    private List<GrantedAuthority> getAuthority(String role) {
        return Collections.singletonList(new SimpleGrantedAuthority(role));
    }
}

另外请注意,在这个简单的示例中,我们不会使用密码编码器。

2.2。添加自定义成功处理程序

我们现在有两个具有两个不同角色的用户: useradmin 。成功登录后,两者都将被重定向到 hompeage.html让我们看看如何根据用户的角色进行不同的重定向。

首先,我们需要将自定义成功处理程序定义为 bean:

@Bean
public AuthenticationSuccessHandler myAuthenticationSuccessHandler(){
    return new MySimpleUrlAuthenticationSuccessHandler();
}
@Override
protected void configure(final HttpSecurity http) throws Exception {
    http
        .authorizeRequests()
        // endpoints
        .formLogin()
            .loginPage("/login.html")
            .loginProcessingUrl("/login")
            .successHandler(myAuthenticationSuccessHandler())
        // other configuration      
}

2.3。 XML 配置

在查看我们的自定义成功处理程序的实现之前,我们还要查看等价的 XML 配置:

<http use-expressions="true" >
    <!-- other configuration -->
    <form-login login-page='/login.html' 
      authentication-failure-url="/login.html?error=true"
      authentication-success-handler-ref="myAuthenticationSuccessHandler"/>
    <logout/>
</http>

<beans:bean id="myAuthenticationSuccessHandler"
  class="com.baeldung.security.MySimpleUrlAuthenticationSuccessHandler" />

<authentication-manager>
    <authentication-provider>
        <user-service>
            <user name="user1" password="{noop}user1Pass" authorities="ROLE_USER" />
            <user name="admin1" password="{noop}admin1Pass" authorities="ROLE_ADMIN" />
        </user-service>
    </authentication-provider>
</authentication-manager>

3。自定义身份验证成功处理程序

除了 AuthenticationSuccessHandler 接口,Spring 还为这个策略组件提供了一个合理的默认值 - AbstractAuthenticationTargetUrlRequestHandler 和一个简单的实现 - SimpleUrlAuthenticationSuccessHandler。通常,这些实现将在登录后确定 URL 并执行到该 URL 的重定向。

虽然有些灵活,但确定此目标 URL 的机制不允许以编程方式进行确定——因此我们将实现接口并提供成功处理程序的自定义实现。 此实现将根据用户的角色确定用户登录后重定向到的 URL。

首先,我们需要重写 onAuthenticationSuccess 方法:

public class MySimpleUrlAuthenticationSuccessHandler
  implements AuthenticationSuccessHandler {
 
    protected Log logger = LogFactory.getLog(this.getClass());

    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, 
      HttpServletResponse response, Authentication authentication)
      throws IOException {
 
        handle(request, response, authentication);
        clearAuthenticationAttributes(request);
    }

我们自定义的方法调用了两个辅助方法:

protected void handle(
        HttpServletRequest request,
        HttpServletResponse response, 
        Authentication authentication
) throws IOException {

    String targetUrl = determineTargetUrl(authentication);

    if (response.isCommitted()) {
        logger.debug(
                "Response has already been committed. Unable to redirect to "
                        + targetUrl);
        return;
    }

    redirectStrategy.sendRedirect(request, response, targetUrl);
}

以下方法执行实际工作并将用户映射到目标 URL:

protected String determineTargetUrl(final Authentication authentication) {

    Map<String, String> roleTargetUrlMap = new HashMap<>();
    roleTargetUrlMap.put("ROLE_USER", "/homepage.html");
    roleTargetUrlMap.put("ROLE_ADMIN", "/console.html");

    final Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
    for (final GrantedAuthority grantedAuthority : authorities) {
        String authorityName = grantedAuthority.getAuthority();
        if(roleTargetUrlMap.containsKey(authorityName)) {
            return roleTargetUrlMap.get(authorityName);
        }
    }

    throw new IllegalStateException();
}

请注意,此方法将返回用户拥有的第一个角色的映射 URL。因此,如果用户有多个角色,映射的 URL 将与 authorities 集合中给出的第一个角色匹配。

protected void clearAuthenticationAttributes(HttpServletRequest request) {
    HttpSession session = request.getSession(false);
    if (session == null) {
        return;
    }
    session.removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
}

determineTargetUrl - 这是策略的核心 - 只需查看用户类型(由权限确定)并根据此角色选择目标 URL

因此,admin 用户 - 由 ROLE_ADMIN 权限确定 - 将在登录后重定向到控制台页面,而 标准用户 - 由 ROLE_USER 确定 - 将是重定向到主页。

4。结论

本文中提及的代码 [在 GitHub 上] (https://github.com/eugenp/tutorials/tree/master/spring-security-modules/spring-security-web-mvc-custom) 可用。这是一个基于 Maven 的项目,因此它应该很容易导入和运行。

作者:|NoOneNoOne|,原文链接: http://www.imooc.com/article/331003

文章推荐

Java设计模式-原型模式

MySQL主从复制原理剖析与应用实践

Oracle JDK 和 OpenJDK 有什么区别?

ARM 详解

Thanos工作原理及组件简介

交换机的简单命令小结

python安装robotframework的一些常见的错误

Asp-Net-Core开发笔记:使用RateLimit中间件实现接口限流

JAVA重试机制多种方式深入浅出

Spring IOC 常用注解与使用

python常用标准库(os系统模块、shutil文件操作模块)

Ubuntu 18.04.4 安装docker18.09