我一直在尝试实现OAuth2密码到期过滤器,我不确定这样做的正确方法 . 想法如下:
-
用户尝试登录 .
-
如果密码已过期,用户将获得包含令牌的标头的响应 .
-
用户使用该令牌重定向到密码更改页面(即/ password-change / ) .
-
他提交了他的旧密码和新密码,它会被更改 .
-
某些其他控制器通过该令牌检索用户ID,并执行其余密码更改逻辑 .
-
用户应该被重定向回初始登录页面,在那里他用新密码登录(如果他在密码更改后立即登录,他可以浏览安全页面,即使密码不会因为某些人而在后台更改例外等) .
所以......我在密码过期的用户详细信息中设置了一个自定义标志,因为我在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
}
}
-
为什么我的过滤器在OAuth2AuthenticationProcessingFilter之后被放置,而's no BasicAuthenticationFilter in the chain? I'已经通过Spring Security和OAuth2文档和来源挖掘,但找不到正确的答案 .
-
如果该用户的密码已过期,我的过滤器会生成一些随机字符串,并将其保存以便在密码更改请求期间稍后检索用户详细信息(至少它应该是):
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对象,因此我不断从我的过滤器获得相同的响应 .
- 甚至是进行所有验证的合适地点吗?如何验证具体用户而不是OAuth客户端的密码?
1 回答
好吧,我想我通过在我的过滤器中注入TokenStore来撤销访问令牌来解决这个问题(我使用BearerTokenExtractor获取令牌值),这在这个看起来很合乎逻辑 . 我仍然没有时间弄明白,为什么我的过滤器放在OAuth2AuthenticationProcessingFilter之后 .