在webflux框架中如何校验token,验证用户是否登录

最近也是在学习webflux框架,学一点就记录一下,今天的这个问题其实在传统的spring的框架中很简单,直接在过滤器中拦截就可以了,其实在webflux响应框架中也是类似,以下是我的两种实现方式
方式1: 实现一个webFilter拦截器,在filter里面实现拦截,这里是仿照org.springframework.web.reactive.DispatcherHandler#handle方法来实现的

@Slf4j
@Component
public class LoginFilter implements WebFilter {

    @Autowired
    private LoginService loginService;
    @Autowired
    private ApplicationContext context;
    @Nullable
    private List<HandlerMapping> handlerMappings;

    private final DataBufferFactory dataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        return Flux.fromIterable(handlerMappings)
                .concatMap(mapping -> mapping.getHandler(exchange))
                .next()
                .switchIfEmpty(Mono.defer((Supplier<Mono<?>>) () -> Mono.just(createResult("找不到资源"))))
                .flatMap(handler -> {
                    // 资源,直接跳过
                    if (handler instanceof ResourceWebHandler) {
                        return chain.filter(exchange);
                    }
                    // 剩下的全判断是否需要登录
                    boolean isLogin = isLogin(handler);
                    if (!isLogin){
                        return chain.filter(exchange);
                    }
                    if (checkToken(exchange)) {
                        return chain.filter(exchange);
                    }
                    exchange.getResponse().getHeaders().add("Content-Type", "application/json");
                    Publisher<? extends DataBuffer> just = Mono.just(dataBufferFactory.wrap(createResult("未登录").getBytes()));
                    return new ChannelSendOperator<>(just, body -> exchange.getResponse().writeWith(body));
                });
    }

    /**
     * 判断是否需要登录
     *
     * @param handler
     * @return
     */
    private boolean isLogin(Object handler) {
        if (handler instanceof HandlerMethod) {
        // 这里通过自定义注解来判断是否需要登录,NoLogin为我的自定义注解
            NoLogin annotation = ((HandlerMethod) handler).getMethod().getAnnotation(NoLogin.class);
            return annotation == null;
        }
        return true;
    }

    @PostConstruct
    protected void initStrategies() {
        Map<String, HandlerMapping> mappingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
                context, HandlerMapping.class, true, false);

        ArrayList<HandlerMapping> mappings = new ArrayList<>(mappingBeans.values());
        AnnotationAwareOrderComparator.sort(mappings);
        this.handlerMappings = Collections.unmodifiableList(mappings);
    }

    private boolean checkToken(ServerWebExchange exchange) {
        final ServerHttpRequest request = exchange.getRequest();
        HttpHeaders headers = request.getHeaders();
        String token = headers.getFirst("token");
        if (token == null) {
            return false;
        }
        // 自己的校验token逻辑
        UserBean userBean = loginService.checkToken(token);
        if (userBean == null) {
            return false;
        }
        return true;
    }

    private String createResult(String errorMsg) {
        BaseResult result = new BaseResult();
        result.setMsg(errorMsg);
        result.setCode(999);
        return JSONObject.toJSONString(result);
    }
}

方式2 继承RequestMappingHandlerAdapter方法,然后通过WebFluxRegistrations注入到springWebflux中


@Slf4j
public class XRequestMappingHandlerAdapter extends RequestMappingHandlerAdapter {
    @Autowired
    private LoginService loginService;

    @Override
    public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
        boolean isLogin = isLogin(handler);
        if (!isLogin){
            return super.handle(exchange, handler);
        }
        if (checkToken(exchange)) {
            return super.handle(exchange, handler);
        }
        log.info("{}不存在token,且未登录:{}", exchange.getLogPrefix(), exchange.getRequest().getPath().value());
        return Mono.just(new HandlerResult(handler, Mono.just(new BaseResult(999, "未登录")),
                new MethodParameter(((HandlerMethod) handler).getMethod(), -1)));
    }

    /**
     * 根据注解判断是否需要登录
     *
     * @param handler
     * @return
     */
    private boolean isLogin(Object handler) {
        if (handler instanceof HandlerMethod) {
            NoLogin annotation = ((HandlerMethod) handler).getMethod().getAnnotation(NoLogin.class);
            return annotation == null;
        }
        return true;
    }

    /**
     * 校验token是否有效
     *
     * @param exchange
     * @return
     */
    private boolean checkToken(ServerWebExchange exchange) {
        final ServerHttpRequest request = exchange.getRequest();
        HttpHeaders headers = request.getHeaders();
        String token = headers.getFirst("token");
        // 获取token
        if (token == null) {
            return false;
        }
        // 校验token
        UserBean userBean = loginService.checkToken(token);
        if (userBean == null) {
            return false;
        }
        return true;
    }
}

// 通过实现WebFluxRegistrations 接口注入RequestMappingHandlerAdapter
@Slf4j
@Component
public class XWebFluxRegistrations implements WebFluxRegistrations {

    @Override
    public RequestMappingHandlerAdapter getRequestMappingHandlerAdapter() {
        return new XRequestMappingHandlerAdapter();
    }
}

以上就是两种拦截并校验token的方式,
方式1,可以拦截多种请求,如资源文件,RequestMapping,或者RouterFunctionMapping,这些都可以拦截。
方式2只能拦截RequestMapping的

如有更好的实现,欢迎交流

如果有用,麻烦动动小手点个赞 ?


版权声明:本文为weixin_38390086原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。