1、OAuth2里面有很多个TokenStore的实现,例如RedisTokenStore,JdbcTokenStore等等,本文以RedisTokenStore实现。

2、自定义CustomAuthorizationManager类实现ReactiveAuthorizationManager接口,重写check()方法

3、关键代码如下

@Component
@Slf4j
public class CustomAuthorizationManager implements ReactiveAuthorizationManager<AuthorizationContext> {

    @Autowired
    private RedisConnectionFactory redisConnectionFactory;
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * Determines if access is granted for a specific authentication and object.
     *
     * @param authenticationMono the Authentication to check
     * @param object             the object to check
     * @return an decision or empty Mono if no decision could be made.
     */
    @Override
    public Mono<AuthorizationDecision> check(Mono<Authentication> authenticationMono, AuthorizationContext object) {
        ServerWebExchange exchange = object.getExchange();
        String requestPath = exchange.getRequest().getURI().getPath();
        log.debug("当前请求路径:{}", requestPath);

        PathMatcher pathMatcher = new AntPathMatcher();
        //校验当前请求的url是否在白名单内
        Set<String> whiteUrlList = redisTemplate.opsForSet().members(RedisConstant.WHITE_URL_LIST);
        if (CollectionUtil.isNotEmpty(whiteUrlList)){
            for (String whiteUrl : whiteUrlList) {
                if (pathMatcher.match(whiteUrl, requestPath)) {
                    //将请求头的token移除,避免授权服务校验token
                    ServerHttpRequest request = exchange.getRequest().mutate()
                            .headers(httpHeaders -> httpHeaders.remove("Authorization")).build();
                    log.debug("url: {}, path: {},白名单地址直接放行,并将当前的请求头的token移除",whiteUrl,requestPath);
                    exchange.mutate().request(request).build();
                    return Mono.just(new AuthorizationDecision(true));
                }
            }
        }

        RedisTokenStore redisTokenStore = new RedisTokenStore(redisConnectionFactory);
        String authorizationToken = exchange.getRequest().getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
        log.debug("当前请求头Authorization中的值:{}",authorizationToken);
        if (StringUtils.isBlank(authorizationToken)) {
            log.warn("当前请求头Authorization中的值不存在");
            return Mono.just(new AuthorizationDecision(false));
        }

        String token = authorizationToken.replace(OAuth2AccessToken.BEARER_TYPE + " ", "");
        OAuth2Authentication oAuth2Authentication = redisTokenStore.readAuthentication(token);
        log.debug("当前token所拥有的OAuth2Authentication:{}", oAuth2Authentication);
        Collection<GrantedAuthority> authorities = oAuth2Authentication.getAuthorities();
        log.debug("当前token所拥有的权限:{}",authorities.toString());

        for (GrantedAuthority authority : authorities) {
            String authorityStr= authority.getAuthority();
            if (pathMatcher.match(authorityStr, requestPath)) {
                //设置请求头参数username
                ServerHttpRequest request = exchange.getRequest().mutate()
                        .headers(httpHeaders -> httpHeaders.add("username",oAuth2Authentication.getName())).build();
                exchange.mutate().request(request).build();
                return Mono.just(new AuthorizationDecision(true));
            }
        }
        return Mono.just(new AuthorizationDecision(false));
    }

}

4、主要思路是:

a.获取当前访问路径,当前访问路径是否在配置的url白名单里面,若是则将当前请求的请求头“Authorization”移除,避免授权服务继续校验token,反之则执行以下校验

b、获取当前请求的token值,不存在则校验失败

c、通过redisTokenStore的readAuthentication方法获取当前token所拥有的权限

d、当前token拥有的权限与当前访问地址比对,比对结果则为校验结果

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐