Spring Cloud Gateway 登录拦截

Spring Cloud Gateway为Spring官方基于Spring 5.0、Spring Boot2.0和Project Reactor等技术开发的网关,鉴权是网关常用的功能,本文介绍基于Spring Cloud Gateway的登录拦截功能。

GlobalFilter为全局过滤器的标识接口,我这个拦截是针对所有服务,所以使用了全局过滤器。

实现Ordered为了告诉Gateway我这个过滤器的优先级,getOrder方法返回值越大优先级越低,越后执行。

还有要注意的是Spring Cloud Gateway底层使用的是webflux,基于响应式编程,我们也要避免代码中出现阻塞I/O的调用,所以我从Redis中取token使用的是非阻塞的ReactiveRedisTemplate

Talk is cheap,show me the code

@Component
@Slf4j
public class TokenFilter implements GlobalFilter, Ordered {

    private static final String TOKEN_HEAD = "token";

    /**
     * Redis中token前缀
     */
    private static final String PREFIX = "CASECODE:USER:TOKEN:";

    private static final long TIMEOUT = 60 * 30;

    /**
     * 需排除的接口
     */
    private static final Set<String> exclude_path = ImmutableSet.of(
            "/sep/user/login", "/sep/user/getCheckCode/"
    );


    @Autowired
    private ReactiveRedisTemplate<String, String> redisTemplate;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        return Mono.just(exchange)
                .map(ServerWebExchange::getRequest)
                .flatMap(serverHttpRequest -> {
                    String path = serverHttpRequest.getPath().toString();
                    log.info("path:{}",path);

                    // 排除不需要登录的接口
                    if (exclude_path.stream().anyMatch(path::contains)) {
                        log.info("无需登录");
                        return chain.filter(exchange);
                    }

                    // 从HEAD中取token
                    List<String> tokens = serverHttpRequest.getHeaders().get(TOKEN_HEAD);
                    // 无TOKEN
                    if (CollectionUtils.isEmpty(tokens)) {
                        return this.buildLoginResp(exchange);
                    }

                    String token = Iterables.getOnlyElement(tokens);

                    // 拼接前缀做为RedisKey
                    String redisKey = PREFIX + token;

                    return redisTemplate.opsForValue().get(redisKey)
                            .defaultIfEmpty(StringUtils.EMPTY)
                            .flatMap(user -> {
                                log.info("\nTOKEN:{}\nVALUE:{}", token, user);
                                // redis中有该用户
                                if (StringUtils.isNotEmpty(user)) {
                                    return redisTemplate.expire(redisKey, Duration.of(TIMEOUT, ChronoUnit.SECONDS))
                                            .then(chain.filter(exchange));
                                }
                                return this.buildLoginResp(exchange);
                            });
                });


    }

    @Override
    public int getOrder() {
        return 0;
    }

    private Mono<Void> buildLoginResp(ServerWebExchange exchange) {
        ServerHttpResponse response = exchange.getResponse();

        Map<String, Object> map = new HashMap<>();
        map.put("code", 401);
        map.put("message", "无效token");

        String json = new Gson().toJson(map);
        byte[] bytes = json.getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = response.bufferFactory().wrap(bytes);
        response.setStatusCode(HttpStatus.OK);
        return response.writeWith(Flux.just(buffer));
    }
}


Spring Cloud Gateway 登录拦截
https://blog.yjll.site/post/a9515f36.html
作者
简斋
发布于
2020年5月13日
许可协议