Spring Security教程外篇(1)---- AuthenticationException异常详解

这个异常是在登录的时候出现错误时抛出的异常,比如账户锁定,证书失效等,先来看下AuthenticationException常用的的子类:

UsernameNotFoundException 用户找不到

BadCredentialsException 坏的凭据

AccountStatusException 用户状态异常它包含如下子类

AccountExpiredException 账户过期

LockedException 账户锁定

DisabledException 账户不可用

CredentialsExpiredException 证书过期


常见的异常就这几个,还有很多不再一一赘述,仅仅为了展示,对后续的阅读没有什么影响

用户登录验证的过滤器是UsernamePasswordAuthenticationFilter,它继承自AbstractAuthenticationProcessingFilter。

今天研究的是看Spring如何处理AuthenticationException这个异常的,异常的处理一般都是doFilter中处理的,所以首先看AbstractAuthenticationProcessingFilter的doFilter中的代码

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {

	.......

        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) {
            logger.error("An internal error occurred while trying to authenticate the user.", failed);
            unsuccessfulAuthentication(request, response, failed);

            return;
        }catch (AuthenticationException failed) {
            // Authentication failed
            unsuccessfulAuthentication(request, response, failed);

            return;
        }

        ......
    }
从这段代码中我们看到Spring将异常捕获后交给了unsuccessfulAuthentication这个方法来处理(InternalAuthenticationServiceException也是AuthenticationException异常的子类,这是一个内部认证服务异常)

    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
            AuthenticationException failed) throws IOException, ServletException {
        SecurityContextHolder.clearContext();

        if (logger.isDebugEnabled()) {
            logger.debug("Authentication request failed: " + failed.toString());
            logger.debug("Updated SecurityContextHolder to contain null Authentication");
            logger.debug("Delegating to authentication failure handler " + failureHandler);
        }

        rememberMeServices.loginFail(request, response);

        failureHandler.onAuthenticationFailure(request, response, failed);
    }
unsuccessfulAuthentication又交给了failureHandler(AuthenticationFailureHandler)来处理,然后追踪failureHandler

private AuthenticationFailureHandler failureHandler = 
	new SimpleUrlAuthenticationFailureHandler();
发现最终是由SimpleUrlAuthenticationFailureHandler这个类的onAuthenticationFailure方法来处理的,打开这个类,有两个核心方法
   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);

            if (forwardToDestination) {
                logger.debug("Forwarding to " + defaultFailureUrl);

                request.getRequestDispatcher(defaultFailureUrl).forward(request, response);
            } else {
                logger.debug("Redirecting to " + defaultFailureUrl);
                redirectStrategy.sendRedirect(request, response, defaultFailureUrl);
            }
        }
    }

    /**
     * Caches the {@code AuthenticationException} for use in view rendering.
     * <p>
     * If {@code forwardToDestination} is set to true, request scope will be used, otherwise it will attempt to store
     * the exception in the session. If there is no session and {@code allowSessionCreation} is {@code true} a session
     * will be created. Otherwise the exception will not be stored.
     */
    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);
            }
        }
    }
从onAuthenticationFailure中我们可以看出,如果没有设置defaultFailureUrl就发出401错误。(SC_UNAUTHORIZED是一个常量值是401),如果设置了defaultFailureUrl,调用saveException方法后跳转到defaultFailureUrl页面。

forwardToDestination是是否是服务器跳转,默认使用重定向即客户端跳转。

saveException字面意思是保存异常信息,其实就是把异常信息写入request或者Session中。

如果使用服务器跳转则写入request,客户端跳转则写入Session。

而key是WebAttributes.AUTHENTICATION_EXCEPTION打开WebAttributes找到这个常量

public final class WebAttributes {
  
    public static final String ACCESS_DENIED_403 = "SPRING_SECURITY_403_EXCEPTION";

    public static final String AUTHENTICATION_EXCEPTION = "SPRING_SECURITY_LAST_EXCEPTION";

    public static final String WEB_INVOCATION_PRIVILEGE_EVALUATOR_ATTRIBUTE = 
	WebAttributes.class.getName() + ".WEB_INVOCATION_PRIVILEGE_EVALUATOR_ATTRIBUTE";
}
发现其值为SPRING_SECURITY_LAST_EXCEPTION,这样的话我们就可以在页面上通过el表达式来获取到这个异常了

注意:saveException保存的是Session对象所以直接使用 ${SPRING_SECURITY_LAST_EXCEPTION}是获取不到异常信息的,需要使用${SPRING_SECURITY_LAST_EXCEPTION.message}




  • 17
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
要将自定义的 `AuthenticationProvider` 添加到 `AuthenticationManager` 中,可以按照以下步骤进行操作: 1. 创建一个实现了 `AuthenticationProvider` 接口的类,并实现其中的 `authenticate` 方法。例如: ```java public class MyAuthenticationProvider implements AuthenticationProvider { @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { // 在这里进行身份认证逻辑的实现 } @Override public boolean supports(Class<?> authentication) { // 返回该AuthenticationProvider支持的Authentication类型 return authentication.equals(UsernamePasswordAuthenticationToken.class); } } ``` 2. 在 Spring 配置文件中配置 `AuthenticationManager` 和 `AuthenticationProvider`。例如: ```xml <security:authentication-manager> <security:authentication-provider ref="myAuthenticationProvider"/> </security:authentication-manager> <bean id="myAuthenticationProvider" class="com.example.MyAuthenticationProvider"/> ``` 3. 现在你可以在你的代码中使用 `AuthenticationManager` 进行身份认证了。例如: ```java @Autowired private AuthenticationManager authenticationManager; public void authenticateUser(String username, String password) { Authentication authentication = new UsernamePasswordAuthenticationToken(username, password); authenticationManager.authenticate(authentication); } ``` 在执行 `authenticationManager.authenticate(authentication)` 时,Spring Security 会自动调用 `MyAuthenticationProvider` 的 `authenticate` 方法进行身份认证。如果身份认证成功,则该方法会返回一个 `Authentication` 对象;如果身份认证失败,则会抛出一个 `AuthenticationException` 异常

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大扑棱蛾子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值