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捕获后如何自定义处理。
相关推荐
oak隔壁找我10 分钟前
SpringBoot 将项目打包成 Fat JAR(肥包),核心原理
后端
陌殇殇28 分钟前
001 Spring AI Alibaba框架整合百炼大模型平台 — 快速入门
人工智能·spring boot·ai
言慢行善42 分钟前
sqlserver模糊查询问题
java·数据库·sqlserver
专吃海绵宝宝菠萝屋的派大星1 小时前
使用Dify对接自己开发的mcp
java·服务器·前端
大数据新鸟1 小时前
操作系统之虚拟内存
java·服务器·网络
Tong Z1 小时前
常见的限流算法和实现原理
java·开发语言
凭君语未可1 小时前
Java 中的实现类是什么
java·开发语言
He少年1 小时前
【基础知识、Skill、Rules和MCP案例介绍】
java·前端·python
克里斯蒂亚诺更新1 小时前
myeclipse的pojie
java·ide·myeclipse