1. 前言

最近几篇我对Spring Security中用户认证流程进行了分析,同时在分析的基础上我们实现了一个验证码登录认证的实战功能。当认证失败后交给了AuthenticationFailureHandler来进行失败后的逻辑处理。今天来谈谈两个和认证授权息息相关的两个状态401403以及它们如何在Spring Security融入体系中的

2. 401 未授权

我在RFC 7235[1]中找到了相关的表述。当客户端收到401状态码时,表明了该请求因为缺乏了被信任的认证凭据而被拒绝访问目标资源。

如果用户在请求中携带了认证凭据,那么401响应表明该凭据是未授信的,不能访问目标资源。服务端的态度是用户应当再次进行尝试,并且应该引导客户端至少再尝试一次。比如,用户输错了密码,服务器应该告诉用户密码错误,并再次进行尝试。

3. 403 禁止访问

表述参见RFC 7231[2]。403状态代码表示服务器已理解了客户端的请求,但拒绝授权。如果请求中提供了身份验证凭据,则服务器认为它们不足以授予访问权限。客户端不应自动携带相同的重复证书再次请求。但是,出于某些原因,请求可能被禁止与凭据无关。如果服务器认为这些反馈信息比较敏感,可以用404来代替。

4. Spring Security 中的这两种状态

通常情况Spring Security中的401403两种状态都是以异常的形式来进行体现的,由AuthenticationExceptionAccessDeniedException两套异常体系承担。它们相关接口有AuthenticationEntryPointAuthenticationFailureHandlerAccessDeniedHandler。仅仅当登录认证失败返回了401,其它情况的这两种异常都返回了403

Spring Security 实战干货: 401和403状态_第1张图片Spring Security异常处理体系

默认情况下他们都会被转发到异常页面。然而目前在前后端分离的架构下,返回JSON才是更加合适的。所以我们需要针对以上几个接口进行定制化实现来满足前后端分离的需要。

其实重新实现AuthenticationEntryPointAccessDeniedHandler即可。因为Spring Security已经提供了下面这个实现供登录失败使用:

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);
   }
}

具体的配置可参考之前的 Spring Security 实战干货:自定义异常处理[3]。

参考资料

[1]RFC 7235: https://tools.ietf.org/html/rfc7235[2]RFC 7231: https://tools.ietf.org/html/rfc7231[3]Spring Security 实战干货:自定义异常处理: https://felord.cn/spring-security-exception.html