(WebFlux)spring-security保护中的两个主要过滤器

认证过滤器:AuthenticationWebFilter

授权过滤器:AuthorizationWebFilter

避免歧义:此处的认证不是指的登录。是指在请求接口时,此请求通过的spring-security中的两个主要过滤器。

基本流程:请求首先通过AuthenticationWebFilter,以认证是否携带token,且token是否有效。
然后通过AuthorizationWebFilter,来检查该用户会否包含访问该接口的权限。

AuthenticationWebFilter.java (认证过滤器)

public Mono filter(ServerWebExchange exchange, WebFilterChain chain) {
        return this.requiresAuthenticationMatcher.matches(exchange).filter((matchResult) -> {
            return matchResult.isMatch();
        }).flatMap((matchResult) -> {
            return this.authenticationConverter.convert(exchange);
        }).switchIfEmpty(chain.filter(exchange).then(Mono.empty())).flatMap((token) -> {
            return this.authenticate(exchange, chain, token);
        });
    }
  1. 筛选出所有需要认证的路径。(requiresAuthenticationMatcher)
  2. 从exchange请求对象中获取token并转化成Authentication对象。(authenticationConverter)
  3. 如果Authentication对象为空,则直接通过过滤器,否则对Authentication中的token进行验证。(authenticate())
  • authenticate方法验证token的主要逻辑是:
    -- 使用authenticationManager对象的authenticate方法进行验证
    -- 如果验证成功则调用onAuthenticationSuccess方法,将Authentication对象存入请求的上下文中(context)。
    -- 如果验证失败则authenticationFailureHandler对象的onAuthenticationFailure方法,默认是返回401。

  • 这里面需要注意的一点是:如果你的某个接口权限是permit,但是如果访问的时候携带了一个过期或非法的token,这时候是无法通过的。对于permit的接口,只有完全不携带token时才能正常访问。
  • 因为没等到进入授权过滤器,就被认证过滤器处理了。
  • 如果想要解决这个问题,可以考虑修改这里的逻辑。

AuthorizationWebFilter.java (授权过滤器)

public Mono filter(ServerWebExchange exchange, WebFilterChain chain) {
        return ((Mono)ReactiveSecurityContextHolder.getContext().filter((c) -> {
            return c.getAuthentication() != null;
        }).map(SecurityContext::getAuthentication).as((authentication) -> {
            return this.accessDecisionManager.verify(authentication, exchange);
        })).switchIfEmpty(chain.filter(exchange));
    }
  1. 从请求上下文(context)中拿到Authentication对象(因为认证过滤器之后会有相应过滤器对不包含token的请求做处理,所以到这里都是有Authentication对象的)
  2. 使用accessDecisionManager对象的verify方法对请求做鉴权。
  3. accessDecisionManager对象的类名是ReactiveAuthorizationManager
    包含验证权限的具体逻辑,默认的逻辑是【通过优先】
  • 通过优先:如果权限A和权限B都包含同一个接口,用户包含其中任意一个权限即可通过验证。
  • 拒绝优先:如果权限A和权限B都包含同一个path,用户必须同时有权限A和权限B才能访问这个接口。(拒绝优先听着好像更安全,但是实际很蠢,而且不符合RABC模型)
  • 注:
    -- 如果希望更改为【拒绝优先】的逻辑,需要重写此类。(后面提供两种模式代码比对)
    -- accessDecisionManager对象是AuthorizationWebFilter初始化的时候指定的,不支持动态替换。
    如果需要做【动态的权限映射】可以重写AuthorizationWebFilter,并提供accessDecisionManager对象的set方法。
  • 静态的权限路径映射:是指程序启动时载入,程序运行时不可更改,修改后需重启程序。
  • 动态的权限路径映射:可程序启动时载入,并且程序运行时可以修改,修改后需要重新set accessDecisionManager对象。

通过优先和拒绝优先的代码比较
默认的accessDecisionManager对象具体的实现类是
DelegatingReactiveAuthorizationManager.class
其中包含成员变量mappings(权限路径的映射列表)
mappings是常量,在该类构造方法中指定。

通过优先

public Mono check(Mono authentication, ServerWebExchange exchange) {
        return Flux.fromIterable(this.mappings).concatMap((mapping) -> {
            return mapping.getMatcher().matches(exchange).filter(MatchResult::isMatch).map((r) -> {
                return r.getVariables();
            }).flatMap((variables) -> {
                return ((ReactiveAuthorizationManager)mapping.getEntry()).check(authentication, new AuthorizationContext(exchange, variables));
            });
        }).next().defaultIfEmpty(new AuthorizationDecision(false));
    }

拒绝优先

public Mono check(Mono authentication, ServerWebExchange exchange) {
        return Flux.fromIterable(this.mappings).concatMap((mapping) -> {
            return mapping.getMatcher().matches(exchange).filter(ServerWebExchangeMatcher.MatchResult::isMatch).map((r) -> {
                return r.getVariables();
            }).flatMap((variables) -> {
                return mapping.getEntry().check(authentication, new AuthorizationContext(exchange, variables));
            });
        })
        .switchIfEmpty(Mono.just(new AuthorizationDecision(false)))
        .filter(authorizationDecision -> !authorizationDecision.isGranted())
        .next()
        .defaultIfEmpty(new AuthorizationDecision(true))
        ;
    }

你可能感兴趣的:((WebFlux)spring-security保护中的两个主要过滤器)