1、常见用法

我们使用SpringSecurity进行配置的时候,有三种方式实现认证失败时的后续处理:其一,通过failureUrl()配置认证失败的重定向路径(Redirect);其二,我们还可以通过failureForwardUrl()配置认证失败的转发路径(Forward),和重定向效果类似,区别主要在于前者是重定向(默认),后者是转发;其三,自定义认证失败处理器,主要通过实现AuthenticationFailureHandler接口实现,其实前面两种方式也是通过实现该接口实现的。

failureUrl()配置方式:

//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")

.failureUrl("/login/error");

}

failureForwardUrl()配置方式,和failureUrl()方法类似:

//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")

.failureForwardUrl("/login/error");

}

自定义认证失败处理器,其实就是定义一个实现AuthenticationFailureHandler接口的处理类,然后通过failureHandler()方法进行注册就可以了,实现如下:

//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")

.failureHandler(new AuthenticationFailureHandler() {

@Override

public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {

//处理逻辑

}

});

}

2、AuthenticationFailureHandler接口

AuthenticationFailureHandler是认证失败后的处理接口,通过实现该接口,可以定义各类复杂的处理方式。默认的行为是,认证失败时,会重定向到登录页面。

AuthenticationFailureHandler类结构如下:

AuthenticationFailureHandler接口定义如下,其中onAuthenticationFailure()方法就是约定了认证失败后,将要执行的方法,该方法会在AbstractAuthenticationProcessingFilter类的unsuccessfulAuthentication()方法中调用。

public interface AuthenticationFailureHandler {

void onAuthenticationFailure(HttpServletRequest request,

HttpServletResponse response, AuthenticationException exception)

throws IOException, ServletException;

}

3、failureForwardUrl()方法

failureForwardUrl()方法和failureUrl()方法是类似的,不过failureForwardUrl()方法使用的是转发技术,由服务端触发跳转的。底层的实现,还是通过实现AuthenticationFailureHandler接口实现,具体实现如下:

//FormLoginConfigurer.java

public FormLoginConfigurer failureForwardUrl(String forwardUrl) {

failureHandler(new ForwardAuthenticationFailureHandler(forwardUrl));

return this;

}

//AbstractAuthenticationFilterConfigurer.java,是FormLoginConfigurer的父类

public final T failureHandler(

AuthenticationFailureHandler authenticationFailureHandler) {

this.failureUrl = null;

this.failureHandler = authenticationFailureHandler;

return getSelf();

}

通过上述代码,我们知道:failureForwardUrl()方法就是创建了一个ForwardAuthenticationFailureHandler对象,然后把该对象赋值给了failureHandler 变量,而ForwardAuthenticationFailureHandler就是AuthenticationFailureHandler接口的实现类,具体实现如下:

public class ForwardAuthenticationFailureHandler implements AuthenticationFailureHandler {

private final String forwardUrl;

public ForwardAuthenticationFailureHandler(String forwardUrl) {

Assert.isTrue(UrlUtils.isValidRedirectUrl(forwardUrl),

() -> "'" + forwardUrl + "' is not a valid forward URL");

this.forwardUrl = forwardUrl;

}

//通过foward方式实现跳转

public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {

request.setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION, exception);

request.getRequestDispatcher(forwardUrl).forward(request, response);

}

}

4、failureUrl()方法

failureUrl()方法是通过浏览器重定向,实现认证失败后的跳转的,其实failureUrl()方法也可以实现和failureForwardUrl()方法一模一样的功能,我们一步步进行分析。

首先,failureUrl()方法的实现,和failureForwardUrl()方法类似,不过这里使用的是SimpleUrlAuthenticationFailureHandler处理器,具体如下:

//AbstractAuthenticationFilterConfigurer.java

public final T failureUrl(String authenticationFailureUrl) {

T result = failureHandler(new SimpleUrlAuthenticationFailureHandler(

authenticationFailureUrl));

this.failureUrl = authenticationFailureUrl;

return result;

}

通过上述代码,我们可以清楚的知道使用了SimpleUrlAuthenticationFailureHandler对象作为处理,我们下面分析一下该处理器的onAuthenticationFailure()方法,实现如下:

//SimpleUrlAuthenticationFailureHandler.java

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(HttpStatus.UNAUTHORIZED.value(),

HttpStatus.UNAUTHORIZED.getReasonPhrase());

}else {

//保存异常信息

saveException(request, exception);

if (forwardToDestination) {

logger.debug("Forwarding to " + defaultFailureUrl);

request.getRequestDispatcher(defaultFailureUrl)

.forward(request, response);

}else {

logger.debug("Redirecting to " + defaultFailureUrl);

redirectStrategy.sendRedirect(request, response, defaultFailureUrl);

}

}

}

SimpleUrlAuthenticationFailureHandler的onAuthenticationFailure()方法其实提供了重定向(默认)和转发两种实现方式,通过forwardToDestination变量进行控制。

5、自定义处理器方式

自定义处理器方式其实就是认证失败处理流程的最基本的实现方式,前面两种也是SpringSecurity框架基于这种方式提供了两种常用的方案而已。配置方法实现如下:

public final T failureHandler(

AuthenticationFailureHandler authenticationFailureHandler) {

this.failureUrl = null;

this.failureHandler = authenticationFailureHandler;

return getSelf();

}

这里也是直接把自定义的处理器直接赋值给了failureHandler 变量,等待后续使用。

6、AuthenticationEntryPointFailureHandler类

该类主要是为了适配AuthenticationEntryPoint实现,其中onAuthenticationFailure()方法其实就是由AuthenticationEntryPoint实现类的commence()方法实现,具体代码如下:

public class AuthenticationEntryPointFailureHandler implements AuthenticationFailureHandler {

private final AuthenticationEntryPoint authenticationEntryPoint;

public AuthenticationEntryPointFailureHandler(AuthenticationEntryPoint authenticationEntryPoint) {

Assert.notNull(authenticationEntryPoint, "authenticationEntryPoint cannot be null");

this.authenticationEntryPoint = authenticationEntryPoint;

}

@Override

public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,

AuthenticationException exception) throws IOException, ServletException {

this.authenticationEntryPoint.commence(request, response, exception);

}

}

7、认证失败处理过程

如果我们没有配置任何认证失败处理相关内容,当我们输入错误的用户名或密码的时候,我们会发现会重新定向到登录页面,而且路径会添加上"?error"字符,例如"http://localhost:8888/qriver-admin/goLogin?error",为什么会这样呢?我们下面一步步进行分析。

7.1、初始化配置

在启动项目的时候,如果我们没有进行认证失败处理器的配置,系统会默认为我们配置一个,该方法主要在AbstractAuthenticationFilterConfigurer类的updateAuthenticationDefaults()方法中实现,在该方法中其实定义了loginProcessingUrl、failureHandler和loginPage三类默认配置,具体实现如下:

//AbstractAuthenticationFilterConfigurer.java

protected final void updateAuthenticationDefaults() {

if (loginProcessingUrl == null) {

loginProcessingUrl(loginPage);

}

if (failureHandler == null) {

failureUrl(loginPage + "?error");

}

final LogoutConfigurer logoutConfigurer = getBuilder().getConfigurer(

LogoutConfigurer.class);

if (logoutConfigurer != null && !logoutConfigurer.isCustomLogoutSuccess()) {

logoutConfigurer.logoutSuccessUrl(loginPage + "?logout");

}

}

上述updateAuthenticationDefaults()方法会在init()和loginPage()两个方法中被调用。如果我们通过loginPage()配置了自定义的登录界面,那么就会重定向到我们自定义的页面,否则就会重定向到默认的登录页面。

然后,updateAuthenticationDefaults()方法又通过调用failureUrl()方法进行配置,这个时候实现了failureUrl 和failureHandler 的初始化,其中failureHandler 处理器实际上就是使用的SimpleUrlAuthenticationFailureHandler对象,实现如下:

//AbstractAuthenticationFilterConfigurer.java

public final T failureUrl(String authenticationFailureUrl) {

T result = failureHandler(new SimpleUrlAuthenticationFailureHandler(

authenticationFailureUrl));

this.failureUrl = authenticationFailureUrl;

return result;

}

public final T failureHandler(

AuthenticationFailureHandler authenticationFailureHandler) {

this.failureUrl = null;

this.failureHandler = authenticationFailureHandler;

return getSelf();

}

而AbstractAuthenticationProcessingFilter对象(实际是UsernamePasswordAuthenticationFilter对象)初始化的时候,又调用AbstractAuthenticationFilterConfigurer类的configure()方法,进而调用了AbstractAuthenticationProcessingFilter对象的setAuthenticationFailureHandler()方法,把上一步中初始化的SimpleUrlAuthenticationFailureHandler对象赋值给了AbstractAuthenticationProcessingFilter对象的failureHandler 变量,实现如下:

//AbstractAuthenticationFilterConfigurer.java

@Override

public void configure(B http) throws Exception {

authFilter.setAuthenticationFailureHandler(failureHandler);

}

//AbstractAuthenticationProcessingFilter.java

public void setAuthenticationFailureHandler(

AuthenticationFailureHandler failureHandler) {

Assert.notNull(failureHandler, "failureHandler cannot be null");

this.failureHandler = failureHandler;

}

7.2、处理流程

前面提到的内容,其实都是在启动项目时,进行初始化的。那么初始化之后,又是如何产生作用的呢?我们下面开始分析认证失败处理器是如何工作的。

认证失败处理器其实就是在unsuccessfulAuthentication()方法中调用执行的,实现如下:

//AbstractAuthenticationProcessingFilter.java

protected void unsuccessfulAuthentication(HttpServletRequest request,

HttpServletResponse response, AuthenticationException failed)

throws IOException, ServletException {

SecurityContextHolder.clearContext();

//省略 debug ……

rememberMeServices.loginFail(request, response);

failureHandler.onAuthenticationFailure(request, response, failed);

}

在unsuccessfulAuthentication()方法中,首先清除SecurityContextHolder中存储的上下文信息,然后通过rememberMeServices的loginFail()方法处理浏览器缓存信息,最后通过调用failureHandler的onAuthenticationFailure()方法完成认证失败处理器的调用,这里failureHandler对象其实就是SimpleUrlAuthenticationFailureHandler对象。

首先,我们分析一下rememberMeServices的loginFail()方法,该方法就是处理浏览器缓存的,实现如下:

//AbstractRememberMeServices.java

@Override

public final void loginFail(HttpServletRequest request, HttpServletResponse response) {

logger.debug("Interactive login attempt was unsuccessful.");

cancelCookie(request, response);

onLoginFail(request, response);

}

//空方法,不做任何实现

protected void onLoginFail(HttpServletRequest request, HttpServletResponse response) {

}

//处理浏览器cookie信息

protected void cancelCookie(HttpServletRequest request, HttpServletResponse response) {

logger.debug("Cancelling cookie");

Cookie cookie = new Cookie(cookieName, null);

cookie.setMaxAge(0);

cookie.setPath(getCookiePath(request));

if (cookieDomain != null) {

cookie.setDomain(cookieDomain);

}

if (useSecureCookie == null) {

cookie.setSecure(request.isSecure());

}

else {

cookie.setSecure(useSecureCookie);

}

response.addCookie(cookie);

}

然后,我们分析一下SimpleUrlAuthenticationFailureHandler的onAuthenticationFailure()方法,该方法主要实现认证失败的后续处理,一般是实现页面的跳转或认证失败数据的返回,前面已经分析过该方法,这里不再贴出代码了。

至此基于SpringSecurity的认证失败处理流程,我们基本上就学习完了,下一节我们将继续学习其他的内容。

Copyright © 2088 1986世界杯_意大利世界杯 - zlrxcw.com All Rights Reserved.
友情链接