ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Spring cloud alibaba 组件gateway网关、配置管理 以及链路追踪、Jwt鉴权过滤

2022-07-10 23:35:09  阅读:255  来源: 互联网

标签:网关 service Spring 配置管理 springframework micro import org gateway


Spring cloud alibaba

一. Spring cloud Gateway网关

什么是网关?就是网络请求的统一入口.

为什么需要网关?

1.如果我们的有成千上万个服务,我们在请求每个服务的时候都需要进行认证,难度与工作量可想而知,要控制用户对于整个服务的访问次数的限制。

2.如果没有统一的入口,那么前端在与服务端交互的时候定位到各个服务,假设服务器端作服务的重构,那么前端也得跟着一起修改。

gateway是spring cloud的第二代网关,其性能是zuul的1.6倍左右,其内部是基于netty、reactor(多路复用)、webflux进行构建,性能强大。gateway需要从注册中心获取服务,然后通过网关来调用对应的服务。但是gateway不在web环境下运行,也就是说不能打成war包放在tomcat下运行

1.1 快速入门

1.创建springcloudalibaba-micro-service-gateway-9090子工程,导入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>springcloudalibaba-micro-service-manager</artifactId>
        <groupId>com.qf</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>springcloudalibaba-micro-service-gateway-9090</artifactId>

    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>

            <exclusions>
                <!-- 排除掉springmvc相关的配置信息 -->
                <exclusion>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-webmvc</artifactId>
                </exclusion>
                <!-- 排除掉tomcat相关的配置 -->
                <exclusion>
                    <groupId>org.springframework.bootk</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.apache.tomcat.embed</groupId>
                    <artifactId>tomcat-embed-core</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.apache.tomcat.embed</groupId>
                    <artifactId>tomcat-embed-el</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.apache.tomcat.embed</groupId>
                    <artifactId>tomcat-embed-websocket</artifactId>
                </exclusion>
            </exclusions>

        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
            <version>2.2.6.RELEASE</version>
        </dependency>

    </dependencies>

</project>

2.创建 GateWayApplication 启动类

package com.qf;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MicroServiceGateWay {
    public static void main(String[] args) {

        SpringApplication.run(MicroServiceGateWay.class,args);
    }
}

3.创建application.yml文件

spring:
  application:
    name: micro-service-gateway # 网关名称

  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        enabled: true
        register-enabled: false # 不注册到nacos上

    gateway:
      discovery:
        locator:
          enabled: true #开启网关端口并使用服务方的名称来访问
          #之前:http://localhost:8080/feign/getById?id=1002
          #现在:http://localhost:9090/micro-service-consumer/feign/getById?id=1002
      
server:
  port: 9090

4.启动服务提供方,消费方以及网关对应工程,进行测试

1.2 谓词配置

谓词(predicate)是gateway内置的的一下关于请求相关的处理,在application.yml中增加routes的配置

注意:谓词配置后,访问时就不需要再写服务名称了,因为谓词里面已经配置过了

配置谓词之前:http://localhost:9090/micro-service-consumer/feign/getUsers

配置谓词之后:http://localhost:9090/feign/getUsers

spring:
  application:
    name: micro-service-gateway # 网关

  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        enabled: true
        register-enabled: false # 不注册到nacos上

    gateway:
      discovery:
        locator:
          enabled: true #开启之后通过网关访问:http://localhost:9090/micro-service-consumer/feign/getUsers
      routes:
        # id可以不是服务名,名字任意,但是不能重复,推荐使用服务名的方式
        - id: micro-service-consumer
          # uri才是控制着某个具体的访问到达我们特定的服务
          uri: lb://micro-service-consumer
          # 谓词: 就是满足的条件,可以在org.springframework.cloud.gateway.handler.predicate这个包下
          predicates:
            # 配置访问消费方controller的一级目录名称,这样就可以通过http://localhost:9090/feign/getUsers来访问了
            - Path=/feign/**
            # 请求的参数中必须携带origin这个参数名,参数值符合[a-zA-Z]+ 这个正则
            - Query=origin,[a-zA-Z]+
            # 请求的方式
            - Method=get,post
            # 设置时间区间内访问: 2020年12月31日 - 2030年12月31日,可以访问,+08:00表示时区
            - After=2020-12-31T00:00:00+08:00[Asia/Shanghai]
            - Before=2030-12-31T00:00:00+08:00[Asia/Shanghai]
            # 描述IP在10.8.13.1~10.8.13.255之间的地址才可以访问
            - RemoteAddr=10.8.13.0/24
            # 请求的头中必须得携带token, value值符合[a-zA-Z0-9]+ 这个正则
            - Header=token,[a-zA-Z0-9]+

server:
  port: 9090

1.3 过滤器配置

GateWay提供了很多内置的过滤器让我们使用,具体的过滤器在spring-cloud-gateway-core-2.1.2.RELEASE.jar下的org.springframework.cloud.gateway.filter.factory包下,接下来我们挑其中一个非常常用的过滤来讲解用法,在实际的开发过程中,有这样一种业务需求,就是限制同一个IP对服务器频繁的请求,例如我们限制每个IP在每秒只能访问3次,那么要怎么实现呢?其实spring-boot已经帮我们实现好了一个,只需要做一定的配置就可以了。

IP限制的原理就是令牌桶算法,随着时间流逝,系统会按恒定 1/QPS 时间间隔(如果 QPS=100,则间隔是 10ms)往桶里加入 Token,如果桶已经满了就不再加了。新请求来临时,会各自拿走一个 Token,如果没有 Token 可拿了就阻塞或者拒绝服务。如下图所示:

1.在springcloudalibaba-micro-service-gateway-9090工程中添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2.创建RedisHostKeyResovler类,实现获取IP的组件

package com.qf.resolver;

import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
public class RedisHostKeyResolver implements KeyResolver {

    /**
     * webflux:
     *      Mono: 用于返回单个值
     *      Flux: 用于返回集合数据
     */
    @Override
    public Mono<String> resolve(ServerWebExchange exchange) {
        //获取用户的访问的 ip
        String host = exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();
        return Mono.just(host); //构建 Mono<String>
    }
}

3.在application.yml中增加filters和redis的配置

server:
  port: 9090

spring:
  application:
    name: micro-service-gateway #服务名称,在nacos上显示

  cloud:
    nacos:
      discovery:
        enabled: true #注册服务
        server-addr: 127.0.0.1:8848 #nacos访问地址
        register-enabled: false #不注册到nacos
    gateway:
      discovery:
        locator:
          enabled: true #开启网关访问
          #访问方式:
                  #原来:http://localhost:8080/feign-user-consumer/findById?id=1&origin=123
           #重点掌握现在:http://localhost:9090/micro-service-consumer/feign-user-consumer/findById?id=1&origin=123
           #一般不这样用:http://localhost:9090/micro-service-provider/user-provider/findById?id=1
      routes:
      # id可以不是服务名,名字任意,但是不能重复,推荐使用服务名的方式
      - id: micro-service-consumer
        # uri才是控制着某个具体的访问到达我们特定的服务
        uri: lb://micro-service-consumer
        # 谓词: 就是满足的条件,可以在org.springframework.cloud.gateway.handler.predicate这个包下
        predicates:
        # 配置访问消费方controller的一级目录名称,这样就可以通过http://localhost:9090/feign-user-consumer/getUsers来访问了
        - Path=/feign-user-consumer/**
        # 请求的参数中必须携带origin这个参数名,参数值符合[a-zA-Z]+ 这个正则
        - Query=origin,[a-zA-Z]+
        # 请求的方式
        - Method=get,post
        # 设置时间区间内访问: 2020年12月31日 - 2030年12月31日,可以访问,+08:00表示时区
        - After=2020-12-31T00:00:00+08:00[Asia/Shanghai]
        - Before=2030-12-31T00:00:00+08:00[Asia/Shanghai]
        # 描述IP在10.8.13.1~10.8.13.255之间的地址才可以访问
        #- RemoteAddr=10.8.162.0/24
        # 请求的头中必须得携带token, value值符合[a-zA-Z0-9]+ 这个正则
        #- Header=token,[a-zA-Z0-9]+

        filters:
          # RequestRateLimiter是固定值
          - name: RequestRateLimiter
            args:
              # key-resolver是用于限流的bean对象,通过SpEL的方式 #{@XXX} 取出spring容器中的bean
              keyResolver: '#{@redisHostKeyResolver}'
              # 每秒往令牌桶中存放的数量
              redis-rate-limiter.replenishRate: 1
              # 令牌桶中最多的令牌的数量
              redis-rate-limiter.burstCapacity: 3

  redis:
    host: 127.0.0.1
    port: 6379

4.可以让局域网内的其他用户访问本机:http://本机ip:9090/feign/getUsers?origin=abc

1.4 自定义全局过滤器

1.导入依赖

<dependency>
    <groupId>com.qf</groupId>
    <artifactId>springcloudalibaba-micro-service-commons</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.79</version>
</dependency>

2.创建全局过滤器类

package com.qf.filters;

import com.alibaba.fastjson2.JSON;
import com.qf.utils.JsonResult;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.List;

//创建多个全局过滤器
@Configuration
public class FilterConfig {

    @Bean
    @Order(-100)//正数值越小,负数的绝对值越大,优先级越高
    public GlobalFilter loginFilter(){
        return new GlobalFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                System.out.println("-100");

                //获取传入的用户名和密码
                ServerHttpRequest request = exchange.getRequest();
                //获取所有参数
                MultiValueMap<String, String> queryParams = request.getQueryParams();
                //获取前端传过来的name以及password
                List<String> nameList = queryParams.get("name");
                List<String> passwordList = queryParams.get("password");
                //判断
                if(nameList !=null && nameList.size() > 0 && passwordList !=null && passwordList.size() > 0){
                    //模拟数据库查询
                    if("jack".equals(nameList.get(0)) && "123".equals(passwordList.get(0))){
                        //放行
                        return chain.filter(exchange);
                    }else{
                        //参数输入错误
                        JsonResult jsonResult = JsonResult.fail();
                        jsonResult.setData("name or password is error");

                        //返回
                        ServerHttpResponse response = exchange.getResponse();
                        String jsonString = JSON.toJSONString(jsonResult);
                        DataBuffer dataBuffer = response.bufferFactory().wrap(jsonString.getBytes(StandardCharsets.UTF_8));
                        return response.writeWith(Mono.just(dataBuffer));
                    }

                }else{
                    //参数输入错误
                    JsonResult jsonResult = JsonResult.fail();
                    jsonResult.setData("name or password is null");
                    //返回
                    ServerHttpResponse response = exchange.getResponse();
                    String jsonString = JSON.toJSONString(jsonResult);
                    DataBuffer dataBuffer = response.bufferFactory().wrap(jsonString.getBytes(StandardCharsets.UTF_8));
                    return response.writeWith(Mono.just(dataBuffer));
                }

            }
        };
    }

    @Bean
    @Order(-90)//正数值越小,负数的绝对值越大,优先级越高
    public GlobalFilter otherFilter(){
        return new GlobalFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                System.out.println("-90");
                return chain.filter(exchange);
            }
        };
    }

}

3.浏览器访问:http://localhost:9090/feign/getUsers?origin=qwer&username=jack&password=123 进行测试

二. nacos配置管理

配置管理,就是将所有的微服务的配置统一进行管理,这样做的好处是我们的配置不用写在项目中,实现集中化的管理,方便环境的变更。

2.1 基本配置

1.在springcloud-alibaba-microservice-gateway-9090(网关)和springcloudalibaba-micro-service-consumer-8080(服务消费方)工程中都导入依赖

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

2.手动在桌面创建文件夹,命名为:micro-service-gateway,然后把springcloud-alibaba-microservice-gateway-9090网关工程中的application.yml中的中文注释都删了再拷贝过去,修改名称为:micro-service-gateway-dev.yml,然后再复制一份,命名改为:micro-service-gateway-test.yml,修改micro-service-gateway-test.yml中的内容,使其和micro-service-gateway-dev.yml文件的内容略有不同即可,比如:修改micro-service-gateway-test.yml 端口号为9091。

同样类似的方式,对springcloudalibaba-micro-service-consumer-8080工程中的application.yml也执行一遍。

最后,分别对micro-service-gateway和micro-service-consumer两个文件夹打成zip压缩包文件,一会要导入到nacos中使用。

3.在springcloud-alibaba-microservice-gateway-9090(网关)和springcloudalibaba-micro-service-consumer-8080(服务消费方)工程中都配置bootstrap.yml,内容如下:(group和name对应的值最好和刚才桌面上创建文件夹的名称一致),然后把两个工程中原来的application.yml命名为application.yml.bak

spring:
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
        # 配置文件的后缀名
        file-extension: yml
        # 配置在nacos上的组名
        group: micro-service-gateway
  application:
    # 服务名称
    name: micro-service-gateway
  profiles:
    # 配置文件环境(生产环境,开发环境,测试环境等等,对应不同的application-*.yml文件)
    active: dev
spring:
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
        # 配置文件的后缀名
        file-extension: yml
        # 配置在nacos上的组名
        group: micro-service-consumer
  application:
    # 服务名称
    name: micro-service-consumer
  profiles:
    # 配置文件环境(生产环境,开发环境,测试环境等等,对应不同的application-*.yml文件)
    active: dev

4.在nacos中导入新建配置文件(zip压缩包形式),参考课件提供的配置文件

5.启动工程进行测试:

配置 active: dev 则显示nacos中对应 *-dev.yml配置文件中的内容;

配置 active: test则显示nacos中对应 *-test.yml配置文件中的内容;

2.2 配置的实时刷新

1.在Controller中添加方法并测试,修改springcloudalibaba-micro-consumer-8080中FeignUserController,在FeignUserController上添加实时刷新注解@RefreshScope

@RefreshScope//实时刷新
@RestController
@RequestMapping("feign")
public class UserFeignController {

    @Value("${user.username}")
    private String username;

    @RequestMapping("getUserName")
    public String getUserName(){
        return username;
    }

2.在nacos面板中修改对应加载的microservice-consumer-test.yml,设置user.username的值为张三

3.启动springcloudalibaba-micro-consumer-8080工程,访问getUsername方法测试即可

4.然后把user.username的值改为李四,再次访问测试即可

三. 链路追踪

微服务架构是一个分布式架构,它按业务划分服务单元,一个分布式系统往往有很多个服务单元。由于服务单元数量众多,业务的复杂性,如果出现了错误和异常,很难去定位。主要体现在,一个请求可能需要调用很多个服务,而内部服务的调用复杂性,决定了问题难以定位。所以微服务架构中,必须实现分布式链路追踪,去跟进一个请求到底有哪些服务参与,参与的顺序又是怎样的,从而达到每个请求的步骤清晰可见,出了问题,很快定位。

举个例子,在微服务系统中,一个来自用户的请求,请求先达到前端A(如前端界面),然后通过远程调用,达到系统的中间件B、C(如负载均衡、网关等),最后达到后端服务D、E,后端经过一系列的业务逻辑计算最后将数据返回给用户。对于这样一个请求,经历了这么多个服务,怎么样将它的请求过程的数据记录下来呢?这就需要用到服务链路追踪。

在本章中我们主要会讲解spring cloud sleuth整合Zipkin来实现链路的追踪,整合起来非常的简单,只需要引入相关的依赖,再加上相关的配置即可。

3.1 Sleuth基本术语

Sleuth负责记录微服务数据,Zipkin是Twitter开源的分布式跟踪系统,主要用来收集系统的时序数据,从而跟踪系统的调用问题。

span: 基本工作单元,发送一个远程调度任务 就会产生一个Span,Span是一个64位ID唯一标识的,Trace是用另一个64位ID唯一标识的,Span还有其他数据信息,比如摘要、时间戳事件、Span的ID、以及进度ID。

**Trace: ** 一系列Span组成的一个树状结构。请求一个微服务系统的API接口,这个API接口,需要调用多个微服务,调用每个微服务都会产生一个新的Span,所有由这个请求产生的Span组成了这个Trace。

**Annotation: **用来及时记录一个事件的,一些核心注解用来定义一个请求的开始和结束 。这些注解包括以下:

  • cs - Client Sent -客户端发送一个请求,这个注解描述了这个Span的开始
  • sr - Server Received -服务端获得请求并准备开始处理它,如果将其sr减去cs时间戳便可得到网络传输的时间。
  • ss - Server Sent (服务端发送响应)–该注解表明请求处理的完成(当请求返回客户端),如果ss的时间戳减去sr时间戳,就可以得到服务器请求的时间。
  • cr - Client Received (客户端接收响应)-此时Span的结束,如果cr的时间戳减去cs时间戳便可以得到整个请求所消耗的时间。

3.2 链路追踪的搭建

Zipkin Server的下载地址:https://github.com/openzipkin/zipkin

1.启动Zipkin Server,命令如下:java -jar zipkin-server-2.19.1-exec.jar,访问:http://localhost:9411/

2.在需要追踪的服务中添加依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zipkin</artifactId>
    <version>2.2.6.RELEASE</version>
</dependency>

3.在nacos面板中分别修改micro-service-consumer-dev.yml和micro-service-gateway-dev.yml中配置如下内容,并分别在各自工程中的bootstrap.yml中指定调用该dev配置文件

spring:
  zipkin:
    base-url: http://localhost:9411/
    discovery-client-enabled: false
  sleuth:
    sampler:
    # 抽样率 100%, 默认10%, 如果服务流量较大,全部采集对存储造成的压力也会很大
    probability: 0.2

D. 查看结果

四.Jwt鉴权过滤

JJWT的是在JVM上创建和验证JSON Web Token(JWTs)的库,基于JWT、JWS、JWE、JWK和JWA RFC规范的Java实现。

4.1 引入工具类

4.1.1在springcloudalibaba-micro-service-common中导入依赖
<!-- jjwt -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.11.2</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.11.2</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.11.2</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.7.0</version>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-text</artifactId>
    <version>1.8</version>
</dependency>
4.1.2在springcloudalibaba-micro-service-commons中创建JsonResult和JwtHelper工具类

JsonResult

package com.qf.utils;

import lombok.Data;

@Data
public class JsonResult<T> {

    private String msg;
    private Integer code;
    private T data;


    public static JsonResult ok(){
        JsonResult jsonResult = new JsonResult<>();
        jsonResult.setCode(200);
        jsonResult.setMsg("success");

        return jsonResult;
    }

    public static JsonResult fail(){
        JsonResult jsonResult = new JsonResult<>();
        jsonResult.setCode(-20000);
        jsonResult.setMsg("error");

        return jsonResult;
    }
}

JwtHelper

package utils;


import io.jsonwebtoken.*;
import org.apache.commons.lang3.time.DateUtils;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.util.Date;

//jwt工具类
public class JwtHelper {

    // 生成Jwt
    public static String jwsWithHS(SignatureAlgorithm signatureAlgorithm, String userInfo, int num, String secret) {
        Key key = new SecretKeySpec(secret.getBytes(), signatureAlgorithm.getJcaName());
        Claims claims = Jwts.claims();
        claims.setSubject(userInfo);  // jwt的信息的本身
        claims.setExpiration(DateUtils.addSeconds(new Date(), num)); //设置jwt多少秒过期
        String jws = Jwts.builder()
                .setClaims(claims).signWith(key, signatureAlgorithm).compact();
        return jws;
    }

    // 校验Jwt
    public static Jwt verifySign(String jws, String secret, SignatureAlgorithm signatureAlgorithm) {
        Key key = new SecretKeySpec(secret.getBytes(), signatureAlgorithm.getJcaName());
        Jwt jwt = Jwts.parserBuilder().setSigningKey(key).build().parse(jws);
        return jwt;
    }

    public static void main(String[] args) {
        //设置,算法:HS256,用户信息:jack,过期时间:10分钟,密码:202cb962ac59075b964b07152d234b70
        //System.out.println(jwsWithHS(SignatureAlgorithm.HS256, "jack", 600, "202cb962ac59075b964b07152d234b70"));

        //校验jwt
        String jwtstring = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJqYWNrIiwiZXhwIjoxNjQwOTMzMTk4fQ.igm6YSD1nBO4hT4rywa5ZPB3FXvD9gcYHEWqL8Rvk18";

        Jwt jwt = verifySign(jwtstring, "202cb962ac59075b964b07152d234b70", SignatureAlgorithm.HS256);
        Claims claims = (Claims)jwt.getBody();
        System.out.println(claims);
        //获取用户信息
        System.out.println(claims.get("sub"));

    }
}

4.2创建认证模块

4.2.1创建springcloudalibaba-micro-service-authentic-6060子模块,导入依赖
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- nacos -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <dependency>
            <groupId>com.qf</groupId>
            <artifactId>springcloudalibaba-micro-service-commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <!-- 均衡负载接口调用Feign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
            <version>2.2.6.RELEASE</version>
        </dependency>

        <!-- 熔断降级sentinel -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

        <!-- nacos配置 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>

        <!-- 链路追踪 -->
<!--                <dependency>-->
<!--                    <groupId>org.springframework.cloud</groupId>-->
<!--                    <artifactId>spring-cloud-starter-zipkin</artifactId>-->
<!--                    <version>2.2.6.RELEASE</version>-->
<!--                </dependency>-->

    </dependencies>
4.2.2配置application.yml
server:
  port: 6060

spring:
  application:
    name: micro-service-authentic #服务名称,在nacos上显示

  cloud:
    nacos:
      discovery:
        enabled: true #注册服务
        server-addr: 127.0.0.1:8848 #nacos访问地址

    sentinel:
      transport:
        port: 8719 #内部传输的端口
        dashboard: localhost:8888 #sentinel控制面板访问端口
      web-context-unify: false #false表示针对调用同一接口的不同url进行链路限制

#feign配合sentinel使用
feign:
  sentinel:
    enabled: true

#配置签名和算法
jwt:
  secret: 21232f297a57a5a743894a0e4a801fc3
  signature-algorithm: HS256
4.2.3编写启动类
package com.qf;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableDiscoveryClient//注册服务
@EnableFeignClients//feign
public class MicroServiceAuthenticApplication {

    public static void main(String[] args) {
        SpringApplication.run(MicroServiceAuthenticApplication.class,args);
    }
}
4.2.4编写Controller
package com.qf.controller;

import com.qf.utils.JsonResult;
import com.qf.utils.JwtHelper;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/authentic")
public class AuthenticController {

    @Value("${jwt.secret}")
    private String secret;

    @Value("${jwt.signature-algorithm}")
    private String algorithm;

    @RequestMapping("login")
    public JsonResult login(String usercode,String password){

        //判断传入的参数
        if((usercode != null && usercode.length() > 0) && (password != null && password.length() > 0) ){
            //模拟数据库进行比对
            if("jack".equals(usercode) && "123".equals(password)){
                //登录成功,生成jwt
                String jwt = JwtHelper.jwsWithHS(SignatureAlgorithm.forName(algorithm), usercode, 600, secret);
                //返回
                JsonResult jsonResult = JsonResult.ok();
                jsonResult.setData(jwt);

                return jsonResult;
            }else {
                JsonResult jsonResult = JsonResult.fail();
                jsonResult.setData("login is fail");
                return jsonResult;
            }

        }else{
            JsonResult jsonResult = JsonResult.fail();
            jsonResult.setData("usercode or password is null");
            return jsonResult;
        }

    }
}

4.3 配置网关

4.3.1在springcloudalibaba-micro-service-gateway-9090导入公共模块依赖
<dependency>
    <groupId>com.qf</groupId>
    <artifactId>springcloudalibaba-micro-service-commons</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>
4.3.2在springcloudalibaba-micro-service-gateway-9090的application.yml中配置谓词和jwt
server:
  port: 9090

spring:
  application:
    name: micro-service-gateway #服务名称,在nacos上显示

  cloud:
    nacos:
      discovery:
        enabled: true #注册服务
        server-addr: 127.0.0.1:8848 #nacos访问地址
        register-enabled: false #不注册到nacos
    gateway:
      discovery:
        locator:
          enabled: true #开启网关访问
          #访问方式:
               #原来: http://localhost:8080/feign/findById?id=1&origin=123
          #重点掌握现在:http://localhost:9090/micro-service-consumer/feign/findById?id=1&origin=123
 
      routes:
      # 配置认证服务
      - id: micro-service-authentic
        uri: lb://micro-service-authentic
        predicates:
        - Path=/authentic/**
      # id可以不是服务名,名字任意,但是不能重复,推荐使用服务名的方式
      - id: micro-service-consumer
        # uri才是控制着某个具体的访问到达我们特定的服务
        uri: lb://micro-service-consumer
        # 谓词: 就是满足的条件,可以在org.springframework.cloud.gateway.handler.predicate这个包下
        predicates:
        # 配置访问消费方controller的一级目录名称,这样就可以通过http://localhost:9090/feign/getUsers来访问了
        - Path=/feign/**
        # 请求的参数中必须携带origin这个参数名,参数值符合[a-zA-Z]+ 这个正则
        - Query=origin,[a-zA-Z]+
        # 请求的方式
        - Method=get,post
        # 设置时间区间内访问: 2020年12月31日 - 2030年12月31日,可以访问,+08:00表示时区
        - After=2020-12-31T00:00:00+08:00[Asia/Shanghai]
        - Before=2030-12-31T00:00:00+08:00[Asia/Shanghai]
        # 描述IP在10.8.13.1~10.8.13.255之间的地址才可以访问
        #- RemoteAddr=10.8.162.0/24
        # 请求的头中必须得携带token, value值符合[a-zA-Z0-9]+ 这个正则
        #- Header=token,[a-zA-Z0-9]+

#        filters:
#          # RequestRateLimiter是固定值
#          - name: RequestRateLimiter
#            args:
#              # key-resolver是用于限流的bean对象,通过SpEL的方式 #{@XXX} 取出spring容器中的bean
#              keyResolver: '#{@redisHostKeyResolver}'
#              # 每秒往令牌桶中存放的数量
#              redis-rate-limiter.replenishRate: 1
#              # 令牌桶中最多的令牌的数量
#              redis-rate-limiter.burstCapacity: 3
#
#  redis:
#    host: 127.0.0.1
#    port: 6379

#配置签名和算法
jwt:
  secret: 21232f297a57a5a743894a0e4a801fc3
  signature-algorithm: HS256
4.3.3在springcloudalibaba-micro-service-gateway-9090创建AuthFilter过滤器
package com.qf.filters;

import com.alibaba.fastjson2.JSON;
import com.qf.utils.JsonResult;
import com.qf.utils.JwtHelper;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwt;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
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.server.RequestPath;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;

@Component
public class AuthFilter implements GlobalFilter, Ordered {

    @Value("${jwt.secret}")
    private String secret;

    @Value("${jwt.signature-algorithm}")
    private String algorithm;

    //校验token
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //请求路径
        String requestPath = exchange.getRequest().getPath().toString();
        System.out.println(requestPath);
        //判断,如果是登录,直接放行
        if(requestPath.contains("/authentic/login")){
            return chain.filter(exchange);
        }
        
        //访问的是其他服务
        //获取token进行校验
        String token = exchange.getRequest().getHeaders().getFirst("token");

        //判断
        if(token == null){
            
            JsonResult jsonResult = JsonResult.fail();
            jsonResult.setData("token is null");

            //返回
            ServerHttpResponse response = exchange.getResponse();
            String jsonString = JSON.toJSONString(jsonResult);
            DataBuffer dataBuffer = response.bufferFactory().wrap(jsonString.getBytes(StandardCharsets.UTF_8));
            return response.writeWith(Mono.just(dataBuffer));
        }else{

            try {

                //校验
                Jwt jwt = JwtHelper.verifySign(token, secret, SignatureAlgorithm.forName(algorithm));

                Claims claims = (Claims)jwt.getBody();
                String sub = claims.get("sub").toString();
                System.out.println("sub:" + sub);

                if("jack".equals(sub)){
                    return chain.filter(exchange);
                }else{
                    JsonResult jsonResult = JsonResult.fail();
                    jsonResult.setData("usercode is error");

                    //返回
                    ServerHttpResponse response = exchange.getResponse();
                    String jsonString = JSON.toJSONString(jsonResult);
                    DataBuffer dataBuffer = response.bufferFactory().wrap(jsonString.getBytes(StandardCharsets.UTF_8));
                    return response.writeWith(Mono.just(dataBuffer));
                }

            }catch (Exception e){
                JsonResult jsonResult = JsonResult.fail();
                jsonResult.setData(e.getMessage());

                //返回
                ServerHttpResponse response = exchange.getResponse();
                String jsonString = JSON.toJSONString(jsonResult);
                DataBuffer dataBuffer = response.bufferFactory().wrap(jsonString.getBytes(StandardCharsets.UTF_8));
                return response.writeWith(Mono.just(dataBuffer));
            }

        }
    }

    @Override
    public int getOrder() {
        //正数值越小,负数的绝对值越大,优先级越高
        return 1;
    }
}

4.4测试

4.4.1启动并测试:http://localhost:9090/feign/getUsers?origin=qwer 由于Header中未携带tonken则无法访问
4.4.2再次访问:http://localhost:9090/authentic/login?usercode=jack&password=123 获取token后再次携带token访问

标签:网关,service,Spring,配置管理,springframework,micro,import,org,gateway
来源: https://www.cnblogs.com/qtyanan/p/16464418.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有