Spring Cloud Gateway Learning (2) - Solução distorcida chinesa Gateway e autenticação de login baseada em gateway e caso de autenticação e caso de integração de gateway e sentinela

Prefácio

Spring Cloud Gateway é baseado no Spring Boot 2 e é um novo projeto do Spring Cloud. O Gateway foi projetado para fornecer uma maneira simples e eficiente de encaminhar solicitações e fornecer preocupações transversais a elas.

O gateway é equivalente ao portal de todos os serviços, que separa as solicitações dos clientes das aplicações do servidor. Depois que as solicitações dos clientes passam pelo gateway, elas são encaminhadas por rotas e asserções definidas. As rotas representam os endereços para os quais as solicitações precisam ser encaminhadas. As asserções são equivalentes às solicitações para esses endereços. Se as condições forem atendidas, ele será encaminhado somente se atender ao roteamento e à asserção.

Este blog apresenta a solução de gateway para o problema ilegível chinês, um caso de implementação de autenticação de login e autenticação baseada em gateway, e apresenta o método de integração de gateway e sentinela.

Outros artigos sobre Gatew são os seguintes:

Spring Cloud Gateway Learning (1) - Conceitos básicos de Gateway e coisas a serem observadas ao introduzir dependências + Soluções e casos de uso introdutórios de gateways globais

Insira a descrição da imagem aqui

liderar


1. Solução do gateway para o problema dos caracteres chineses distorcidos;
2. Um caso de implementação de autenticação de login e autenticação baseada em gateway;
3. Introdução do método de integração de gateway e sentinela;

Insira a descrição da imagem aqui

Problema distorcido do gateway chinês

Insira a descrição da imagem aqui

Comparação entre caracteres ilegíveis e não ilegíveis

Insira a descrição da imagem aqui

Insira a descrição da imagem aqui

response.getHeaders().add("Content-Type","application/json;charset=UTF-8");

solução final

Insira a descrição da imagem aqui

package com.tianju.gateway.config;

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * 未起作用
 */
@Component
// 该值是可选的,表示Ordered接口中定义的订单值。值越低,优先级越高。
// 默认值为Ordered.LOWEST_PRECDENCE,表示最低优先级(输给任何其他指定的顺序值)
@Order(-1)
public class CharacterGateway implements GlobalFilter {
    
     // GatewayFilter

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    
    
        ServerHttpResponse response = exchange.getResponse();
        response.getHeaders().add("Content-Type","application/json;charset=UTF-8");
//        HttpHeaders headers = response.getHeaders();
//        headers.set(HttpHeaders.CONTENT_TYPE, "text/html;charset=UTF-8");
//        headers.set(HttpHeaders.CONTENT_TYPE, "application/json;charset=UTF-8");
        return chain.filter(exchange);
    }
}

Solicite novamente, o resultado da resposta é normal em chinês

Insira a descrição da imagem aqui

Gateway implementa login e permissões simples

1. Introduzir dependências

Insira a descrição da imagem aqui

<?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">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.tianju</groupId>
        <artifactId>springcloud-restTemplate</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <groupId>com.tianju.gateway</groupId>
    <artifactId>springcloud-gateway</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <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>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
        </dependency>

        <!--        测试sql mapper 的包-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

        <!--        响应式编程的 redis 的包-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
        </dependency>

        <!--    sentinel整合gateway-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
        </dependency>


    </dependencies>

</project>

2. Arquivo de configuração

Insira a descrição da imagem aqui

arquivo de configuração bootsrape.yml

spring:
  cloud:
    # nacos的部分
    nacos:
      discovery: # 注册中心
        server-addr: http://192.168.111.130:8848/
        register-enabled: true
        # 命名空间
        namespace: my-tianju
        # 组名
        group: DEV

    # sentinel的配置
    sentinel:
      transport:
        dashboard: 192.168.111.130:7777
        port: 8719
      # 这样一启动能够立马被发现,不用请求一次后才被监控
      eager: true
      # 自定义异常返回
      scg:
        fallback:
          mode: response
          response-body: "{'code':403.'msg':'请求次数过多,被限流'}"

    # 网关部分
    gateway:
      discovery:
        locator:
          enabled: true # 允许定位
      routes: # 路由
        - id: springCloud-consumer # id要唯一
          # lb表示负载均衡
          uri: lb://springCloud-consumer # 在nacos里根据服务名称找
          predicates:
          # http://localhost:18888/hello-wx/api/consumer/addHello
          # 根据上面,拼出来下面路径
          # http://localhost:10002/api/consumer/addHello
           - Path=/hello-wx/** # 比如输了 ip+端口/hello-wx/** 然后在nacos找真的路径
          filters:
            - StripPrefix=1 # 替换第一个,内置的filter过滤器
            # 给头部添加token AddRequestHeader: 键为token,值为后面的
            - AddRequestHeader=token, eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.e30.s1dUJ5ffnO6ntO3WD3Je2Ythfb2qUK59_7tAS5ItXkY

        - id: my-hello-baidu # id要唯一
          uri: https://www.sohu.com
          predicates:
          # http://localhost:18888/hello-ly
           - Path=/hello-ly/**

  # 如要要用spring web,则把tomcat排除一下
#  main:
#    web-application-type: reactive

arquivo de configuração application.yml, redis, etc.

server:
  port: 18888


spring:

  # redis的相关配置
  redis:
    host: 124.70.138.34
    port: 6379
    database: 0
    password: My3927

  # 应用名
  application:
    name: springCloud-gateway

logging:
  level:
    com.tianju.gateway: debug

3. Escreva um filtro global

Insira a descrição da imagem aqui

(1) Definir formato de codificação

package com.tianju.gateway.config;

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * GatewayFilter未起作用,换成GlobalFilter后实现了,
 * 并且@Order(-1)设置为-1
 */
@Component
// 该值是可选的,表示Ordered接口中定义的订单值。值越低,优先级越高。
// 默认值为Ordered.LOWEST_PRECDENCE,表示最低优先级(输给任何其他指定的顺序值)
@Order(-1)
public class CharacterGateway implements GlobalFilter {
    
     // GatewayFilter

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    
    
        ServerHttpResponse response = exchange.getResponse();
        response.getHeaders().add("Content-Type","application/json;charset=UTF-8");
//        HttpHeaders headers = response.getHeaders();
//        headers.set(HttpHeaders.CONTENT_TYPE, "text/html;charset=UTF-8");
//        headers.set(HttpHeaders.CONTENT_TYPE, "application/json;charset=UTF-8");
        return chain.filter(exchange);
    }
}

(2) Filtro de autenticação de login

Insira a descrição da imagem aqui

package com.tianju.gateway.config;

import cn.hutool.jwt.JWTUtil;
import com.fasterxml.jackson.databind.ObjectMapper;

import com.tianju.gateway.result.HttpResp;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;

@Component
@Order(1)
@Slf4j

// http://localhost:18888/hello-wx/api/cinema/checkGenreInThisCinema
public class LoginGateway implements GlobalFilter {
    
    

    @Override
    @SneakyThrows
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    
    
        log.debug("我是登陆过滤器>>>>>>>>>>");
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
//        response.getHeaders().add("Content-Type","application/json;charset=UTF-8");


        // 请求request 的URL:http://localhost:18888/hello-wx/api/cinema/checkGenreInThisCinema
        log.debug("请求request 的URL:"+request.getURI());

        // eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.e30.s1dUJ5ffnO6ntO3WD3Je2Ythfb2qUK59_7tAS5ItXkY
        String token = request.getHeaders().getFirst("token");
        log.debug("请求request 的token数据"+token);


        if (ObjectUtils.isEmpty(token) || !JWTUtil.verify(token, "PET".getBytes())){
    
     // 如果没有携带token 或者JWT没有通过
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            ObjectMapper objectMapper = new ObjectMapper();
            DataBuffer buffer = response.bufferFactory()
                    .wrap(objectMapper.writeValueAsString(HttpResp.failed("未携带token"))
                            .getBytes(StandardCharsets.UTF_8));

            return response.writeWith(Mono.just(buffer));

        }else {
    
    
            return chain.filter(exchange);
        }
    }
}

(3) Filtro de permissão

Insira a descrição da imagem aqui

package com.tianju.gateway.config;

import cn.hutool.jwt.JWTUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tianju.gateway.result.HttpResp;
import lombok.SneakyThrows;
import lombok.Synchronized;
import lombok.extern.slf4j.Slf4j;
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.annotation.Order;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.ReactiveStringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.nio.charset.StandardCharsets;

@Component
@Order(2)
@Slf4j

/**
 * 鉴权的过滤器
 */
public class AuthGateway implements GlobalFilter {
    
    

    @Autowired
    private ReactiveStringRedisTemplate redisTemplate;

    @Override
    @SneakyThrows
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    
    
        log.debug("我是鉴权的过滤器>>>>>>>>>>");
        ServerHttpRequest request = exchange.getRequest();
//        ServerHttpResponse response = exchange.getResponse();


        // 鉴权
        // 先获取用户信息目前用户演示指定userId=1(在实际在开过程中用户信息是从JWT中获取),
        // 再获取path
        // 请求request 的URL:http://localhost:18888/hello-wx/api/cinema/checkGenreInThisCinema
        String path = request.getPath().toString();
        log.debug("请求request 的URI:"+request.getURI());
        log.debug("请求request 的PATH:"+path);
        String uri = request.getURI().toString();

//        redisTemplate.opsForHash().hasKey("1",path).map(
//                b->{
    
    
//                    return chain.filter(exchange);
//                }
//        );

        // eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.e30.s1dUJ5ffnO6ntO3WD3Je2Ythfb2qUK59_7tAS5ItXkY
        String token = request.getHeaders().getFirst("token");
        log.debug("请求request 数据"+token);
        return redisTemplate.opsForHash().hasKey("1", path).flatMap(b -> {
    
    
            if (b) {
    
     // 放行
                return chain.filter(exchange);
            }
            ServerHttpResponse response = exchange.getResponse();
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            // TODO:响应式编程中解决中文乱码问题
//            response.getHeaders().add("Content-Type","application/json;charset=UTF-8");

            // 返回httpResp的相关
            ObjectMapper objectMapper = new ObjectMapper();
            DataBuffer buffer = null;
            try {
    
    
                buffer = response.bufferFactory()
                        .wrap(objectMapper.writeValueAsString(HttpResp.failed("没有权限"))
                                .getBytes(StandardCharsets.UTF_8));
            } catch (JsonProcessingException e) {
    
    
                return Mono.error(new RuntimeException(e));
            }
            return response.writeWith(Mono.just(buffer));
        });
    }
}

Dados armazenados em redis

Insira a descrição da imagem aqui

4. Classe de devolução personalizada

(1)HttpResp

Insira a descrição da imagem aqui

package com.tianju.gateway.result;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.util.Date;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class HttpResp<T> implements Serializable {
    
    
    private int code;
    private String msg;
    private T data;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    private Date date;

    /**
     * 成功无返回结果
     *
     * @return HttpResp
     */
    public static <T> HttpResp<T> success() {
    
    
        return success(null);
    }
    /**
     * 成功, 有返回结果
     *
     * @param data 返回数据
     * @return 返回HttpResp对象
     */
    public static <T> HttpResp<T> success(T data) {
    
    
        RespCode success = RespCode.SUCCESS;
        return new HttpResp(
                success.getCode(),
                success.getMsg(),
                data,
                new Date());
    }
    /**
     * 失败自定义返回错误信息
     *
     * @return 返回HttpResp对象
     */
    public static <T> HttpResp<T> failed(String msg) {
    
    
        return new HttpResp(
                RespCode.ERROR.getCode(),
                msg,
                null,
                new Date());
    }
    /**
     * 失败
     *
     * @return 返回HttpResp对象
     */
    public static <T> HttpResp<T> failed(RespCode respCode) {
    
    
        return new HttpResp(
                respCode.getCode(),
                respCode.getMsg(),
                null,
                new Date());
    }
}

(2) Tipo de enumeração RespCode

Insira a descrição da imagem aqui

package com.tianju.gateway.result;

import lombok.Getter;

@Getter
public enum RespCode {
    
    
    SUCCESS(200, "ok"),
    ERROR(500, "服务器异常"),
    PARAM_IS_NULL(410, "请求必填参数为空"),
    PARAM_ERROR(400, "用户请求参数错误"),
    ACCESS_UNAUTHORIZED(301, "访问未授权"),
    USER_NOT_EXIST(201, "用户不存在"),
    USER_ACCOUNT_LOCKED(202, "用户账户被冻结"),
    USER_ACCOUNT_INVALID(203, "用户账户已作废"),
    USERNAME_OR_PASSWORD_ERROR(210, "用户名或密码错误"),
    CLIENT_AUTHENTICATION_FAILED(212, "客户端认证失败"),
    TOKEN_INVALID_OR_EXPIRED(230, "token无效或已过期"),
    TOKEN_ACCESS_FORBIDDEN(231, "token已被禁止访问"),
    RESOURCE_NOT_FOUND(404, "未找到接口异常");

    ;
    private int code;
    private String msg;

    RespCode(int code,String msg){
    
    
        this.code = code;
        this.msg = msg;
    }
}

Integração de gateway e sentinela

1. Introduzir dependências

Insira a descrição da imagem aqui

        <!--    sentinel整合gateway-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
        </dependency>

2. Adicionar configuração de gateway

Insira a descrição da imagem aqui

spring:
  cloud:
    # nacos的部分
    nacos:
      discovery: # 注册中心
        server-addr: http://192.168.111.130:8848/
        register-enabled: true
        # 命名空间
        namespace: my-tianju
        # 组名
        group: DEV

    # sentinel的配置
    sentinel:
      transport:
        dashboard: 192.168.111.130:7777
        port: 8719
      # 这样一启动能够立马被发现,不用请求一次后才被监控
      eager: true

    # 网关部分
    gateway:
      discovery:
        locator:
          enabled: true # 允许定位
      routes: # 路由
        - id: springCloud-consumer # id要唯一
          # lb表示负载均衡
          uri: lb://springCloud-consumer # 在nacos里根据服务名称找
          predicates:
          # http://localhost:18888/hello-wx/api/consumer/addHello
          # 根据上面,拼出来下面路径
          # http://localhost:10002/api/consumer/addHello
           - Path=/hello-wx/** # 比如输了 ip+端口/hello-wx/** 然后在nacos找真的路径
          filters:
            - StripPrefix=1 # 替换第一个,内置的filter过滤器
            # 给头部添加token AddRequestHeader: 键为token,值为后面的
            - AddRequestHeader=token, eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.e30.s1dUJ5ffnO6ntO3WD3Je2Ythfb2qUK59_7tAS5ItXkY

        - id: my-hello-baidu # id要唯一
          uri: https://www.sohu.com
          predicates:
          # http://localhost:18888/hello-ly
           - Path=/hello-ly/**

  # 如要要用spring web,则把tomcat排除一下
#  main:
#    web-application-type: reactive

3. exibição da página sentinela

Envie uma solicitação, duas páginas sentinela aparecem

Insira a descrição da imagem aqui

4. Padrão rápido de falha

Modo de falha rápido, defina o número máximo de solicitações por segundo

Insira a descrição da imagem aqui

Correspondência entre o arquivo de configuração e os parâmetros da página sentinela

Insira a descrição da imagem aqui

Faça solicitações rápidas, não limite o tráfego sentinela

Insira a descrição da imagem aqui

Retorno de exceção personalizado

Arquivo de configuração para configurações de retorno de exceção customizadas

Insira a descrição da imagem aqui

spring:
  cloud:
    # nacos的部分
    nacos:
      discovery: # 注册中心
        server-addr: http://192.168.111.130:8848/
        register-enabled: true
        # 命名空间
        namespace: my-tianju
        # 组名
        group: DEV

    # sentinel的配置
    sentinel:
      transport:
        dashboard: 192.168.111.130:7777
        port: 8719
      # 这样一启动能够立马被发现,不用请求一次后才被监控
      eager: true
      # 自定义异常返回
      scg:
        fallback:
          mode: response
          response-body: "{'code':403.'msg':'请求次数过多,被限流'}"

Retornar o efeito da exceção personalizada

Insira a descrição da imagem aqui

5.Modo API

Primeiro você precisa adicionar uma API

Insira a descrição da imagem aqui

Seleção de parâmetros do modo de agrupamento de API

Insira a descrição da imagem aqui

Solicitação rápida, restrita por sentinela

Insira a descrição da imagem aqui


Resumir

1. Solução do gateway para o problema dos caracteres chineses distorcidos;
2. Um caso de implementação de autenticação de login e autenticação baseada em gateway;
3. Introdução do método de integração de gateway e sentinela;

Acho que você gosta

Origin blog.csdn.net/Pireley/article/details/133443833
Recomendado
Clasificación