• SpringCloud——Gateway(Predicate断言工厂、Filter过滤器工厂、token校验)


    Predicate断言工厂使用(了解)

    在gateway启动时会加载一些路由断言工厂(判断一句话是否正确 一个boolean表达式)
    在这里插入图片描述
    具体可以在官网查看添加链接描述
    注:不能作用在动态路由上

    Filter过滤器工厂(重点)

    介绍

    gateway 里面的过滤器和 Servlet 里面的过滤器,功能差不多,路由过滤器可以用于修改进入 Http 请求和返回 Http 响应
    在这里插入图片描述

    使用

    创建一个类,继承GlobalFilter,Ordered

    继承GlobalFilter可以重写他的filter方法,从exchange可以得到request、response,从而获得想要的数据,而Ordered是将过滤器排序的类,重写getOrder() 方法,返回参数越小,过滤器越先执行

    /**
     * 定义了一个过滤器
     */
    @Component
    public class MyGlobalFilter implements GlobalFilter , Ordered {
        /**
         * 这个就是过滤的方法
         * @param exchange
         * @param chain
         * @return
         */
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            //针对请求的过滤 拿到请求 header
            //ServerHttpRequest webflux里面 响应式里面的
            ServerHttpRequest request = exchange.getRequest();
            String path = request.getURI().getPath();
            System.out.println(path);
            String methodName = request.getMethod().name();
            System.out.println(methodName);
            String hostName = request.getRemoteAddress().getHostName();
            System.out.println(hostName);
    
            //响应相关的数据
            ServerHttpResponse response = exchange.getResponse();
    
            //放行,到下一个过滤器
            return chain.filter(exchange);
        }
    
        /**
         * 指定顺序的方法
         * 越小越先执行
         * @return
         */
        @Override
        public int getOrder() {
            return 0;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    测试:
    在这里插入图片描述
    在这里插入图片描述
    若不放行,代码:

    //响应相关的数据
    ServerHttpResponse response = exchange.getResponse();
    //一般通过json传递数据
    //{"code":"200","msh","ok"}
    //设置编码 响应头里设置
    response.getHeaders().set("content-type","application/json;charset=utf-8");
    //组装业务返回值
    HashMap<String, Object> map = new HashMap<>(4);
    map.put("code", HttpStatus.UNAUTHORIZED.value());
    map.put("msg","未授权");
    ObjectMapper objectMapper = new ObjectMapper();
    //把map转换成一个字节
    byte[] bytes = new byte[0];
    try {
        bytes = objectMapper.writeValueAsBytes(map);
    } catch (JsonProcessingException e) {
        e.printStackTrace();
    }
    //通过buffer工厂将字节数组包装成一个数据包
    DataBuffer wrap = response.bufferFactory().wrap(bytes);
    return response.writeWith(Mono.just(wrap));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    测试:
    在这里插入图片描述

    IP黑名单拦截

    校验ip一般有较大的并发量,一般不用mysql,这里采取将黑名单写入内存中

    /**
     * ip黑名单校验
     */
    public class IPFilter implements GlobalFilter, Ordered {
        public static final List<String> BLACK_LIST = Arrays.asList("192.1.1.1","193.1.1.1");
    
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            ServerHttpRequest request = exchange.getRequest();
            String ip = request.getHeaders().getHost().getHostString();
            if(! BLACK_LIST.contains(ip)){
                return chain.filter(exchange);
            }
            ServerHttpResponse response = exchange.getResponse();
            response.getHeaders().set("content-type","application/json;charset=utf-8");
            HashMap<String, Object> map = new HashMap<>(4);
            map.put("code", 488);
            map.put("msg","你是黑名单");
            ObjectMapper objectMapper = new ObjectMapper();
            byte[] bytes = new byte[0];
            try {
                bytes = objectMapper.writeValueAsBytes(map);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
            DataBuffer wrap = response.bufferFactory().wrap(bytes);
            return response.writeWith(Mono.just(wrap));
        }
    
        @Override
        public int getOrder() {
            return -1;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    测试:
    在这里插入图片描述

    token校验

    设计

    token校验的本质就是让服务知晓访问的客户端
    在这里插入图片描述

    1.完善login-service

    接着之前创建的login-service写代码
    添加lombok依赖

    <dependency>
        <groupId>org.projectlombokgroupId>
        <artifactId>lombokartifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4

    创建entity

    import lombok.AllArgsConstructor;
    import lombok.Builder;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    @AllArgsConstructor
    @NoArgsConstructor
    @Data
    @Builder
    public class User {
        private Integer id;
        private String name;
        private String pwd;
        private Integer age;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    引入redis依赖

    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-data-redisartifactId>
    dependency>
    <dependency>
        <groupId>org.springframework.datagroupId>
        <artifactId>spring-data-redisartifactId>
        <version>2.3.9.RELEASEversion>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    接下来写controller,假设已经从数据库中查出数据

    @RestController
    public class LoginController {
        @Autowired
        public StringRedisTemplate redisTemplate;
    
        @GetMapping("doLogin")
        public String doLogin(String name,String pwd){
            System.out.println(name);
            System.out.println(pwd);
            //假设这里做了登录
            User user = new User(1,name,pwd,20);
            //token
            String token = UUID.randomUUID().toString();
            //存起来
            redisTemplate.opsForValue().set(token,user.toString(), Duration.ofSeconds(7200));
            return token;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    启动服务,接着在gateway-server中引入redis依赖

    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-data-redisartifactId>
    dependency>
    <dependency>
        <groupId>org.springframework.datagroupId>
        <artifactId>spring-data-redisartifactId>
        <version>2.3.9.RELEASEversion>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    写一个过滤器,为TokenCheckFilter

    /**
     * token校验
     */
    @Component
    public class TokenCheckFilter implements GlobalFilter, Ordered {
        public static final List<String> ALLOW_URL = Arrays.asList("/02-login-service/doLogin","/myUrl","/doLogin");
        @Autowired
        public StringRedisTemplate redisTemplate;
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            /**
             * 前提是和前端约定好放在哪,一般放在请求头里面(key Authorization,value bearer token)
             * 1.拿到请求url
             * 2.判断放行
             * 3.拿到请求头
             * 4.拿到token
             * 5.校验
             * 6.放行/拦截
             */
            ServerHttpRequest request = exchange.getRequest();
            String path = request.getURI().getPath();
            if(ALLOW_URL.contains(path)){
                return chain.filter(exchange);
            }
            //检查
            HttpHeaders headers = request.getHeaders();
            List<String> authorization = headers.get("Authorization");
            if(!CollectionUtils.isEmpty(authorization)){
                String token = authorization.get(0);
                if (StringUtils.hasText(token)){
                    //约定好有前缀的bearer token
                    String realtoken = token.replace("Bearer ", "");
                    if(StringUtils.hasText(realtoken)&&redisTemplate.hasKey(realtoken)){
                        return chain.filter(exchange);
                    }
                }
            }
            //拦截
            ServerHttpResponse response = exchange.getResponse();
            response.getHeaders().set("content-type","application/json;charset=utf-8");
            HashMap<String,Object> map = new HashMap<>(4);
            //返回401
            map.put("code", HttpStatus.UNAUTHORIZED.value());
            map.put("msg","未授权");
            ObjectMapper objectMapper = new ObjectMapper();
            byte[] bytes = new byte[0];
            try {
                bytes = objectMapper.writeValueAsBytes(map);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
            DataBuffer wrap = response.bufferFactory().wrap(bytes);
    
            return response.writeWith(Mono.just(wrap));
        }
    
        @Override
        public int getOrder() {
            return 0;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    开启网关启动类

    测试

    使用Postman进行测试
    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述

  • 相关阅读:
    Mysql - InnoDB引擎
    探花交友_第5章_圈子、小视频功能实现
    缓存使用常见思路及问题
    微服务统一认证方案
    【C++】C++11—— 包装器
    LeetCode 刷题系列 -- 1425. 带限制的子序列和
    MySQL索引与事务
    文举论金:黄金原油全面走势分析策略指导。
    Interest basics(每天进步一点点)
    业界主流数据加速技术路线
  • 原文地址:https://blog.csdn.net/YiRenGengShangBuQi/article/details/126554888