网关是一个服务:
Spring Cloud GateWay是Spring Cloud的⼀个全新项⽬,⽬标是取代Netflix Zuul,基于Spring5.0+SpringBoot2.0+WebFlux(基于⾼性能的Reactor模式响应式通信框架Netty,异步⾮阻塞模型)等技术开发,性能⾼于Zuul,官⽅测试,GateWay是Zuul的1.6倍,旨在为微服务架构提供⼀种简单有效的统⼀的API路由管理⽅式。
1.引入包(gateway)
- <!-- 网关 -->
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-gateway</artifactId>
- </dependency>
- <!-- 负载均衡 -->
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-loadbalancer</artifactId>
- </dependency>
2. 配置nacos进行服务注册和发现
描述:使用nacos+gateway实现各模块服务的负载均衡
nacos的配置使用可以参考文章:
SpringCloud:注册中心nacos_Cx_轩的博客-CSDN博客
3..yml配置
- spring:
- application:
- name: Cx-gateway
- cloud:
- gateway:
- httpclient:
- connect-timeout: 90000 #连接超时 毫秒
- response-timeout: 90s #应答超时 java.time.Duration http状态码504
- discovery:
- locator:
- enabled: false # 启用探测器 默认false,开启后可以通过ip:port/服务名称/接口地址进行服务转发
- lower-case-service-id: true # 路由名小写
- routes:
- - id: Cx-admin # admin后台
- uri: lb://Cx-admin
- predicates:
- - Path=/admin/**
- filters:
- - StripPrefix=1 # 过滤前缀 admin
- - id: Cx-es # es服务
- uri: lb://Cx-es
- predicates:
- - Path=/es/**
- filters:
- - StripPrefix=1 # 过滤前缀 es
此处对predicates断言进行使用描述:(常用的几种列举出来方便大家参考)
- 1.Path路径断言
- predicates:
-
- -Path=/xx # 拦截此路径下的所有请求发送到网关配置的uri (以一个模块一个路径去区分)
- 2.Query断言
- predicates:
-
- -Query=name,age. #参数值可以写正则,也可以只写参数名
- 3.Method断言
- predicates:
-
- -Method=get
- 4.Host断言
- predicates:
-
- -Host=xxxx.com
- 5.Cookie断言
- predicates:
-
- -Cookie=user,Cx #cookie断言和上面介绍的几种断言方式都大同小异,唯一不同的是他必须连同属性值一起验证,不能单独只验证属性是否存在
- 6.Header断言
- predicates:
-
- -Header=id,1234\d+ # 正则表达式\d+ 数字,header断言是检查头信息里是否携带了相关属性或令牌
- 7.时间路由
- predicates:
- - After=2022-02-07T17:05:00.789+08:00[Asia/Shanghai] # 时间匹配有三种模式,分别是Before、After和Between,指定了在什么时间范围内路由才会生效
1.使用jwt生成无状态token令牌,然后在网关项目中进行auth验证。
2.创建网关全局拦截
- @Configuration
- @Component
- @Slf4j
- public class AuthFilter implements GlobalFilter,Ordered {
-
- private static final String AUTHORIZE_TOKEN = "token";
-
- private static AntPathMatcher matcher = new AntPathMatcher();
-
- @Override
- public int getOrder() {
- return 0;
- }
-
- @Override
- public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
- ServerHttpRequest request = exchange.getRequest();
- String url = request.getPath().toString();
- log.info("【登录拦截】获取请求地址路径:"+url);
- // 不需要拦截的url直接通过
- if(isNotAuth(url)){
- return chain.filter(exchange);
- }
- String token = request.getHeaders().getFirst(AUTHORIZE_TOKEN);
- if(StrUtil.isBlank(token)){
- token = request.getQueryParams().getFirst(AUTHORIZE_TOKEN);
- }
- if(StrUtil.isBlank(token)){
- log.info("【登录拦截】未获取到token值...");
- loginResponse(exchange);
- }
- log.debug("【登录拦截】获取token值:"+token);
- try {
- SSOToken ssoToken = SSOToken.parser(token);
- // 获取token中的用户名
- String userId = ssoToken.getId();
- log.info("【登录拦截】获取userId:"+userId);
- } catch (Exception e) {
- log.error("【登录拦截】异常:"+e.getMessage());
- return loginResponse(exchange);
- }
- return chain.filter(exchange);
- }
-
- public static Mono<Void> loginResponse(ServerWebExchange exchange) {
- JSONObject json = JSONUtil.createObj();
- json.set("code", 401);
- json.set("msg", "请重新登陆授权");
- ServerHttpResponse response = exchange.getResponse();
- byte[] bytes = JSONUtil.toJsonStr(json).getBytes(StandardCharsets.UTF_8);
- //指定编码,否则在浏览器中会中文乱码
- response.getHeaders().add("Content-Type", "text/plain;charset=UTF-8");
- DataBuffer buffer = response.bufferFactory().wrap(bytes);
- return response.writeWith(Flux.just(buffer));
- }
-
- private boolean isNotAuth(String url) {
- List<String> uriList = new ArrayList<>();
- // admin后台不需要拦截
- uriList.add("/admin/**");
- uriList.add("/sso/login");
-
- for (String pattern : uriList) {
- if (matcher.match(pattern, url)) {
- // 不需要拦截
- return true;
- }
- }
- return false;
- }
- }
上述代码中SSOToken就是jwt的封装,大家可以直接使用jwt进行解析即可。
注:此处使用jwt无状态token令牌,为了方便不同模块、不同前端项目针对不同ip和端口部署的分布式应用时在进行用户验权时可以不需要考虑会话session和cookie只需要将token存储在浏览器中即可全局使用。在访问时只校验token的有效性。所以在使用jwt的token时可以降低token的有效时长在增加安全性。