SpringSecurity系列 之 认证失败处理流程

您所在的位置:网站首页 登录权限验证失败怎么办 SpringSecurity系列 之 认证失败处理流程

SpringSecurity系列 之 认证失败处理流程

2024-07-17 02:08| 来源: 网络整理| 查看: 265

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 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3