Spring Security:OAuth2自定义过滤器,用于验证密码到期错误处理

时间:2015-08-24 19:38:06

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

我一直在尝试实施OAuth2密码到期过滤器,我不确定这样做的正确方法。这个想法如下:

  1. 用户尝试登录。
  2. 如果密码已过期,用户将获得包含令牌的标头的响应。
  3. 使用该令牌将用户重定向到密码更改页面(即/ password-change / {token})。
  4. 他提交了旧密码和新密码,它会被更改。
  5. 某些其他控制器通过该令牌检索用户ID,并使其余的密码更改逻辑。
  6. 应该将用户重定向回初始登录页面,并在其中使用新密码登录(如果他在密码更改后立即登录,即使密码不会在后台更改,他也可以浏览安全页面)某些例外,等等。)。
  7. 所以......我在密码过期的用户详细信息中设置了一个自定义标志,因为我在DaoAuthenticationProvider中验证时不能使用credentialsNonExpired,并作为异常被抛出,因为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);
          }
      }
      }
      
    3. 我是否还必须撤销OAuth令牌?它会在以后的请求中重用,并且我不断获取最后一个userDetails对象,因此我不断从我的过滤器获得相同的响应。

      1. 甚至是进行所有验证的合适地点吗?如何验证具体用户而不是OAuth客户端的密码?

1 个答案:

答案 0 :(得分:0)

好吧,我想我通过在我的过滤器中注入TokenStore来撤销访问令牌来解决这个问题(我使用BearerTokenExtractor来获取令牌值),这在这个看起来很合乎逻辑。我仍然没有时间弄明白,为什么我的过滤器会在OAuth2AuthenticationProcessingFilter之后放置。