Spring Security Context Set Authentication 对象不起作用

Posted

技术标签:

【中文标题】Spring Security Context Set Authentication 对象不起作用【英文标题】:Spring Security Context Set Authentication object not working 【发布时间】:2012-03-07 12:40:49 【问题描述】:

我有一个场景,我必须强制用户在首次登录时重置密码。为了 我正在使用自定义的successAuthenticationHandler。 所有这个处理程序试图做的是查看登录用户是否需要重置密码。如果是,则创建一个新的 UsernamePasswordAuthenticationToken 并将其设置到 SecurityContext。然后重定向到resetPasswordUrl。

这是我的 onAuthenticationSuccess 方法:

@Override
public void onAuthenticationSuccess(final HttpServletRequest request, final HttpServletResponse response,
        final Authentication authentication) throws ServletException, IOException 

    final AugmentedUser aUser = (AugmentedUser) SecurityContextHolder.getContext().getAuthentication()
            .getPrincipal();
    System.out.println("In password reset handler.");
    if (authorizationService.isPasswordResetRequired(aUser.getUsername(), aUser.getUsertype())) 
        LOG.debug("Password reset is required.");
        System.out.println("Password reset is required");
        final UsernamePasswordAuthenticationToken authRequest = reAssignUserWithOnlyResetPasswordRole(aUser,
                request);
        SecurityContextHolder.getContext().setAuthentication(authRequest);
        SecurityContextHolder.getContext().getAuthentication();
        System.out.println("User reassinged with only RESET_PASSWORD Authority, redirecting to resetPasswordPage");
        response.sendRedirect(resetPasswordUrl);

        //super.onAuthenticationSuccess(request, response, newAuthentication);
     else 
        super.onAuthenticationSuccess(request, response, authentication);
    


如果是,则使用与登录用户相同的凭据创建另一个 UsernamePasswordAuthenticationToken,但只需为他分配一个角色“RESET_PASSWORD”,这样他就无法通过点击任何其他链接/url 来访问任何其他内容。

private UsernamePasswordAuthenticationToken reAssignUserWithOnlyResetPasswordRole(final AugmentedUser aUser,
        final HttpServletRequest request) 
    final String username = aUser.getUsername();
    final String password = aUser.getPassword();
    final boolean isEnabled = aUser.isEnabled();
    final boolean isAccountNonExpired = aUser.isAccountNonExpired();
    final boolean isCredentialsNonExpired = aUser.isCredentialsNonExpired();
    final boolean isAccountNonLocked = aUser.isAccountNonLocked();
    LOG.debug("Re-assigning the user: " + username + " with only RESET PASSWORD AUTHORITY");
    System.out.println("Re-assigning the user: " + username + "with only RESET PASSWORD AUTHORITY");
    final Map<String, String> userAttributesMap = new HashMap<String, String>();
    final AugmentedUser userWithResetPasswordRole = new AugmentedUser(username, password, aUser.getUsertype(),
            isEnabled, isAccountNonExpired, isCredentialsNonExpired, isAccountNonLocked,
            createResetPasswordGrantedAuhtority(), userAttributesMap);

    final UsernamePasswordAuthenticationToken authenticationRequest = new UsernamePasswordAuthenticationToken(
            userWithResetPasswordRole, userWithResetPasswordRole.getAuthorities());

    //WebAuthenticationDetails are required for sessionId and ipAddress
    final WebAuthenticationDetails webAuthenticationDetails = new WebAuthenticationDetails(request);
    authenticationRequest.setDetails(webAuthenticationDetails);

    return authenticationRequest;


现在我确实看到新的 UsernamePasswordAuthenticationToken 仅使用 RESET 密码角色创建。 但是看起来在重定向到 resetPasswordURl 时,spring 过滤器会进行一些检查,并且在我设置了新的 UsernamePasswordAuthenticationToken 之后,用户正在未经身份验证。

这是我在日志中看到的根本原因:

doAuthentication - Authentication attempt using
org.springframework.security.authentication.dao.DaoAuthenticationProvider
2012-02-15 22:47:20,931 [http-8081-6] DEBUG       org.springframework.security.web.access.ExceptionTranslationFilter    org.springframework.security.web.access.ExceptionTranslationFilter handleException - Authentication exception occurred; redirecting to authentication entry point

org.springframework.security.authentication.BadCredentialsException: Bad credentials
at org.springframework.security.authentication.dao.DaoAuthenticationProvider.additionalAuthenticationChecks(DaoAuthenticationProvider.java:67)
at org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.authenticate(AbstractUserDetailsAuthenticationProvider.java:139)
at org.springframework.security.authentication.ProviderManager.doAuthentication(ProviderManager.java:120)

我哪里出错了?

【问题讨论】:

【参考方案1】:

如果没有来自日志的更多上下文(堆栈跟踪的其余部分加上前面的日志消息),很难说,但我最好的猜测是您为UsernamePasswordAuthenticationToken 使用了错误的构造函数。由于历史原因,它需要 Object 参数,这没有帮助。

双参数版本应该采用用户名和凭据,并创建一个未经身份验证的令牌(用于请求),从安全拦截器的角度来看这是无效的。所以我猜测拦截器正在尝试重新验证令牌(应该从调用来自的堆栈跟踪中显而易见)并且它失败了,因为凭据参数实际上是权限列表而不是密码。

所以使用:

new UsernamePasswordAuthenticationToken(
        userWithResetPasswordRole, null, userWithResetPasswordRole.getAuthorities());

改为。

此外,您需要有一个处理RESET_PASSWORD 的自定义投票器,因为默认配置无法识别它。或者使用ROLE_ 前缀,即ROLE_RESET_PASSWORD

【讨论】:

以上是关于Spring Security Context Set Authentication 对象不起作用的主要内容,如果未能解决你的问题,请参考以下文章

Spring:HttpSession在集群Tomcat故障转移中为SPRING_SECURITY_CONTEXT返回了空对象

Spring Security Context Set Authentication 对象不起作用

访问没有斜杠的 Tomcat 应用程序时,Spring Security Context 为空

页面获取Spring Security登录用户

spring security

spring security