1、常见用法
和SpringSecurity认证失败处理器方法类似,认证成功也提供了三种的配置方式,而且和认证失败的方式基本上是对应的。分别是:
- defaultSuccessUrl()方法,提供了两个重载方法,一个参数的方法只需要配置默认重定向路径,两个参数的方法除了配置重定向路径,还需要配置是否无论什么情况只重定向配置的路径。
- successForwardUrl()方法,服务端转发,无论什么情况只转发到配置的路径。
- successHandler()方法,自定义认证成功处理器。
defaultSuccessUrl()方法的用法
首先,我们看一下defaultSuccessUrl()方法的用法,如果使用两个参数的方法,当第二个参数为true时,当我们登陆认证成功后,即使是之前访问过需要认证的路径,这个时候也会直接调整到配置的重定向路径上,如果第二个参数为false时,如果之前访问了需要认证的路径,认证成功后就会重定向到之前访问的需要认证的路径上。一个参数的配置,就是默认第二个参数为false。
//SpringSecurity配置类
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/error","/goLogin","/doLogin","/401","/static/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/goLogin")
.loginProcessingUrl("/doLogin")
.defaultSuccessUrl("/index/toIndex",true)
.failureUrl("/login/error")
.permitAll()
}
successForwardUrl()方法的用法
从用法和效果上,非常类似defaultSuccessUrl()方法第二个参数为true的情况,即无论是否访问过需要认证的路径,都只是转发到配置的路径上。和defaultSuccessUrl()方法区别还有一点就是,这里是通过服务端进行转发的,而defaultSuccessUrl()方法则是由浏览器重定向完成的。具体用法如下:
//SpringSecurity配置类
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/error","/goLogin","/doLogin","/401","/static/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/goLogin")
.loginProcessingUrl("/doLogin")
.successForwardUrl("/index/toIndex")
.failureUrl("/login/error")
.permitAll()
}
successHandler()方法的用法
这种方式,主要是用来实现自定义认证处理器的配置,使用起来会更加灵活。其实,前两种用法其实本质上也是基于这种用法实现的,不过是框架默认提供了便捷的使用方式。具体用法如下:
//SpringSecurity配置类
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/error","/goLogin","/doLogin","/401","/static/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/goLogin")
.loginProcessingUrl("/doLogin")
.successHandler(new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
//处理逻辑
}
})
.failureUrl("/login/error")
.permitAll()
}
2、AuthenticationSuccessHandler接口
AuthenticationSuccessHandler是认证成功后的处理接口,通过实现该接口,可以定义各类复杂的处理方式。
类结构如下:
AuthenticationSuccessHandler接口定义如下,其中onAuthenticationSuccess()方法就是约定了认证成功后,将要执行的方法,该方法会在AbstractAuthenticationProcessingFilter类的successfulAuthentication()方法中调用。
public interface AuthenticationSuccessHandler {
default void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, FilterChain chain, Authentication authentication)
throws IOException, ServletException{
onAuthenticationSuccess(request, response, authentication);
chain.doFilter(request, response);
}
void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication)
throws IOException, ServletException;
}
3、defaultSuccessUrl()方法
defaultSuccessUrl()方法是通过浏览器重定向实现页面跳转的,我们从代码层面分析配置是如何生效的。
//AbstractAuthenticationFilterConfigurer.java
//一个参数的方法,其实是调用两个参数的方法,并把第二个参数设置为false
public final T defaultSuccessUrl(String defaultSuccessUrl) {
return defaultSuccessUrl(defaultSuccessUrl, false);
}
//两个参数的方法
public final T defaultSuccessUrl(String defaultSuccessUrl, boolean alwaysUse) {
SavedRequestAwareAuthenticationSuccessHandler handler = new SavedRequestAwareAuthenticationSuccessHandler();
handler.setDefaultTargetUrl(defaultSuccessUrl);
handler.setAlwaysUseDefaultTargetUrl(alwaysUse);
this.defaultSuccessHandler = handler;
return successHandler(handler);
}
根据上述代码,我们知道,defaultSuccessUrl()方法,底层使用了SavedRequestAwareAuthenticationSuccessHandler 处理器,然后再设置重定向路径和是否总是使用配置路径两个参数(对应方法的两个变量),然后再把该处理器赋值给全局变量defaultSuccessHandler(框架默认使用的也是SavedRequestAwareAuthenticationSuccessHandler 处理器),最后调用successHandler()方法,把定义的处理器赋值给successHandler变量,即设置当前认证成功处理器。
//AbstractAuthenticationFilterConfigurer.java
public final T successHandler(AuthenticationSuccessHandler successHandler) {
this.successHandler = successHandler;
return getSelf();
}
至此,使用defaultSuccessUrl()方法的配置到这里其实就完成了设置,后续我们再分析这些配置是如何生效的。
SavedRequestAwareAuthenticationSuccessHandler类
SavedRequestAwareAuthenticationSuccessHandler类是实现认证成功处理的核心逻辑,主要在onAuthenticationSuccess()方法中定义,实现如下:
//SavedRequestAwareAuthenticationSuccessHandler.java
@Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication)
throws ServletException, IOException {
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);
// Use the DefaultSavedRequest URL
String targetUrl = savedRequest.getRedirectUrl();
logger.debug("Redirecting to DefaultSavedRequest Url: " + targetUrl);
getRedirectStrategy().sendRedirect(request, response, targetUrl);
}
在上述方法中,又调用了父类SimpleUrlAuthenticationSuccessHandler的onAuthenticationSuccess()方法,实现如下:
//SimpleUrlAuthenticationSuccessHandler.java
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
//AbstractAuthenticationTargetUrlRequestHandler中定义,实现跳转页面的获取,和实现由浏览器进行的地址重定向
handle(request, response, authentication);
//清除session中的认证信息
clearAuthenticationAttributes(request);
}
//AbstractAuthenticationTargetUrlRequestHandler.java
protected void handle(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
String targetUrl = determineTargetUrl(request, response, authentication);
if (response.isCommitted()) {
logger.debug("Response has already been committed. Unable to redirect to "
+ targetUrl);
return;
}
redirectStrategy.sendRedirect(request, response, targetUrl);
}
4、defaultSuccessUrl()方法
successForwardUrl()方法是通过服务端转发实现页面跳转的,本质上是使用了ForwardAuthenticationSuccessHandler处理器,具体实现如下:
//FormLoginConfigurer.java
public FormLoginConfigurer<H> successForwardUrl(String forwardUrl) {
successHandler(new ForwardAuthenticationSuccessHandler(forwardUrl));
return this;
}
//AbstractAuthenticationFilterConfigurer.java
public final T successHandler(AuthenticationSuccessHandler successHandler) {
this.successHandler = successHandler;
return getSelf();
}
ForwardAuthenticationSuccessHandler类
ForwardAuthenticationSuccessHandler类是defaultSuccessUrl()方法实现认证成功处理的核心逻辑,主要在onAuthenticationSuccess()方法中定义,实现如下:
public class ForwardAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
private final String forwardUrl;
public ForwardAuthenticationSuccessHandler(String forwardUrl) {
Assert.isTrue(UrlUtils.isValidRedirectUrl(forwardUrl),
() -> "'" + forwardUrl + "' is not a valid forward URL");
this.forwardUrl = forwardUrl;
}
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
//实现服务端转发,且只考虑配置的地址,不考虑是否有认证地址需要跳转
request.getRequestDispatcher(forwardUrl).forward(request, response);
}
}
5、自定义处理器方式
自定义处理器方式其实就是认证成功处理流程的最基本的实现方式,前面两种也是SpringSecurity框架基于这种方式提供了两种常用的方案而已。配置方法实现如下:
//AbstractAuthenticationFilterConfigurer.java
public final T successHandler(AuthenticationSuccessHandler successHandler) {
this.successHandler = successHandler;
return getSelf();
}
6、认证成功处理的工作原理
认证成功处理主要包括了上述配置内容的初始化和认证成功后执行两部分。
6.1、初始化操作
在启动项目的时候,会根据配置内容进行初始化操作。配置是在AbstractAuthenticationFilterConfigurer类的configure()方法中来完成的。实现如下:
//AbstractAuthenticationFilterConfigurer.java
//默认赋值
private AuthenticationSuccessHandler successHandler = this.defaultSuccessHandler;
//用户自定义配置,前面三种配置方式,最后都是调用了该方法,即为successHandler 变量赋值,配置了我们定义的处理器。
public final T successHandler(AuthenticationSuccessHandler successHandler) {
this.successHandler = successHandler;
return getSelf();
}
//为过滤器设置认证成功处理器
@Override
public void configure(B http) throws Exception {
//省略 ……
RequestCache requestCache = http.getSharedObject(RequestCache.class);
if (requestCache != null) {
this.defaultSuccessHandler.setRequestCache(requestCache);
}
authFilter.setAuthenticationSuccessHandler(successHandler);
//省略 ……
}
在上述方法中,主要实现了为过滤器(UsernamePasswordAuthenticationFilter对象)配置认证成功处理器,实际定义是在父类AbstractAuthenticationProcessingFilter中,实现如下:
//AbstractAuthenticationProcessingFilter.java
public void setAuthenticationSuccessHandler(
AuthenticationSuccessHandler successHandler) {
Assert.notNull(successHandler, "successHandler cannot be null");
this.successHandler = successHandler;
}
通过上述的过程,项目启动后,配置的认证成功处理器,实际上就已经配置到对应的过滤器上,并完成了初始化的工作。
6.2、处理过程
前面分析了处理器是如何进行初始化的。那么初始化之后,又是如何产生作用的呢?我们下面开始分析认证成功处理器是如何工作的。
认证成功处理器其实就是在AbstractAuthenticationProcessingFilter类的successfulAuthentication()方法中调用执行的,实现如下:
protected void successfulAuthentication(HttpServletRequest request,
HttpServletResponse response, FilterChain chain, Authentication authResult)
throws IOException, ServletException {
//省略 ……
SecurityContextHolder.getContext().setAuthentication(authResult);
rememberMeServices.loginSuccess(request, response, authResult);
if (this.eventPublisher != null) {
eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(
authResult, this.getClass()));
}
successHandler.onAuthenticationSuccess(request, response, authResult);
}
在successfulAuthentication()方法中,首先为SecurityContextHolder对象设置认证信息,然后调用rememberMeServices的loginSuccess()方法,实现登录成功后的缓存处理,通过该方法可以实现“记住登录密码”功能,最后调用successHandler的onAuthenticationSuccess()方法,实现认证成功处理器的调用和执行,登录认证成功,我们可以跳转到登录成功的首页或访问的需要认证地址(一般单应用)也可以通过实现自定义的处理器实现返回json数据等,前面已经介绍了三种方式是如何实现登录成功的后续处理的,这里不再重复。
至此,我们就梳理完了 认证成功处理器的工作原理和处理流程,后续我们将继续学习SpringSecurity中其他的知识点,努力向前!!!