Chat2Excel 项目网关服务开发

一、编写 bootstrap 配置文件

复制代码
spring:
  application:
    name: gateway-service
  main:
    web-application-type: reactive  # 设置为响应式应用类型,避免与Spring MVC冲突

  # Jackson配置
  jackson:
    date-format: HH:mm:ss
    time-zone: UTC
    serialization:
      write-dates-as-timestamps: false
      fail-on-empty-beans: false
    deserialization:
      fail-on-unknown-properties: false
      fail-on-null-for-primitives: false
      accept-empty-string-as-null-object: true
      accept-single-value-as-array: true

  # Redis配置 - Gateway 服务使用更长的超时时间
  data:
    redis:
      host: ***
      port: 6379
      password: ***
      database: 0
      timeout: 10000ms
      lettuce:
        pool:
          max-active: 8
          max-idle: 8
          min-idle: 0
          max-wait: -1ms

  # Nacos服务发现配置
  cloud:
    nacos:
      discovery:
        server-addr: ***:8848
        username: nacos
        password: ***
        namespace: public
        enabled: true  # 启用 Nacos 服务发现

    gateway:
      # 默认过滤器
      default-filters:
        - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin
#        - JwtAuth

      # 全局跨域配置
      globalcors:
        cors-configurations:
          '[/**]':
            # 允许跨域的源(使用 allowedOriginPatterns 支持通配符和凭证)
            allowedOriginPatterns: "*"
            # 允许跨域的HTTP方法
            allowedMethods:
              - GET
              - POST
              - PUT
              - DELETE
              - OPTIONS
            # 允许跨域的请求头
            allowedHeaders: "*"
            # 允许携带凭证
            allowCredentials: true
            # 跨域预检的有效期,单位为秒
            maxAge: 3600

      # 路由配置
      routes:
        # 用户服务路由
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/api/v1/users/**
          filters:
            - StripPrefix=2  # 去掉 /api/v1,保留后续路径

        # 文件服务路由
        - id: file-service
          uri: lb://file-service
          predicates:
            - Path=/api/v1/files/**
          filters:
            - StripPrefix=2  # 去掉 /api/v1,保留后续路径

        # AI服务路由 - llm接口
        - id: ai-service-llm
          uri: lb://ai-service
          predicates:
            - Path=/api/v1/llm/**
          filters:
            - StripPrefix=2  # 去掉 /api/v1,保留后续路径

        # AI服务路由 - ai接口
        - id: ai-service-ai
          uri: lb://ai-service
          predicates:
            - Path=/api/v1/ai/**
          filters:
            - StripPrefix=2  # 去掉 /api/v1,保留后续路径


# MyBatis Plus公共配置
mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    db-config:
      id-type: auto
      table-underline: true

# 安全配置 - 网关令牌(所有服务共享)
security:
  gateway:
    token: "internal-gateway-secret-token-2024"
    enabled: true  # 设置为 false 可以禁用网关检查(开发环境)
  whitelist:
    - /users/auth
    - /users/verification-code

# 自定义日志配置
logging:
  pattern:
    console: "%clr(%d{HH:mm:ss.SSS}){faint} %clr(%5p) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){cyan} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n%wEx"

二、创建配置类

java 复制代码
package com.linzhixin.gateway.config;

import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 网关配置类(空路由,在bootstrap里面已经配置类路由规则,其实这个配置类可以省略)
 */
@Configuration
public class GateWayConfig {

    /**
     * 自定义路由配置
     * @param builder RouteLocatorBuilder构造器
     * @return RouteLocator实例对象
     */
    @Bean
    public RouteLocator routeLocator(RouteLocatorBuilder builder) {
        return builder.routes().build();
    }
}
java 复制代码
package com.linzhixin.gateway.config;


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
import org.springframework.data.redis.core.ReactiveRedisTemplate;
import org.springframework.data.redis.core.ReactiveStringRedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 *  Redis 配置类
 * Spring 确实会自动配置,但默认序列化方式不友好。这个配置类主要是为了把序列化方式改成 String,确保 Redis 数据可读、可共享。
 */
@Configuration
public class RedisConfig {

    /**
     * 构建 ReactiveRedisTemplate -》 用于在响应式编程环境中(如 Gateway/WebFlux)异步操作 Redis。
     * @param connectionFactory 连接工厂
     * @return ReactiveRedisTemplate示例对象
     */
    @Bean
    public ReactiveRedisTemplate<String, String> reactiveRedisTemplate(ReactiveRedisConnectionFactory connectionFactory) {
        // 1、序列化
        // StringRedisSerializer 是 Spring Data Redis 提供的一个序列化器,负责 Java 对象 ↔ Redis 存储格式 的转换。
        StringRedisSerializer serializer = new StringRedisSerializer();

        // 2、创建 builder
        RedisSerializationContext.RedisSerializationContextBuilder<String, String> builder =
                RedisSerializationContext.newSerializationContext();

        RedisSerializationContext<String, String> context = builder
                .key(serializer)
                .value(serializer)
                .hashKey(serializer)
                .hashValue(serializer)
                .build();
        return new ReactiveRedisTemplate<>(connectionFactory, context);
    }
}

三、实现令牌过滤器(写在 common-service 中)

java 复制代码
package com.linzhixin.common.filter;

import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.io.IOException;


/**
 * 网关令牌验证过滤器
 * GatewayTokenFilter 会作用于所有依赖 common-service 的服务,只要这些服务启动时被 Spring 扫描到。
 */
@Component
@Slf4j
@Order(1)
public class GatewayTokenFilter implements Filter {
    /**
     * 网关令牌
     */
    @Value("${security.gateway.token:}")
    private String gatewayToken;

    /**
     * 是否启用网关令牌(网关控制器)
     */
    @Value("${security.gateway.enabled:true}")
    private boolean gatewayCheckEnable;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        if (gatewayCheckEnable) {
            log.info("网关令牌过滤器已经启动");
        } else {
            log.info("允许直接访问后端");
        }
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws
            IOException, ServletException {
        // 1、需要请求request
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
        String path = httpServletRequest.getRequestURI();
        String method = httpServletRequest.getMethod();

        // 2、网关控制器做判断
        if(!gatewayCheckEnable) {
            //将请求和响应传递给过滤器链中的下一个过滤器,如果已是最后一个过滤器,则传递给目标 Servlet/Controller。
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        }

        // 3、获取网关令牌
        String gatewayToke = httpServletRequest.getHeader("X-Gateway-Token");
        if(gatewayToke == null || gatewayToke.isEmpty()) {
            log.warn("网关令牌为空");
            unAuthorized(httpServletResponse, "禁止访问");
            return;
        }

        // 4、验证网关令牌
        if(!gatewayToke.equals(this.gatewayToken)) {
            log.warn("网关令牌错误");
            unAuthorized(httpServletResponse, "禁止访问");
            return;
        }

        log.info("网关令牌验证通过:{} {}", method, path);
        filterChain.doFilter(httpServletRequest, httpServletResponse);
     }

    @Override
    public void destroy() {
        Filter.super.destroy();
    }

    private void unAuthorized(HttpServletResponse response, String message) {
        response.setStatus(403);
        //设置 HTTP 响应的内容类型和字符编码,告诉浏览器/客户端"我返回的是 JSON 数据,用 UTF-8 解码"。
        response.setContentType("application/json;charset=UTF-8");
        //用 String.format 把错误码和错误信息动态拼成 JSON 字符串。
        // { : JSON 对象开始
        // \"code\" : 转义双引号 → 表示 JSON 的 key 是 code
        //:%d :占位符,填入整数

        String json = String.format(
                "{\"code\":%d,\"message\":\"%s\"}",
                403,
                message
        );
        try {
            //获取响应的字符输出流,将 JSON 字符串写入响应体,发送给客户端。
            response.getWriter().write(json);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
相关推荐
谢亮_vipxieliang14 分钟前
ValidX vs Apache Commons Validator:功能与性能对比
java·服务器·spring boot·后端·spring cloud·apache·hibernate
tryxr19 分钟前
Chat2Excel 项目通用服务开发
java·开发语言·excel·java项目开发
小裕哥略帅23 分钟前
Spring AOP 实现通用 Token 自动刷新重试工具包
java·后端·spring
Lenyiin27 分钟前
第6篇_Python高级语法与标准库精髓:从会用到达精通
java·python·html
YHHLAI30 分钟前
TypeScript 面试题:type 与 interface 的区别与相同点
java·ubuntu·typescript
咖啡八杯35 分钟前
PageHelper 分页封装:TableDataInfo 与 startPage() 的工作原理
java·spring boot·分页·若依·开源框架·pagehelper
多敲代码防脱发37 分钟前
Alibaba Sentinel(熔断、限流)(国内下载Sentinel)
java·开发语言·sentinel
.Hypocritical.40 分钟前
【SpringBoot】配置文件加载位置与优先级详解
java·spring boot·后端