GlobalFilter+AbstractGatewayFilterFactory实现指定路由跳过全局过滤器

GlobalFilter+AbstractGatewayFilterFactory实现指定路由跳过全局过滤器

全局过滤器GlobalFilter

具体代码如下

package com.chanjet.dsf.web.filter;

import com.alibaba.fastjson.JSON;
import com.chanjet.dsf.cache.RedisUtil;
import com.chanjet.dsf.constant.AttrbuteConstant;
import com.chanjet.dsf.constant.ResultCodeEnum;
import com.chanjet.dsf.result.ResultFactory;
import com.chanjet.dsf.web.models.AuthTokenPo;
import com.chanjet.dsf.web.util.Constant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;

@Component
public class AuthFilter implements GlobalFilter, Ordered {

    Logger logger = LoggerFactory.getLogger(AuthFilter.class);

    private static final String TOKEN_PREFIX = "DSFTOKEN::";

    @Autowired
    RedisUtil redisUtil;

//    @Resource
//    RestTemplate restTemplate;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String token = exchange.getRequest().getQueryParams().getFirst(Constant.QUERY_PARAMS.TOKEN);

        logger.info("=======AuthFilter:token======" + token);
//        restTemplate.
        // 不判断token的过滤器,比如登录接口
        if (exchange.getAttribute(AttrbuteConstant.ATTRIBUTE_IGNORE_TEST_GLOBAL_FILTER) != null
            && exchange.getAttribute(AttrbuteConstant.ATTRIBUTE_IGNORE_TEST_GLOBAL_FILTER).equals(true)) {
            return chain.filter(exchange);
        }

        if (StringUtils.isEmpty(token)) {
            //返回一个错误码 前端去做跳转淘宝授权登录页面 (前端处理)
//           exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
//           return exchange.getResponse().setComplete();
            return generateJSON(exchange, ResultFactory.getErrorResult(ResultCodeEnum.TOKEN_IS_NULL));
        }
        String rediskey = TOKEN_PREFIX + token;
        // 通过token取缓存
        try {
            AuthTokenPo authTokenPo = (AuthTokenPo) redisUtil.get(rediskey);
            //判断token过期
            // 校验缓存的账号信息是否为空
            if (authTokenPo == null) {
                return generateJSON(exchange, ResultFactory.getErrorResult(ResultCodeEnum.TOKEN_IS_INVALID));
            }

        } catch (Exception e) {
            logger.error("AuthFilter.filter 错误信息{}", e.getMessage(), e);

        }

      

        return chain.filter(exchange);
    }


    private <T>Mono generateJSON(ServerWebExchange exchange, T t){
        ServerHttpResponse response = exchange.getResponse();
        byte[] bits = JSON.toJSONString(t).getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = response.bufferFactory().wrap(bits);
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        //指定编码,否则在浏览器中会中文乱码
        response.getHeaders().add("Content-Type", "text/plain;charset=UTF-8");
        return response.writeWith(Mono.just(buffer));
    }

    @Override
    public int getOrder() {
        return -900;
    }

}

AbstractGatewayFilterFactory 局部过滤器

内部类写法,是为了指定过滤器的优先级,要优先于全局过滤器,否则
容易造成全局过滤器 拦截到指定 局部过滤器的配置内容,从而导致局
部过滤器失效

package com.chanjet.dsf.web.filter;

import com.chanjet.dsf.constant.AttrbuteConstant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * 设置忽略路由
 */
@Component
public class IgnoreAuthFilter extends AbstractGatewayFilterFactory<IgnoreAuthFilter.Config> {

    Logger logger = LoggerFactory.getLogger(IgnoreAuthFilter.class);

    public IgnoreAuthFilter() {
        super(Config.class);
        logger.info("IgnoreFilter 进入 IgnoreAuthGatewayFilterFactory ");
    }

    @Override
    public GatewayFilter apply(Config config) {
        logger.info("IgnoreFilter 进入  apply");
        /*return (exchange, chain) -> {
            if (!config.isIgnoreGlobalFilter()) {
                return chain.filter(exchange);
            }
            return chain.filter(exchange);
        };*/
        //===============================注意=================================
        //下面的内部类写法,是为了指定过滤器的优先级,要优先于全局过滤器,否则
        //容易造成全局过滤器 拦截到指定 局部过滤器的配置内容。
        return new InnerFilter(config);
    }

    /**
     * 创建一个内部类,来实现2个接口,指定顺序
     * 这里通过Ordered指定优先级
     */
    private class InnerFilter implements GatewayFilter, Ordered {

        private Config config;

        InnerFilter(Config config) {
            this.config = config;
        }

        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            /*System.out.println("  pre 自定义过滤器工厂 AAAA  " + this.getClass().getSimpleName());
            boolean root = true == config.isIgnoreGlobalFilter();
            if (root) {
                System.out.println("  is root ");
            } else {
                System.out.println("  is no root ");
            }
            // 在then方法里的,相当于aop中的后置通知
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                System.out.println("  post 自定义过滤器工厂 AAAA " + this.getClass().getSimpleName());
            }));*/
            logger.info("进入innerFilter=====" + config.isIgnoreGlobalFilter());
            if (config.isIgnoreGlobalFilter() == true) {
                exchange.getAttributes().put(AttrbuteConstant.ATTRIBUTE_IGNORE_TEST_GLOBAL_FILTER, true);
            }
            return chain.filter(exchange);
        }

        @Override
        public int getOrder() {
            return -1000;
        }
    }


    public static class Config{
        boolean ignoreGlobalFilter;

        public boolean isIgnoreGlobalFilter() {
            return ignoreGlobalFilter;
        }

        public void setIgnoreGlobalFilter(boolean ignoreGlobalFilter) {
            this.ignoreGlobalFilter = ignoreGlobalFilter;
        }
    }

	//这个name方法 用来在yml配置中指定对应的过滤器名称
    @Override
    public String name() {
        return "IgnoreAuthFilter";
    }

}

yml配置

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
          lower-case-service-id: true
      routes:
        - id: dsf-report-ser
          uri: lb://dsf-report-ser
          predicates:
            - Path=/report/**
        - id: dsf-account-ser
          uri: lb://dsf-account-ser
          predicates:
            - Path=/account/**,/setting/**
          filters:
            - name: IgnoreAuthFilter #本路由跳过全局过滤器
              args:
                ignoreGlobalFilter: true
        - id: dsf-finance-ser
          uri: lb://dsf-finance-ser
          predicates:
            - Path=/shopTrade/**,/goods/**,/cost/**
  • 7
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
在SpringBoot+SpringSecurity+Vue中实现动态路由的过程如下: 1. 在后端(SpringBoot)中,首先需要定义一个权限表,用于存储所有的权限信息,包括权限名称、权限标识等。 2. 在前端(Vue)中,需要定义一个路由表,用于存储所有的路由信息,包括路由路径、组件名称等。 3. 后端需要提供一个接口,用于获取当前用户的权限列表。该接口会根据用户的角色查询对应的权限,并返回给前端。 4. 前端在登录成功后,会调用后端接口获取当前用户的权限列表,并将权限列表存储到本地(如localStorage或vuex)中。 5. 前端在路由跳转时,会根据当前用户的权限列表动态生成路由。可以通过遍历权限列表,根据权限标识匹配路由表中的路由信息,将匹配到的路由添加到路由表中。 6. 前端在生成路由后,需要使用Vue Router的addRoutes方法将动态生成的路由添加到路由表中。 7. 前端在路由跳转时,会根据用户的权限判断是否有权限访问该路由。可以通过导航守卫的beforeEach方法,在路由跳转前进行权限判断。 8. 后端可以使用Spring Security的注解对接口进行权限控制。可以通过在接口上添加注解,指定需要的权限才能访问该接口。 9. 后端在接口调用时,可以通过从redis中获取当前用户的权限列表,并进行权限判断。 10. 前端和后端通过接口交互,实现动态路由的权限控制。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值