SpringSecurity认证成功、失败处理器原理分析
首先需要知道的是,SpringSecurity默认的成功、失败处理器是SavedRequestAwareAuthenticationSuccessHandler
和SimpleUrlAuthenticationFailureHandler
;正常情况下,请求一个资源,在进入到SpringSecurity的认证中,认证成功才会跳转到待请求的资源
SavedRequestAwareAuthenticationSuccessHandler源码分析
认证成功处理器流程:用户请求一个接口时,会被SpringSecurity拦截。当用户认证成功后,才会跳转到之前用户想要请求的接口那里
问题:怎么知道用户的请求呢?
SavedRequest getRequest(HttpServletRequest request, HttpServletResponse response)
。这个方法就可以将用户的请求缓存起来。
假设此时用户已经认证成功,那么关于认证成功处理器的调用流程是:
AbstractAuthenticationProcessingFilter#successfulAuthentication
>AuthenticationSuccessHandler#onAuthenticationSuccess
>SavedRequestAwareAuthenticationSuccessHandler#onAuthenticationSuccess
SavedRequestAwareAuthenticationSuccessHandler
@Override
//这里是重写了该方法,与父类里的实现不一样了
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication)
throws ServletException, IOException {
//将用户的原始请求缓存住 放到SavedRequest里
SavedRequest savedRequest = requestCache.getRequest(request, response);
if (savedRequest == null) {
super.onAuthenticationSuccess(request, response, authentication);
return;
}
String targetUrlParameter = getTargetUrlParameter();
if (isAlwaysUseDefaultTargetUrl()
|| (targetUrlParameter != null && StringUtils.hasText(request
.getParameter(targetUrlParameter)))) {
requestCache.removeRequest(request, response);
super.onAuthenticationSuccess(request, response, authentication);
return;
}
//删除可能已存储在服务器中的与身份验证有关的临时数据
clearAuthenticationAttributes(request);
String targetUrl = savedRequest.getRedirectUrl();
logger.debug("Redirecting to DefaultSavedRequest Url: " + targetUrl);
//跳转到指定的原用户请求路径
getRedirectStrategy().sendRedirect(request, response, targetUrl);
}
这里的getRedirectStrategy()是DefaultRedirectStrategy。
DefaultRedirectStrategy
public void sendRedirect(HttpServletRequest request, HttpServletResponse response,
String url) throws IOException {
//处理url
String redirectUrl = calculateRedirectUrl(request.getContextPath(), url);
redirectUrl = response.encodeRedirectURL(redirectUrl);
if (logger.isDebugEnabled()) {
logger.debug("Redirecting to '" + redirectUrl + "'");
}
//这里执行跳转操作
response.sendRedirect(redirectUrl);
}
SimpleUrlAuthenticationFailureHandler源码分析
当onAuthenticationFailure
方法被调用时,AuthenticationFailureHandler
会根据defaultFailureUrl
的路径进行跳转。如果defaultFailureUrl
没有设置,那么会发送401错误码和与AuthenticationException
有关的错误信息给客户端。
如果useForward
属性被设置,那么RequestDispatcher.forward
将会重定向到目的地而不是跳转。
public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response, AuthenticationException exception)
throws IOException, ServletException {
//如果默认的错误跳转路径为空
if (defaultFailureUrl == null) {
logger.debug("No failure URL set, sending 401 Unauthorized error");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
"Authentication Failed: " + exception.getMessage());
}
else {
//保存异常信息
saveException(request, exception);
//如果forwardToDestination属性值为true
if (forwardToDestination) {
logger.debug("Forwarding to " + defaultFailureUrl);
request.getRequestDispatcher(defaultFailureUrl)
.forward(request, response);
}
else {
logger.debug("Redirecting to " + defaultFailureUrl);
redirectStrategy.sendRedirect(request, response, defaultFailureUrl);
}
}
}
//根据forwardToDestination的值 选择性的将错误信息 保存在request[重定向与原请求公用一个request域]中或session中。
protected final void saveException(HttpServletRequest request,
AuthenticationException exception) {
if (forwardToDestination) {
request.setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION, exception);
}
else {
HttpSession session = request.getSession(false);
if (session != null || allowSessionCreation) {
request.getSession().setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION,
exception);
}
}
}
自定义成功、失败处理器
成功处理器
@Component
@Log
public class MyAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
@Autowired
private ObjectMapper objectMapper;
@Autowired
private SecurityProperties securityProperties;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
log.info("登录成功处理器");
//这种方式 即调用父类的方法 是成功之后跳转到指定url
// super.onAuthenticationSuccess(request, response, authentication);
//下面这种方式 是将认证成功的用户信息打印到控制台
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(objectMapper.writeValueAsString(authentication));
}
}
}
失败处理器
@Component
@Log
public class MyAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
//SpringMVC加载JSON解析工具时 注入的 可以直接用
@Autowired
private ObjectMapper objectMapper;
@Autowired
private SecurityProperties securityProperties;
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
log.info("登录失败处理器");
//这种方式 即调用父类的方法 是失败之后跳转
// super.onAuthenticationFailure(request, response, exception);
//下面这种方式 是将认证失败的错误信息打印到控制台
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(objectMapper.writeValueAsString(exception));
}
}
}
将自定义认证处理器加入到SpringSecurity中
@Configuration
@EnableWebSecurity
public class WebMvcSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MyAuthenticationFailureHandler failureHandler;
@Autowired
private MyAuthenticationSuccessHandler successHandler;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.successHandler(successHandler)
.failureHandler(failureHandler)
.and().authorizeRequests().anyRequest().authenticated();
}
}
这样认证过程中 就能走到我们自己定义的认证处理器了。
以上,如有错误,请指出谢谢。