AuthorizationFilter过滤器的功能

AuthorizationFilter过滤器的作用

在 Spring Security 框架中,AuthorizationFilter 是负责执行授权(Authorization) 决策的核心过滤器。根据当前的 Authentication(认证信息)和请求上下文,判断当前用户是否有权限访问目标资源。

先看段源码

java 复制代码
public class AuthorizationFilter extends GenericFilterBean {

   ...
   private final AuthorizationManager<HttpServletRequest> authorizationManager;

   

   /**
    * Creates an instance.
    * @param authorizationManager the {@link AuthorizationManager} to use
    */
   public AuthorizationFilter(AuthorizationManager<HttpServletRequest> authorizationManager) {
      Assert.notNull(authorizationManager, "authorizationManager cannot be null");
      this.authorizationManager = authorizationManager;
   }

   @Override
   public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
         throws ServletException, IOException {

      HttpServletRequest request = (HttpServletRequest) servletRequest;
      HttpServletResponse response = (HttpServletResponse) servletResponse;

      if (this.observeOncePerRequest && isApplied(request)) {
         chain.doFilter(request, response);
         return;
      }

      if (skipDispatch(request)) {
         chain.doFilter(request, response);
         return;
      }

      String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();
      request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);
      try {
         //授权决策
         AuthorizationDecision decision = this.authorizationManager.check(this::getAuthentication, request);
         this.eventPublisher.publishAuthorizationEvent(this::getAuthentication, request, decision);
         if (decision != null && !decision.isGranted()) {
            throw new AccessDeniedException("Access Denied");
         }
         chain.doFilter(request, response);
      }
      finally {
         request.removeAttribute(alreadyFilteredAttributeName);
      }
   }

从上面的代码中可以看出:AuthorizationFilter 本身不直接做授权判断,而是委托给一个 AuthorizationManager 接口.

AuthorizationManager

从上面的代码中可以看出,授权的核心在于调用authorizationManager的check方法,去决定当前的authentication是否有权限访问request

kotlin 复制代码
//授权决策
AuthorizationDecision decision = this.authorizationManager.check(this::getAuthentication, request);
typescript 复制代码
@FunctionalInterface
public interface AuthorizationManager<T> {

   /**
    * Determines if access should be granted for a specific authentication and object.
    * @param authentication the {@link Supplier} of the {@link Authentication} to check
    * @param object the {@link T} object to check
    * @throws AccessDeniedException if access is not granted
    */
   default void verify(Supplier<Authentication> authentication, T object) {
      AuthorizationDecision decision = check(authentication, object);
      if (decision != null && !decision.isGranted()) {
         throw new AccessDeniedException("Access Denied");
      }
   }

   /**
    * Determines if access is granted for a specific authentication and object.
    * @param authentication the {@link Supplier} of the {@link Authentication} to check
    * @param object the {@link T} object to check
    * @return an {@link AuthorizationDecision} or null if no decision could be made
    */
   @Nullable
   AuthorizationDecision check(Supplier<Authentication> authentication, T object);

}

AuthorizationManager接口定义了check方法,为什么说叫授权决策,因为check方法返回的是AuthorizationDecision,这是一种授权结果 里面有个字段granted,用来决定是否有权限继续访问当前资源。

typescript 复制代码
    public class AuthorizationDecision implements AuthorizationResult {

   private final boolean granted;

   public AuthorizationDecision(boolean granted) {
      this.granted = granted;
   }

   @Override
   public boolean isGranted() {
      return this.granted;
   }

   @Override
   public String toString() {
      return getClass().getSimpleName() + " [granted=" + this.granted + "]";
   }

}
    

RequestMatcherDelegatingAuthorizationManager

如果你去调试你会发现AuthorizationFilter委托的AuthorizationManager的实现类是 RequestMatcherDelegatingAuthorizationManager,至于为什么是这个实现类,我们后续会出一个文章专门来讲解,大体还是HttpSecurity,因为它就是用来配置过滤器链的,底层就是配置过滤器的。这里不会多讲。拆解一下这个类的名字

  • RequestMatcher:请求匹配器,用于匹配不同的 HTTP 请求(如 /admin/**, /public/**

  • Delegating:代表"委派",即它自己不做判断,而是把任务委派 给其他具体的 AuthorizationManager

  • AuthorizationManager:授权管理接口,负责做出"允许"或"拒绝"的决策

RequestMatcherDelegatingAuthorizationManager 内部维护了一个列表mappings:

java 复制代码
public final class RequestMatcherDelegatingAuthorizationManager implements AuthorizationManager<HttpServletRequest> {

   private static final AuthorizationDecision DENY = new AuthorizationDecision(false);

   private final List<RequestMatcherEntry<AuthorizationManager<RequestAuthorizationContext>>> mappings;
kotlin 复制代码
/**
 * A rich object for associating a {@link RequestMatcher} to another object.
 * 从注释中可以看出RequestMatcherEntry就是用来将RequestMatcher和其他对象建立关联的
 * @author Marcus Da Coregio
 * @since 5.5.5
 */
public class RequestMatcherEntry<T> {

   private final RequestMatcher requestMatcher;

   private final T entry;

   public RequestMatcherEntry(RequestMatcher requestMatcher, T entry) {
      this.requestMatcher = requestMatcher;
      this.entry = entry;
   }

   public RequestMatcher getRequestMatcher() {
      return this.requestMatcher;
   }

   public T getEntry() {
      return this.entry;
   }

}

mappings中的每个条目RequestMatcherEntry代表着一种映射关系,里面包含了

  • 一个 RequestMatcher(比如 AntPathRequestMatcher("/admin/**", "GET")

  • 一个对应的 AuthorizationManager(比如负责判断 hasRole('ADMIN') 的管理器)

接下来我们看下RequestMatcherDelegatingAuthorizationManager的check流程

kotlin 复制代码
/**
 * Delegates to a specific {@link AuthorizationManager} based on a
 * {@link RequestMatcher} evaluation.
 * @param authentication the {@link Supplier} of the {@link Authentication} to check
 * @param request the {@link HttpServletRequest} to check
 * @return an {@link AuthorizationDecision}. If there is no {@link RequestMatcher}
 * matching the request, or the {@link AuthorizationManager} could not decide, then
 * null is returned
 */
@Override
public AuthorizationDecision check(Supplier<Authentication> authentication, HttpServletRequest request) {
   if (this.logger.isTraceEnabled()) {
      this.logger.trace(LogMessage.format("Authorizing %s", requestLine(request)));
   }
   for (RequestMatcherEntry<AuthorizationManager<RequestAuthorizationContext>> mapping : this.mappings) {

      RequestMatcher matcher = mapping.getRequestMatcher();
      MatchResult matchResult = matcher.matcher(request);
      if (matchResult.isMatch()) {
         AuthorizationManager<RequestAuthorizationContext> manager = mapping.getEntry();
         if (this.logger.isTraceEnabled()) {
            this.logger.trace(
                  LogMessage.format("Checking authorization on %s using %s", requestLine(request), manager));
         }
         return manager.check(authentication,
               new RequestAuthorizationContext(request, matchResult.getVariables()));
      }
   }
   if (this.logger.isTraceEnabled()) {
      this.logger.trace(LogMessage.of(() -> "Denying request since did not find matching RequestMatcher"));
   }
   return DENY;
}

从上面的代码我们可以看出check的核心流程是:当请求到来时,它会:

  1. 遍历这个列表,取出里面的每个RequestMatcher,然后去判断当前RequestMatcher是否和当前请求request匹配,找到匹配后,找到和RequestMatcher匹配的AuthorizationManager

  2. 将授权任务委托 给该条目对应的 AuthorizationManager

  3. 如果没有任何匹配,默认拒绝(或使用默认策略)。

mappings列表中注册的RequestMatcher和AuthorizationManager关系是如何绑定的

后续我会专门出一期文章从源码角度讲解RequestMatcherDelegatingAuthorizationManager是如何完成创建以及初始化mappings的,这里我们会如何使用就好啦。我们经常在配置过滤器链的时候有如下配置

less 复制代码
@Bean
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http)
        throws Exception {
    System.out.println("filterChain http: " + System.identityHashCode(http));
    http
            .authorizeHttpRequests((authorize) -> authorize
                    .requestMatchers("/admin/**").hasRole("ADMIN")
                    .anyRequest().authenticated()
            );
           

    return http.build();
}

比如这句代码.requestMatchers("/admin/**").hasRole("ADMIN")就是在配置mappings映射关系,也就是当访问/admin/**时,底层会创建一个AuthorityAuthorizationManager来处理授权决策(这块后面出一篇文章细讲),.hasRole("ADMIN")底层会创建一个AuthorizationManager来处理授权请求。

授权结果

从 AuthorizationFilter的源码中我们知道,如果授权没通过的话就会抛出一个AccessDeniedException异常,这个异常通常会被ExceptionTranslationFilter过滤器捕获 然后进行处理,这个我们后续再将

csharp 复制代码
    if (decision != null && !decision.isGranted()) { throw new AccessDeniedException("Access Denied"); }

总结

AuthorizationFilter过滤器是用来实现授权决策的,但是它会委托给 RequestMatcherDelegatingAuthorizationManager来实现具体的授权决策,结合当前请求和authentication来决定是否有权限。但是这里我也留下了几处坑,没有填,后续我会写文章继续填坑

  1. AuthorizationFilter里面的RequestMatcherDelegatingAuthorizationManager是什么时候配置的
  2. RequestMatcherDelegatingAuthorizationManager需要维护一个mappings,用来映射requestMatcher和authorizationManager
  3. 用户配置调用的requestMatchers("/admin/**").hasRole("ADMIN") 等配置,最后怎么生效的,底层原理是什么 4.授权没通过抛出的异常被 ExceptionTranslationFilter捕获后如何自定义处理。
相关推荐
带刺的坐椅12 分钟前
老码农教你:Solon + EasyExcel 导出工具
java·excel·solon·easyexcel
pany16 分钟前
体验一款编程友好的显示器
前端·后端·程序员
Java水解22 分钟前
深度剖析【Spring】事务:万字详解,彻底掌握传播机制与事务原理
后端·spring
迷知悟道22 分钟前
java面向对象的四大核心特征之继承---超详细(保姆级)
java
lixn23 分钟前
深入理解JVM字节码:invokedynamic
java·jvm
数据智能老司机26 分钟前
探索Java 全新的线程模型——结构化并发
java·性能优化·架构
数据智能老司机27 分钟前
探索Java 全新的线程模型——作用域值
java·性能优化·架构
开始学java28 分钟前
继承树追溯
后端
数据智能老司机29 分钟前
探索Java 全新的线程模型——并发模式
java·性能优化·架构
何中应33 分钟前
分布式事务的两种解决方案
java·分布式·后端