【问题标题】:Spring Security: OAuth2 custom filter to validate password expiration error handlingSpring Security:OAuth2 自定义过滤器验证密码过期错误处理
【发布时间】:2015-11-18 08:28:15
【问题描述】:

我一直在尝试实现 OAuth2 密码过期过滤器,但我不确定这样做的正确方法是什么。思路如下:

  1. 用户尝试登录。
  2. 如果密码过期,用户会收到带有包含令牌的标头的响应。
  3. 使用该令牌(即 /password-change/{token})将用户重定向到密码更改页面。
  4. 他提交了他的旧密码和新密码,它被更改了。
  5. 一些休息控制器通过该令牌检索用户 ID 并执行休息密码更改逻辑。
  6. 应将用户重定向回初始登录页面,在该页面中他使用新密码登录(如果他在密码更改后立即登录,即使密码不会在后台更改,他也可以浏览安全页面某些例外等)。

所以...我在用户详细信息中设置了一个自定义标志以用于密码过期,因为我无法使用 credentialsNonExpired,因为它在 DaoAuthenticationProvider 中得到验证并作为异常抛出,该异常作为 InvalidGrantException 处理,这并没有给我太多控制权。我发现为了在身份验证后立即访问用户详细信息,我的过滤器应该位于 OAuth2AuthenticationProcessingFilter 之后放置的内部 Spring Security 过滤器链中:

@Configuration
@EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {

        ...

        http.addFilterAfter(new PasswordExpirationFilter(), BasicAuthenticationFilter.class
    }
}
  1. 为什么我的过滤器放在 OAuth2AuthenticationProcessingFilter 之后,而链中没有 BasicAuthenticationFilter?我浏览了 Spring Security 和 OAuth2 文档和资源,但找不到正确的答案。
  2. 如果该用户的密码已过期,我的过滤器会生成一些随机字符串并将其保存,以便稍后在密码更改请求期间检索用户详细信息(至少应该如此):

    public class PasswordExpirationFilter extends OncePerRequestFilter implements Filter, InitializingBean {
    
    private static final String TOKEN_HEADER = ...;
    private ExpiredPasswordRepository repo; // gets set in a constructor and is basically holding a concurrent map of tokens
    
    ...
    
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    
        UserDetails details = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    
        if (details.isPasswordExpired) {
            String uuid = UUID.randomUUID().toString();
            repo.push(uuid, details.getId());
    
            SecurityContextHolder.clearContext();
            SecurityContextHolder.getContext().setAuthentication(null);
            request.getSession(false).invalidate(); // don't create a new session
            response.addHeader(TOKEN_HEADER, uuid);
            response.sendError(HttpStatus.SC_PRECONDITION_FAILED, "Credentials have expired");
        } else {
            filterChain.doFilter(request, response);
        }
    }
    }
    

我是否也必须撤销 OAuth 令牌?它在以后的请求中被重用,我不断得到最后一个 userDetails 对象,因此我不断从我的过滤器中得到相同的响应。

  1. 它是否适合进行所有这些验证?应该如何验证具体用户而不是 OAuth 客户端的密码?

【问题讨论】:

    标签: java spring spring-security spring-boot spring-security-oauth2


    【解决方案1】:

    好的,我想我通过在过滤器中注入的 TokenStore 撤销访问令牌解决了这个问题(我使用 BearerTokenExtractor 来获取令牌值),这在这种情况下似乎很合乎逻辑。不过,我仍然没有时间弄清楚,为什么我的过滤器放在 OAuth2AuthenticationProcessingFilter 之后。

    【讨论】:

    • 哦,我认为更合适的方法是在获取 oauth2 令牌之前进行整个过期密码验证。我只需要在已经建立的身份验证机制上快速执行此操作。
    猜你喜欢
    • 2019-04-27
    • 1970-01-01
    • 2011-05-07
    • 2016-03-25
    • 2020-02-14
    • 2011-07-21
    • 2018-08-06
    • 1970-01-01
    • 2016-04-30
    相关资源
    最近更新 更多