spring cloud gateway 使用

Spring Cloud Gateway 具有如下特性:
基于Spring Framework 5, Project Reactor 和 Spring Boot 2.0 进行构建;
动态路由:能够匹配任何请求属性; 可以对路由指定 Predicate(断言)和 Filter(过滤器);
集成Hystrix的断路器功能;
集成 Spring Cloud 服务发现功能;
易于编写的 Predicate(断言)和 Filter(过滤器);
请求限流功能;
支持路径重写。

项目搭建
1.引入jar包


     <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <!-- Maven整个生命周期内排除内置容器,排除内置容器导出成war包可以让外部容器运行spring-boot项目-->
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

2.修改配置文件application.yml 注:使用时请删除注释

#端口号
server:
  port: 5055
spring:
  application:
  #项目名称
    name: jq-gateway
  cloud:
    gateway:
      discovery:
        locator:
          #开启从注册中心动态创建路由的功能
          enabled: true
          #使用小写服务名,默认是大写
          lower-case-service-id: true
#连接redis
  redis:
    host: localhost
    port: 6379
    timeout: 10000
    database: 0
    lettuce:
      pool:
        max-active: 8
        max-wait: -1
        max-idle: 8
        min-idle: 0
#连接eureka
eureka:
  client:
    register-with-eureka: true
    service-url:
      defaultZone: http://localhost:5051/eureka
    registry-fetch-interval-seconds: 30
    fetch-registry: true
  instance:
    lease-expiration-duration-in-seconds: 90
    lease-renewal-interval-in-seconds: 30

#需要释放的请求
filter:
  ignore:
    urls:
      - /jq-member/**
      - /fh-product/**
      - /jq-search/**
   

创建FiterIgnoreUrl类

package com.jq.filter;

import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.List;

@Data
@EqualsAndHashCode(callSuper = false)
@Component//交给spring管理
@ConfigurationProperties(prefix = "filter.ignore")//获取yml配置文件中的数据
public class FiterIgnoreUrl {

    private List<String> urls;

}

创建MyGatewayFilter类

package com.jq.filter;

import cn.hutool.json.JSONUtil;
import com.jq.jwt.JWTUtil;
import com.jq.msg.RedisKeyUtils;
import com.jq.result.ResultCode;
import com.jq.result.ResultObj;
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.data.redis.core.RedisTemplate;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;

@Component
public class MyGatewayFilter implements GlobalFilter, Ordered {

    @Autowired
    private FiterIgnoreUrl fiterIgnoreUrl;

    @Autowired
    private JWTUtil jwtUtil;

    @Autowired
    private RedisTemplate redisTemplate;


    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        AntPathMatcher antPathMatcher = new AntPathMatcher();
        //判断哪些请求需要拦截,哪些需要释放
        String path = request.getURI().getPath();
        for (String url:fiterIgnoreUrl.getUrls()){
            if(antPathMatcher.match(url,path)){
                System.out.println("请求为:"+path+"放过拦截");
                return  chain.filter(exchange);
            }
        }

        String token = request.getHeaders().getFirst("Authorization-token");
        if (StringUtils.isEmpty(token)){
            //登录失败重新登录
            return responseData(exchange, ResultObj.error(ResultCode.LOGIN_ERROR));
        }
        //验证token
        ResultObj resultObj = jwtUtil.verifyToken(token);
        if(resultObj.getCode()!=200) {
            //登录失败
            return responseData(exchange,resultObj);
        }

        //token续签
        String phone = jwtUtil.getPhone(token);
        Boolean hasKey = redisTemplate.hasKey(RedisKeyUtils.getTokenKey(phone));
        if (hasKey==false){
            //登录失败重新登录
            return responseData(exchange,ResultObj.error(ResultCode.LOGIN_ERROR));
        }
        redisTemplate.expire(ResultObj.error(ResultCode.LOGIN_ERROR),3, TimeUnit.HOURS);

        //往redis中存放唯一值
        request.mutate().headers(httpHeaders -> {
            httpHeaders.add("phone",phone);
        });

        return chain.filter(exchange);
    }



    private Mono responseData(ServerWebExchange exchange, ResultObj resultObj){
        ServerHttpResponse response=exchange.getResponse();
        String data= JSONUtil.toJsonStr(resultObj);
        DataBuffer dataBuffer=response.bufferFactory().allocateBuffer()
                .write(data.getBytes(StandardCharsets.UTF_8));
        return response.writeWith(Mono.just(dataBuffer));
    }


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

创建JWTUtil工具类


package com.jq.jwt;

import com.jq.result.ResultCode;
import com.jq.result.ResultObj;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Component
@PropertySource({"classpath:jwt.properties"})
public class JWTUtil {

    @Value("{user.jwtset.type}")
    private String type;

    @Value("{user.jwtset.alg}")
    private String alg;

    @Value("{user.jwtset.signing}")
    private String signing;




    public  String createToken(String  phone){
        // token由三部分组成:头部、有效负载,签名
        // 1.头部信息
        Map<String,Object> headerMap = new HashMap<String,Object>();
        headerMap.put("type",type);
        headerMap.put("alg",alg);

        // 2.有效负载
        Map<String,Object> payload = new HashMap<>();
        payload.put("phone",phone);
        payload.put("date",new Date());

        // 有效时间
        long timeMillis = System.currentTimeMillis();
        long endTime = timeMillis+6000000;

        // 3.签名
        String token = Jwts.builder().setHeader(headerMap)
                .setClaims(payload)
                .setExpiration(new Date(endTime))
                .signWith(SignatureAlgorithm.HS256, "signing")
                .compact();
        return token;
    }


    public  ResultObj verifyToken(String token) {
        try {
            Claims claims = Jwts.parser().setSigningKey("signing")
                    .parseClaimsJws(token)
                    .getBody();
            return ResultObj.success(claims);
        } catch (Exception e) {
            return ResultObj.error(ResultCode.TOKEN_ERROR);
        }
    }

    public  String getPhone(String token){
        try {
            Claims claims = Jwts.parser().setSigningKey("signing")
                    .parseClaimsJws(token)
                    .getBody();
            return (String) claims.get("phone");
        } catch (Exception e) {
            return null;
        }
    }
}


在这里插入图片描述
jwt.properties文件代码:

user.jwtset.type=JWT
user.jwtset.alg=HS256
user.jwtset.signing=jq1223

启动类代码:

@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class})
@EnableEurekaClient
public class JqGatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(JqGatewayApplication.class, args);
    }
}

创建Code码枚举类

package com.jq.result;


public enum ResultCode {

    SUCCESS(200,"操作成功"),
    ERROR(500,"操作失败"),
    USERNAME_PASSWORD_ISNULL(1001,"用户名或密码为空"),
    USER_NOEXIST(1002,"用户不存在"),
    PASSWORD_ERROR(1003,"密码错误"),
    TOKEN_ERROR(1004,"身份过期,请重新登录"),
    NO_PERMISSION(403,"没有权限访问该方法"),
    PHONE_NULL(1005,"手机号为空"),
    GET_CODE_ERROR(1006,"获取验证码失败"),
    PHONE_CODE_ISBULL(1007,"手机号码或验证码不能为空"),
    CODE_ERROR(1008,"验证码错误"),
    PHONE_CODE_TIMEOUT(1009,"手机号码不正确或验证码过期"),
    LOGIN_ERROR(1010,"登录失败请重新登录")
    ;

    private Integer code;
    private String msg;

    ResultCode(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}

创建统一返回值类:

package com.jq.result;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ResultObj {

    // 状态码
    private Integer code;

    // 信息
    private String msg;

    // 数据
    private Object data;

    public ResultObj(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public static ResultObj success(){
        return new ResultObj(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg());
    }

    public static ResultObj success(Object data){
        return new ResultObj(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg(), data);
    }

    public static ResultObj error(ResultCode resultCode){
        return new ResultObj(resultCode.getCode(), resultCode.getMsg(),null);
    }


}

创建RedisKeyUtils类:

private static final String codeKey = "code:";
    private static final String tokenKey = "token:";

    public static String getCodeKey(String phone){
        return codeKey + phone;
    }
    public static String getTokenKey(String phone){
        return tokenKey + phone;
    }

vue前端
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jq1223

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值