Spring Cloud Security核心过滤器UsernamePasswordAuthenticationFilter解析
在前面几篇文章介绍的三个过滤器总是会被spring-security-web配置到安全过滤器调用链中。
但是直到此时,SecurtiyContext
中都可能是空的,并没有真正的认证过程,因此过滤器调用链中还需要一个对用户身份进行认证的过滤器,允许用户进行身份认证,并为SecurityContext
注入有效的用户信息。UsernamePasswordAuthenticationFilter
就是其中最常用的身份认证过滤器,spring-security在/login
端点登录时要求通过username和password进行登录就是通过该过滤器对认证进行处理。
UsernamePasswordAuthenticationFilter
继承于AbstractAuthenticationProcessingFilter
,其过滤器的核心处理流程如下,位于AbstractAuthenticationProcessingFilter
中:
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
// 如果不需要认证,直接返回
if (!requiresAuthentication(request, response)) {
chain.doFilter(request, response);
return;
}
Authentication authResult;
try {
authResult = attemptAuthentication(request, response);
if (authResult == null) {
// return immediately as subclass has indicated that it hasn't completed
// authentication
return;
}
sessionStrategy.onAuthentication(authResult, request, response);
}catch (InternalAuthenticationServiceException failed) {
// 认证失败
unsuccessfulAuthentication(request, response, failed);
return;
}catch (AuthenticationException failed) {
// Authentication failed
unsuccessfulAuthentication(request, response, failed);
return;
}
// 认证成功
if (continueChainBeforeSuccessfulAuthentication) {
chain.doFilter(request, response);
}
successfulAuthentication(request, response, chain, authResult);
}
在这个流程中,AbstractAuthenticationProcessingFilter
将真正进行认证的过程委托给子类的attemptAuthentication()
方法进行,同时定义了unsuccessfulAuthentication()
和successfulAuthentication()
方法对认证失败和认证成功进行处理,我们首先看一下这两个方法的实现。
protected void unsuccessfulAuthentication(HttpServletRequest request,
HttpServletResponse response, AuthenticationException failed)
throws IOException, ServletException {
// 清理SecurityContext
SecurityContextHolder.clearContext();
rememberMeServices.loginFail(request, response);
failureHandler.onAuthenticationFailure(request, response, failed);
}
protected void successfulAuthentication(HttpServletRequest request,
HttpServletResponse response, FilterChain chain, Authentication authResult)
throws IOException, ServletException {
// 将认证成功返回的authResult保存到SecurtiyContext
SecurityContextHolder.getContext().setAuthentication(authResult);
rememberMeServices.loginSuccess(request, response, authResult);
// Fire event
if (this.eventPublisher != null) {
eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(
authResult, this.getClass()));
}
successHandler.onAuthenticationSuccess(request, response, authResult);
}
在认证失败后,spring-security会对SecurityContext
进行清理,并委托AuthenticationFailureHandler
进行具体的处理,默认是返回401状态码或者重定向到认证失败页面;而认证成功后就会将认证后返回的Authentication
保存到SecurityContext
中,然后委托AuthenticationSuccessHandler
进行处理,默认也是重定向到相关的登录成功页面或者继续进行请求。
UsernamePasswordAuthenticationFilter
中的attemptAuthentication()
主要是通过委托AuthenticationManager
进行认证的,代码如下:
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
if (postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
// 从request中获取用户名和密码
String username = obtainUsername(request);
String password = obtainPassword(request);
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
username = username.trim();
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
// 允许子类自定义额外的数据
setDetails(request, authRequest);
// 调用`AuthenticationManager`进行认证
return this.getAuthenticationManager().authenticate(authRequest);
}
在该方法中,首先从request中获取到username和password,封装成UsernamePasswordAuthenticationToken
用于交予AuthenticationManager
进行真正的认证,总体来说流程还是很清晰的。认证成功后会返回充满丰富用户信息的UsernamePasswordAuthenticationToken
用于接下来的请求。
- 点赞
- 收藏
- 关注作者
评论(0)