目录

一、分布式。

1、什么是分布式

核心特征

关键技术组件

2、为什么需要分布式

单个计算机的局限性

多个计算机的优势

分布式系统通过冗余设计持有高可用性

地理分布需求

3、言而总之

二、网关

1、什么是网关

2、网关的作用

3、言而总之

三、认证

1、认证在计算机中,大家应该不陌生。主要有一下三点作用。

2、言而总之

四、环境准备阶段

1、Intellij IDEA官网地址:

 2、JDK8资源地址:关注公众号,回复:jdk8         小牛 - 全栈工程师 | 个人官网

3、nacos下载地址:Nacos官网| Nacos 配置中心 | Nacos 下载| Nacos 官方社区 | Nacos 官网Nacos 提供动态服务发现、配置和管理,助力用户在私有云、混合云、公有云等环境中快速构建和交付微服务平台,提升业务复用和创新交付速度,为用户赢得市场竞争力。https://nacos.io/

 五、开始系统搭建

1、首先打开IDEA编辑器,点击新建项目。版本:springboot2.3.12.RELEASE

 2、创建模块

按照以上模块创建流程,继续创建cloud-user、cloud-gateway、cloud-common。

3、cloud-auth模块代码

4、cloud-common模块代码

5、cloud-gateway模块代码

6、cloud-user模块代码

7、父工程pom文件


一、分布式。

1、什么是分布式

分布式系统(Distributed System)是指由多个独立的计算机节点通过网络连接组成的系统,这些节点协同工作,对用户呈现为一个统一的整体。

核心特征

  1. 多节点性:系统由多台计算机组成,通常分布在不同的地理位置
  2. 并发性:各节点可以并行处理任务
  3. 无共享架构:节点之间不共享内存或时钟,通过消息传递通信
  4. 透明性:对用户隐藏分布特性,表现为单一系统形象
  5. 故障独立性:单个节点故障不会导致整个系统崩溃

关键技术组件

  • 分布式通信:RPC、消息队列、gRPC等
  • 共识算法:Paxos、Raft等
  • 分布式存储:HDFS、Ceph等
  • 资源调度:YARN、Mesos等
  • 服务发现:ZooKeeper、etcd等

2、为什么需要分布式

单个计算机的局限性

  • 单机硬件存在物理上限(CPU、内存、I/O)
  • 成本非线性增长(高端服务器价格昂贵)
  • 存在单点故障风险

多个计算机的优势

  • 通过增加普通服务器提升整体性能
  • 线性扩展能力
  • 成本效益更高
  • Google搜索每天处理数十亿查询,必须依赖分布式架构才能实现毫秒级响应。

分布式系统通过冗余设计持有高可用性

  • 数据多副本存储(如HDFS默认3副本)
  • 故障自动检测与恢复
  • 服务无状态设计,可快速迁移

地理分布需求

  • 就近服务用户,降低延迟(CDN原理)
  • 符合数据主权法规(GDPR等)
  • 灾难恢复能力(异地多活)
  • 跨国电商需要在各大洲部署分布式系统,确保各地用户购物体验一致。

3、言而总之

一台机器扛不住,需要多个机器一起扛。有难同当。

二、网关

1、什么是网关

网关是连接异构网络的枢纽设备/服务,主要工作在OSI第4-7层,承担协议转换、流量调度和安全管控三大核心职能。现代API网关(如Kong/Nginx)已成为微服务架构的"智能交通警察",实现请求路由负载均衡鉴权限流等关键功能。在云原生场景中,网关更演变为服务网格的流量控制平面,通过协议转换能力(如HTTP/gRPC互转)实现系统间无缝对接,是构建高可用分布式系统的核心组件。

2、网关的作用

网关是连接不同网络系统的"智能枢纽",主要发挥三大核心作用:

  1. 协议转换:像翻译官一样,让使用不同通信协议的系统能相互理解(如把HTTP请求转为银行系统的私有协议)

  2. 流量管控:担任"网络交警"的角色,智能分配请求流量、控制访问速度、保障系统不被突发流量冲垮

  3. 安全防护:作为统一的安全检查站,集中处理身份验证、权限控制、防攻击等安全事务

  4. 现代网关(如API网关)更是微服务架构的"总调度室",既协调各服务高效协作,又对外提供统一入口。它就像公司的前台接待,既分流来访者到对应部门,又核查身份确保安全,让整个系统运转更顺畅、更安全。

3、言而总之

保安

三、认证

1、认证在计算机中,大家应该不陌生。主要有一下三点作用。

  1. 身份核验(Authentication)
  • 通过密码、短信验证码、指纹、token、session等方式验证"你是你"
  • 典型案例:微信扫码登录第三方网站时,需在手机端确认授权
  1. 权限管控(Authorization)
  • 确定用户能访问哪些资源
  • 例如:普通员工只能查看自己的考勤记录,而HR可以查看全公司数据
  1. 访问审计(Auditing)
  • 记录所有登录尝试和操作行为
  • 典型应用:银行APP会记录每一次登录的设备和地点

2、言而总之

请出示证件

四、环境准备阶段

首先选择一个自己喜欢的编辑器方便开发,我选择Intellij IDEA,准备好JDK8(最常用的版本、因为稳定,稳定比过花哨),nacos(服务注册与发现),Maven,redis

1、Intellij IDEA官网地址:

IntelliJ IDEA – the IDE for Pro Java and Kotlin DevelopmentIntelliJ IDEA is the JetBrains IDE for pro development in Java and Kotlin. Built for your comfort, it unlocks productivity, ensures quality code, supports cutting-edge tech, and protects your privacy.https://www.jetbrains.com/idea/

 

 2、JDK8资源地址:关注公众号,回复:jdk8         小牛 - 全栈工程师 | 个人官网

3、nacos下载地址:Nacos官网| Nacos 配置中心 | Nacos 下载| Nacos 官方社区 | Nacos 官网Nacos 提供动态服务发现、配置和管理,助力用户在私有云、混合云、公有云等环境中快速构建和交付微服务平台,提升业务复用和创新交付速度,为用户赢得市场竞争力。https://nacos.io/

 

 Nacos的使用教程可以前往这个博主文章:Nacos使用教程,还不会使用Nacos,看这一篇就够了!-CSDN博客

 Maven官网下载链接:Maven – 下载 Maven

Redis的github链接地址:https://github.com/microsoftarchive/redis

 五、开始系统搭建

1、首先打开IDEA编辑器,点击新建项目。版本:springboot2.3.12.RELEASE

 

 2、创建模块

选择好后直接下一步创建

  

按照以上模块创建流程,继续创建cloud-user、cloud-gateway、cloud-common。

3、cloud-auth模块代码

项目结构

pom文件

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.com</groupId>
        <artifactId>cloud</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <artifactId>cloud-auth</artifactId>
    <packaging>jar</packaging>

    <name>cloud-auth</name>
    <description>cloud-auth</description>

    <properties>

    </properties>

    <dependencies>
        <!--web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--验证参数-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        <!--服务注册与发现-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>org.com</groupId>
            <artifactId>cloud-common</artifactId>
            <version>0.0.1-SNAPSHOT</version>
            <scope>compile</scope>
            <exclusions>
                <exclusion>
                    <groupId>com.baomidou</groupId>
                    <artifactId>mybatis-plus-boot-starter</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.projectlombok</groupId>
                    <artifactId>lombok</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.apache.commons</groupId>
                    <artifactId>commons-pool2</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>cn.hutool</groupId>
                    <artifactId>hutool-all</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.apache.commons</groupId>
                    <artifactId>commons-lang3</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>commons-codec</groupId>
                    <artifactId>commons-codec</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>commons-io</groupId>
                    <artifactId>commons-io</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <encoding>${project.build.sourceEncoding}</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <configuration>
                    <mainClass>org.com.cloudAuth.CloudAuthApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

yml文件 

server:
    port: 9092
    servlet:
        context-path: /api
spring:
    application:
        name: authService
    main:
        allow-bean-definition-overriding: true
    profiles:
        active: dev
    cloud:
        nacos:
            server-addr: localhost:8848
            discovery:
                cluster-name: HN
auth:
    excludePaths: # 不需要登录 即可访问的地址
        - /api/login/**
    origins:
        - 127.0.0.1:9090

CommonConfig 

package org.com.cloudAuth.config;

import org.com.cloudCommon.redis.RedisTool;
import org.com.cloudCommon.utils.JwtUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;


@Configuration
public class CommonConfig<V> {
    @Bean
    public RedisTool<V> redisTool(RedisTemplate<String, V> redisTemplate) {
        return new RedisTool<>(redisTemplate);
    }

    @Bean("redisTemplate")
    public RedisTemplate<String, V> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, V> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        //设置key的序列化方式
        template.setKeySerializer(RedisSerializer.string());
        //设置value的序列化方式
        template.setValueSerializer(RedisSerializer.json());
        //设置hash的key的序列化方式
        template.setHashKeySerializer(RedisSerializer.string());
        //设置hash的value的序列化方式
        template.setHashValueSerializer(RedisSerializer.json());
        return template;
    }

    @Bean
    public JwtUtil<V> jwtUtil(RedisTool<V> redisTool) {
        return new JwtUtil<>(redisTool);
    }
}

ExceptionAdvice

package org.com.cloudAuth.config;

import lombok.extern.slf4j.Slf4j;
import org.com.cloudCommon.enums.ResultCode;
import org.com.cloudCommon.exception.BusinessException;
import org.com.cloudCommon.model.R;
import org.com.cloudCommon.exception.AuthException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.validation.ConstraintViolationException;


@Slf4j
@RestControllerAdvice
public class ExceptionAdvice {
    @ExceptionHandler(Exception.class)
    public R<String> handleException(Exception e) {
        log.error("系统异常", e);
        return R.fail(ResultCode.SystemError);
    }

    @ExceptionHandler(BusinessException.class)
    public R<String> BusinessException(BusinessException e) {
        log.error(e.getMessage());
        return R.fail(e.getResultCode());
    }

    @ExceptionHandler(AuthException.class)
    public R<String> AuthException(AuthException e) {
        log.error(e.getMessage());
        return R.fail(e.getResultCode());
    }


    @ExceptionHandler(NoHandlerFoundException.class)
    public R<String> NoHandlerFoundException(NoHandlerFoundException e) {
        log.error("请求路径不存在 {}", e.getMessage());
        return R.fail(ResultCode.NotFindError);
    }

    @ExceptionHandler(IllegalStateException.class)
    public R<String> IllegalStateException(IllegalStateException e) {
        log.error("参数错误 {}", e.getMessage());
        return R.fail(ResultCode.CODE_10017);
    }

    @ExceptionHandler(HttpMessageNotReadableException.class)
    public R<String> HttpMessageNotReadableException(HttpMessageNotReadableException e) {
        log.error("参数错误 {}", e.getMessage());
        return R.fail(ResultCode.CODE_10017);
    }

    @ExceptionHandler(IllegalArgumentException.class)
    public R<String> IllegalArgumentException(IllegalArgumentException e) {
        log.error("参数错误 {}", e.getMessage());
        return R.fail(ResultCode.CODE_10017);
    }

    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public R<String> HttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
        log.error("参数错误 {}", e.getMessage());
        return R.fail(ResultCode.CODE_10017);
    }

    @ExceptionHandler(ConstraintViolationException.class)
    public R<String> ConstraintViolationException(ConstraintViolationException e) {
        log.error("参数错误 {}", e.getMessage());
        return R.fail(ResultCode.CODE_10017);
    }
}

WebConfig

package org.com.cloudAuth.config;

import lombok.extern.slf4j.Slf4j;
import org.com.cloudAuth.pro.AuthPro;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

import javax.annotation.Resource;


@Configuration
@Slf4j
public class WebConfig extends WebMvcConfigurationSupport {
    @Resource
    private AuthPro authPro;

    /**
     * 配置跨域
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins(authPro.getOrigins())
                .allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE")
                .allowedHeaders("Content-Type", "Authorization")  // 允许的请求头
                .maxAge(3600)
                .allowCredentials(true);
        log.info("cloud-auth认证中心跨域配置完成");
    }
}

AuthController

package org.com.cloudAuth.controller;


import com.sun.istack.internal.NotNull;
import org.com.cloudAuth.service.AuthService;
import org.com.cloudCommon.entity.UserInfo;
import org.com.cloudCommon.model.R;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.validation.constraints.NotEmpty;
import java.util.Map;


@RestController
@RequestMapping("/auth")
@Validated
public class AuthController {
    @Resource
    private AuthService authService;

    @PostMapping("/token")
    public R<String> getToken(@RequestBody @NotNull UserInfo userInfo) {
        String token = authService.createToken(userInfo);
        return R.ok(token);
    }

    @GetMapping("check")
    public R<Map<String, Object>> validateToken(@RequestParam @NotEmpty String token, @RequestParam String role) {
        Map<String, Object> map = authService.validateToken(token, role);
        return R.ok(map);
    }
}

AuthPro

package org.com.cloudAuth.pro;

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


@Data
@ConfigurationProperties(prefix = "auth")
@Component
public class AuthPro {
    /**
     * 允许跨域的域名列表
     */
    private String[] origins;
    /**
     * 排除路径
     */
    private String[] excludePaths;
}

AuthService

package org.com.cloudAuth.pro;

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


@Data
@ConfigurationProperties(prefix = "auth")
@Component
public class AuthPro {
    /**
     * 允许跨域的域名列表
     */
    private String[] origins;
    /**
     * 排除路径
     */
    private String[] excludePaths;
}

AuthServiceImpl

package org.com.cloudAuth.service.impl;

import org.com.cloudAuth.service.AuthService;
import org.com.cloudCommon.entity.UserInfo;
import org.com.cloudCommon.enums.RoleEnum;
import org.com.cloudCommon.exception.AuthException;
import org.com.cloudCommon.utils.JwtUtil;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Map;


@Service(value = "authService")
public class AuthServiceImpl implements AuthService {
    @Resource
    private JwtUtil<Long> jwtUtil;

    @Override
    public String createToken(UserInfo userInfo) {
        return jwtUtil.generateJwtToken(userInfo);
    }

    @Override
    public Map<String, Object> validateToken(String token, String role) throws AuthException {
        return jwtUtil.checkTokenToClaims(token, RoleEnum.getByType(role));
    }
}

启动类

package org.com.cloudAuth;

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


@SpringBootApplication
public class CloudAuthApplication {
    public static void main(String[] args) {
        SpringApplication springApplication = new SpringApplication(CloudAuthApplication.class);
        springApplication.setBannerMode(Banner.Mode.OFF);
        springApplication.run(args);
    }
}

4、cloud-common模块代码

项目结构

pom文件

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.com</groupId>
        <artifactId>cloud</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <artifactId>cloud-common</artifactId>
    <packaging>jar</packaging>

    <name>cloud-common</name>
    <description>cloud-common</description>

    <properties>
    </properties>

    <dependencies>
        <!--jwt-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-api</artifactId>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-impl</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-jackson</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!--redis -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-redis</artifactId>
            <version>1.3.8.RELEASE</version>
        </dependency>
        <!--数据库交互-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mysql-plus.version}</version>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <encoding>${project.build.sourceEncoding}</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <configuration>
                    <mainClass>org.com.cloudCommon.CloudCommonApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

UserInfo

package org.com.cloudCommon.entity;

import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;


/**
 * Description 用户信息
 * date 2024-08-28 12:49:24
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("user_info")
public class UserInfo implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * 用户ID
     */
    @TableId(value = "user_id")
    private String userId;
    /**
     * 邮箱
     */
    private String email;
    /**
     * 头像
     */
    private String avatar;
    /**
     * 微信ID
     */
    private String openId;
    /**
     * 手机号
     */
    private String phone;
    /**
     * 昵称
     */
    private String nickName;
    /**
     * 0:直接添加 1:同意后添加
     */
    private Integer joinType;
    /**
     * 角色 admin:管理员 user:普通用户
     */
    private String role;
    /**
     * 0:女 1男
     */
    private Integer sex;
    /**
     * 密码
     */
    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    private String password;
    /**
     * 个性签名
     */
    private String personalSignature;
    /**
     * 状态
     */
    private Integer status;
    /**
     * 创建时间
     */
    private Long createTime;
    /**
     * 最后一次登录时间
     */
    private Long lastLoginTime;
    /**
     * 地区
     */
    private String areaName;
    /**
     * 地区编号
     */
    private String areaCode;
    /**
     * 退出登录时间
     */
    private Long lastOffTime;
}

JoinTypeEnum

package org.com.cloudCommon.enums;

import lombok.Getter;


@Getter
public enum JoinTypeEnum {
    JOIN(0, "直接加入"),
    APPLY(1, "需要同意");
    private final int joinType;
    private final String description;

    JoinTypeEnum(int joinType, String description) {
        this.joinType = joinType;
        this.description = description;
    }
}

ResultCode

package org.com.cloudCommon.enums;

import lombok.Getter;
import lombok.Setter;


@Getter
public enum ResultCode {
    Success(1, "操作成功"),
    Fail(0, "操作失败"),
    NotFindError(10001, "未查询到信息"),
    SaveError(10002, "保存信息失败"),
    UpdateError(10003, "更新信息失败"),
    DeleteError(100012, "删除信息失败"),
    ValidateError(10004, "数据检验失败"),
    StatusHasValid(10005, "状态已经被启用"),
    StatusHasInvalid(10006, "状态已经被禁用"),
    SystemError(10007, "系统异常"),
    BusinessError(10008, "业务异常"),
    TransferStatusError(10010, "当前状态不正确,请勿重复提交"),
    NotGrant(10011, "没有操作该功能的权限,请联系管理员"),
    CODE_10013(10013, "请求地址不存在"),
    CODE_500(500, "抱歉,服务器出差?"),
    CODE_10015(10015, "未登录"),
    CODE_10016(10016, "无权限"),
    CODE_10017(10017, "请求参数错误"),
    CODE_10018(10018, "邮箱验证码发送失败"),
    CODE_900(900, "登录超时");

    @Setter
    private Integer code;
    @Setter
    private String msg;

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

RoleEnum

package org.com.cloudCommon.enums;

import lombok.Getter;

import java.util.Objects;


@Getter
public enum RoleEnum {
    ADMIN("ADMIN"),
    USER("USER");

    private final String role;

    RoleEnum(String role) {
        this.role = role;
    }

    public static RoleEnum getByType(String name) {
        for (RoleEnum roleEnum : RoleEnum.values()) {
            if (name != null && roleEnum.toString() == name) {
                return roleEnum;
            }
        }
        return null;
    }
}

UserStatusEnum

package org.com.cloudCommon.enums;

import lombok.Getter;


@Getter
public enum UserStatusEnum {
    SHUT_DOWN(0, "封禁"),

    NORMAL(1, "正常");

    private final int type;
    private final String description;

    UserStatusEnum(int type, String description) {
        this.type = type;
        this.description = description;
    }
}

zeEnum

package org.com.cloudCommon.enums;

import lombok.Getter;


@Getter
public enum ZeEnum {
    ZE_EMAIL("^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$"),
    ZE_USERNAME("^.{1,7}$"),
    ZE_PASSWORD("^[a-zA-Z0-9]{3,10}$");
    private final String ze;

    ZeEnum(String ze) {
        this.ze = ze;
    }
}

AuthException

package org.com.cloudCommon.exception;


import lombok.Getter;
import org.com.cloudCommon.enums.ResultCode;


@Getter
public class AuthException extends RuntimeException {
    private final ResultCode resultCode;

    public AuthException(ResultCode codeEnum) {
        super(codeEnum.getMsg());
        this.resultCode = codeEnum;
    }

    public AuthException(ResultCode codeEnum, String msg) {
        super(msg);
        codeEnum.setMsg(msg);
        this.resultCode = codeEnum;
    }
}

BusinessException

package org.com.cloudCommon.exception;

import lombok.Getter;
import org.com.cloudCommon.enums.ResultCode;


@Getter
public class BusinessException extends RuntimeException {
    private final ResultCode resultCode;

    public BusinessException(ResultCode codeEnum) {
        super(codeEnum.getMsg());
        this.resultCode = codeEnum;
    }

    public BusinessException(ResultCode codeEnum, String msg) {
        super(msg);
        codeEnum.setMsg(msg);
        this.resultCode = codeEnum;
    }

    /**
     * 重写fillInStackTrace 业务异常不需要堆栈信息,提高效率
     */
    @Override
    public synchronized Throwable fillInStackTrace() {
        return this;
    }
}

TokenUserInfoDto

package org.com.cloudCommon.model.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.com.cloudCommon.entity.UserInfo;


@Data
@AllArgsConstructor
@NoArgsConstructor
public class TokenUserInfoDto {
    private UserInfo userInfo;
    private String token;
}

UserInfoDto

package org.com.cloudCommon.model.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.com.cloudCommon.entity.UserInfo;


@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserInfoDto {
    private UserInfo userInfo;
}

R

package org.com.cloudCommon.model;

import lombok.Data;
import lombok.NoArgsConstructor;
import org.com.cloudCommon.enums.ResultCode;

import java.io.Serializable;


@Data
@NoArgsConstructor
public class R<T> implements Serializable {
    private static final long serialVersionUID = -1L;

    private Integer code;
    private String msg;
    private T result;

    private R(T result, Integer code, String msg) {
        this.result = result;
        this.code = code;
        this.msg = msg;
    }

    private R(T result, ResultCode resultCode) {
        this.result = result;
        this.msg = resultCode.getMsg();
        this.code = resultCode.getCode();
    }

    public static R<String> ok() {
        return new R<>("", ResultCode.Success);
    }

    public static <T> R<T> ok(T data) {
        return new R<>(data, ResultCode.Success);
    }

    public static R<String> fail(ResultCode resultCode) {
        return new R<>("", resultCode);
    }

    public static R<String> fail(ResultCode resultCode, String msg) {
        return new R<>("", resultCode.getCode(), msg);
    }
}

RedisTool

package org.com.cloudCommon.redis;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


@Slf4j
@Data
public class RedisTool<V> {
    private RedisTemplate<String, V> redisTemplate;

    /**
     * 构造函数
     *
     * @param template RedisTemplate
     */
    public RedisTool(RedisTemplate<String, V> template) {
        this.redisTemplate = template;
    }

    /**
     * 批量删除对应的value
     *
     * @param key 键
     */
    public void delete(String... key) {
        if (key != null && key.length > 0) {
            if (key.length == 1) {
                redisTemplate.delete(key[0]);
            } else {
                redisTemplate.delete(Arrays.stream(key).collect(Collectors.toList()));
            }
        }
    }

    /**
     * 获取缓存
     *
     * @param key 键
     * @return 值
     */
    public V get(String key) {
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }

    /**
     * 存入缓存
     *
     * @param key   键
     * @param value 值
     */
    public void set(String key, V value) {
        try {
            redisTemplate.opsForValue().set(key, value);
        } catch (Exception ignore) {
            log.error("设置redis失败 Key:{} value:{}", key, value);
        }
    }

    /**
     * 带有时效性存入缓存
     *
     * @param key   键
     * @param value 值
     * @param time  失效
     */
    public void setX(String key, V value, long time) {
        try {
            if (time > 0) {
                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            } else {
                set(key, value);
            }
        } catch (Exception e) {
            log.error("设置redis失败 Key:{} value:{}", key, value);
        }
    }

    /**
     * 设置失效时间
     *
     * @param key  键
     * @param time 失效
     */
    public void expire(String key, long time) {
        try {
            if (time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }

    /**
     * 获取队列
     *
     * @param key 键
     * @return 队列
     */
    public List<V> getQueuelist(String key) {
        return redisTemplate.opsForList().range(key, 0, -1);
    }

    /**
     * 从队列中取出一个元素
     *
     * @param key 键
     * @return 队列中的一个元素
     */
    public V rPop(String key) {
        return redisTemplate.opsForList().rightPop(key);
    }

    /**
     * 加入队列
     *
     * @param key   键
     * @param value 值
     * @param time  失效
     */
    public void lPush(String key, V value, long time) {
        try {
            redisTemplate.opsForList().leftPush(key, value);
            if (time > 0) {
                expire(key, time);
            }
        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }

    /**
     * 从队列中取出一个元素
     *
     * @param key 键
     * @return 队列中的一个元素
     */
    public Long remove(String key, Object value) {
        try {
            return redisTemplate.opsForList().remove(key, 1, value);
        } catch (Exception e) {
            log.error(e.getMessage());
            return 0L;
        }
    }

    /**
     * 向集合中添加元素
     *
     * @param key   键
     * @param value 值
     */
    public void addSet(String key, V value) {
        redisTemplate.opsForSet().add(key, value);
    }

    /**
     * 移除集合中的元素
     *
     * @param key   键
     * @param value 值
     */
    public void removeSet(String key, V value) {
        redisTemplate.opsForSet().remove(key, value);
    }

    /**
     * 获取集合
     *
     * @param key 键
     * @return 集合
     */
    public List<V> getSet(String key) {
        Long size = redisTemplate.opsForSet().size(key);
        if (size != null && size > 0) {
            return redisTemplate.opsForSet().randomMembers(key, size);
        } else {
            return new ArrayList<>();
        }
    }

    /**
     * 获取集合中的一个元素
     *
     * @param key 键
     * @return 集合中的一个元素
     */
    public V getSetOne(String key) {
        if (getSet(key) != null && !getSet(key).isEmpty()) {
            return getSet(key).get(0);
        }
        return null;
    }

    /**
     * 批量存入缓存
     *
     * @param key    键
     * @param values 值
     * @param time   失效
     */
    public void lPushAll(String key, List<V> values, long time) {
        try {
            redisTemplate.opsForList().leftPushAll(key, values);
            if (time > 0) {
                expire(key, time);
            }
        } catch (Exception e) {
            log.error("批量存入redis失败:\n", e);
        }
    }

    /**
     * 哈希表加入
     *
     * @param key 键
     * @param hk  哈希表字段
     * @param hv  哈希表值
     */
    public void putHash(String key, String hk, Object hv) {
        try {
            redisTemplate.opsForHash().put(key, hk, hv);
        } catch (Exception e) {
            log.error("缓存哈希加入失败:\n", e);
        }
    }

    /**
     * 获取哈希表中指定字段的值
     *
     * @param key 键
     * @param hk  哈希表字段
     * @return 哈希表中指定字段的值
     */
    public Object getHash(String key, String hk) {
        try {
            return redisTemplate.opsForHash().get(key, hk);
        } catch (Exception e) {
            log.error("缓存哈希获取失败:\n", e);
        }
        return null;
    }

    /**
     * 获取键的剩余过期时间
     *
     * @param key 键
     * @return 剩余过期时间(秒)
     */
    public int ttl(String key) {
        Long t = redisTemplate.getExpire(key);
        t = t == null ? 0 : t;
        return Math.toIntExact(t);
    }
}

DataTool

package org.com.cloudCommon.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.com.cloudCommon.enums.ResultCode;
import org.com.cloudCommon.enums.ZeEnum;
import org.com.cloudCommon.exception.BusinessException;

import java.util.regex.Matcher;
import java.util.regex.Pattern;


@Slf4j
public class DataTool {
    public static void checkAffectRow(int result, String message) throws BusinessException {
        if (result == 0) {
            message = message.isEmpty() ? ResultCode.Fail.getMsg() : message;
            throw new BusinessException(ResultCode.Fail, message);
        }
    }

    public static boolean zeCheckStr(ZeEnum zeEnum, String str) {
        Pattern ze = Pattern.compile(zeEnum.getZe());
        Matcher matcher = ze.matcher(str);
        return matcher.matches();
    }

    public static String getUserId() {
        return 'U' + getRandomNumber(11);
    }

    public static String getRandomNumber(Integer count) {
        return RandomStringUtils.random(count, false, true);
    }
}

JwtUtil

package org.com.cloudCommon.utils;

import io.jsonwebtoken.*;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.com.cloudCommon.entity.UserInfo;
import org.com.cloudCommon.enums.ResultCode;
import org.com.cloudCommon.enums.RoleEnum;
import org.com.cloudCommon.exception.AuthException;
import org.com.cloudCommon.redis.RedisTool;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;


@Data
@NoArgsConstructor
public class JwtUtil<V> {
    /**
     * 签名密钥
     */
    private static final String secretKey = "J+/0Nr8/LeRpjIXuvhfbFz18RBp99/uHzg+26SPByks=";
    /**
     * token过期时间
     */
    private final Long tokenExpire = 2592000L;

    private final String role = "role";
    private final String userId = "userId";
    private final String nickName = "nickName";
    private RedisTool<V> redisTool;

    public JwtUtil(RedisTool<V> redisTool) {
        this.redisTool = redisTool;
    }

    /**
     * 生成token 默认30天时效
     */
    public String generateJwtToken(UserInfo userInfo) {
        return generateJwtToken(userInfo, tokenExpire);
    }

    /**
     * 生成token
     */
    public String generateJwtToken(UserInfo userInfo, long expirationMillis) {
        Date now = new Date();
        Date expirationDate = new Date(now.getTime() + expirationMillis);
        return Jwts.builder()
                // 头部
                .setHeaderParam("typ", "JWT")
                .setHeaderParam("alg", "HS256")
                //将用户的唯一标识作为主题设置到JWT令牌中
                .claim(role, userInfo.getRole())
                .claim(userId, userInfo.getUserId())
                .claim(nickName, userInfo.getNickName())
                //设置令牌的签发时间
                .setIssuedAt(now)
                //设置过期时间
                .setExpiration(expirationDate)
                .signWith(SignatureAlgorithm.HS256, secretKey + userInfo.getLastOffTime())
                .compact();
    }

    /**
     * 解析token
     */
    public Claims parseJwtToken(String token) {
        V offTme = redisTool.get(token);
        if (offTme == null) {
            throw new AuthException(ResultCode.CODE_10015);
        }
        // 创建JwtParser对象,用于解析JWT令牌
        JwtParserBuilder jwtParserBuilder = Jwts.parserBuilder();
        JwtParser jwtParser = jwtParserBuilder.setSigningKey(secretKey + offTme) //设置验证签名所使用的密钥(与生成JWT时使用的密钥相同)
                .build();
        // 解析JWT令牌并验证签名
        Jws<Claims> claimsJws = jwtParser.parseClaimsJws(token); // 解析JWT令牌
        return claimsJws.getBody();
    }

    /**
     * 抛出token异常
     */
    public Map<String, Object> checkTokenToClaims(String token, RoleEnum roleEnum) throws AuthException {
        if (token == null || token.isEmpty()) {
            throw new AuthException(ResultCode.CODE_10015);
        }
        Map<String, Object> map = new HashMap<>();
        try {
            Claims claims = parseJwtToken(token);
            map.put(role, claims.get(role));
            map.put(userId, claims.get(userId));
            map.put(nickName, claims.get(nickName));
            if (roleEnum == null) {
                return map;
            }
            switch (roleEnum) {
                case ADMIN:
                    if (!claims.get(role).equals(RoleEnum.ADMIN.getRole())) {
                        throw new AuthException(ResultCode.CODE_10016);
                    }
                    break;
                case USER:
                    if (!claims.get(role).equals(RoleEnum.USER.getRole())) {
                        throw new AuthException(ResultCode.CODE_10016);
                    }
                    break;
                default:
                    break;
            }
        } catch (Exception ignored) {
            redisTool.delete(token);
            throw new AuthException(ResultCode.CODE_900);
        }
        return map;
    }
}

Md5Util

package org.com.cloudCommon.utils;

import lombok.extern.slf4j.Slf4j;
import org.com.cloudCommon.enums.ResultCode;
import org.com.cloudCommon.exception.BusinessException;

import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Optional;


/**
 * 写一个MD5算法,运行结果与MySQL的md5()函数相同
 * 将明文密码转成MD5密码
 * 123456->e10adc3949ba59abbe56e057f20f883e
 */
@Slf4j
public class Md5Util {
    /**
     * 将明文密码转成MD5密码
     */
    public static String encodeByMd5(String string) {
        try {
            //Java中MessageDigest类封装了MD5和SHA算法
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            //调用MD5算法,即返回16个byte类型的值
            byte[] byteArray = md5.digest(string.getBytes());
            //注意:MessageDigest只能将String转成byte[],接下来的事情,由我们程序员来完成
            return byteArrayToHexString(byteArray);
        } catch (Exception e) {
            log.error("异常: ", e);
            return "fc5ae9ce578fa6bff964d0052e91b2d143";
        }
    }

    /**
     * 将byte[]转在16进制字符串
     */
    private static String byteArrayToHexString(byte[] byteArray) {
        StringBuilder sb = new StringBuilder();
        //遍历
        for (byte b : byteArray) {//16次
            //取出每一个byte类型,进行转换
            String hex = byteToHexString(b);
            //将转换后的值放入StringBuffer中
            sb.append(hex);
        }
        return sb.toString();
    }

    /**
     * 将byte转成16进制字符串
     */
    private static String byteToHexString(byte b) {//-31转成e1,10转成0a,。。。
        //将byte类型赋给int类型
        int n = b;
        //如果n是负数
        if (n < 0) {
            //转正数
            //-31的16进制数,等价于求225的16进制数
            n = 256 + n;
        }
        //商(14),数组的下标
        int d1 = n / 16;
        //余(1),数组的下标
        int d2 = n % 16;
        //通过下标取值
        return hex[d1] + hex[d2];
    }

    public static String Md5Method(String[] args) {
        Optional<String> reduce = Arrays.stream(args).sorted().reduce((a, b) -> a + b);
        if (reduce.isPresent()) {
            return encodeByMd5(reduce.get());
        }
        throw new BusinessException(ResultCode.BusinessError);
    }

    private static final String[] hex = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};

    public static void main(String[] args) {
        String g11942322362 = Md5Method(new String[]{"G15200011049"});
        System.out.println(g11942322362);
    }
}

启动类

package org.com.cloudCommon;

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


@SpringBootApplication
public class CloudCommonApplication {
    public static void main(String[] args) {
        SpringApplication springApplication = new SpringApplication(CloudCommonApplication.class);
        springApplication.setBannerMode(Banner.Mode.OFF);
        springApplication.run(args);
    }
}

5、cloud-gateway模块代码

项目结构

pom文件

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.com</groupId>
        <artifactId>cloud</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <artifactId>cloud-gateway</artifactId>
    <packaging>jar</packaging>

    <name>cloud-gateway</name>
    <description>cloud-gateway</description>

    <properties>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>org.com</groupId>
            <artifactId>cloud-common</artifactId>
            <version>0.0.1-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <encoding>${project.build.sourceEncoding}</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <configuration>
                    <mainClass>org.com.cloudGateway.CloudGatewayApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

yml文件

server:
    port: 9090
spring:
    application:
        name: gatewayService
    main:
        allow-bean-definition-overriding: true
    cloud:
        gateway:
            globalcors:
                corsConfigurations:
                    '[/**]':
                        allowedOrigins: "*"
                        allowedMethods:
                            - GET
                            - POST
                            - PUT
                            - DELETE
                        allowedHeaders: "*"
                        allowCredentials: true
        nacos:
            server-addr: localhost:8848
            discovery:
                cluster-name: HN
userService:
    ribbon: # 负载均衡配置
        NFLoadBalancerRuleClassName:
            com.alibaba.cloud.nacos.ribbon.NacosRule
ribbon:
    eager-load:
        enabled: true # 开启饥饿加载
        clients: # 指定饥饿加载的服务名
            - userService
            - socketService
gateway:
    adminPaths: # 角色认证路径
        - /api/userService/admin
    excludePaths: # 排除认证路径
        - /api/userService/public
        - /api/notifyService/notify
    authIp: # 认证服务地址
        http://127.0.0.1:9092
    authPath: # 认证服务路径
        /api/auth/check
    serviceNames: # 服务名列表
        - userService
    socketNames: # socket服务名列表
        - socketService

GatewayConfig

package org.com.cloudGateway.config;

import org.com.cloudGateway.filter.AuthenticationFilter;
import org.com.cloudGateway.pro.GatewayPro;
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;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.reactive.function.client.WebClient;

import javax.annotation.Resource;


@Configuration
public class GatewayConfig {
    @Resource
    private GatewayPro appContext;

    @Bean
    public AuthenticationFilter authenticationFilter(WebClient.Builder webClientBuilder) {
        return new AuthenticationFilter(webClientBuilder);
    }

    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder, AuthenticationFilter authenticationFilter) {
        RouteLocatorBuilder.Builder routes = builder.routes();
        for (String serviceName : appContext.getServiceNames()) {
            routes.route(serviceName, r -> r.path("/api/" + serviceName + "/**")
                    .filters(f ->
                            f.filter((exchange, chain) -> {
                                // 指定路径不需要认证
                                String path = exchange.getRequest().getPath().toString();
                                for (String excludePath : appContext.getExcludePaths()) {
                                    if (path.startsWith(excludePath)) {
                                        return chain.filter(exchange);
                                    }
                                }
                                return authenticationFilter.filter(exchange, chain);
                            }).filter((exchange, chain) -> {
                                // 获取原始路径
                                String originalPath = exchange.getRequest().getURI().getRawPath();
                                // 拆分路径段
                                String[] parts = originalPath.split("/");
                                // 构造新的路径,去掉第二段(索引为2,因为 parts[0] 是空字符串)
                                if (parts.length > 2) {
                                    StringBuilder newPath = new StringBuilder();
                                    newPath.append("/").append(parts[1]); // 保留第1段(如 "api")
                                    for (int i = 3; i < parts.length; i++) {
                                        newPath.append("/").append(parts[i]);
                                    }
                                    // 使用 mutate 创建新的 exchange 请求
                                    ServerHttpRequest newRequest = exchange.getRequest().mutate()
                                            .path(newPath.toString())
                                            .build();
                                    exchange = exchange.mutate().request(newRequest).build();
                                }
                                return chain.filter(exchange);
                            })
                    )
                    .uri("lb://" + serviceName));  // 此处为下游微服务的URI 这里使用 lb:// 前缀来启用负载均衡
        }
        for (String serviceName : appContext.getSocketNames()) {
            routes.route(serviceName, r -> r.path("/" + serviceName + "/**")
                    .filters(f -> f.stripPrefix(1))
                    .uri("lb://" + serviceName));
        }
        return routes.build();
    }
}

AuthenticationFilter

package org.com.cloudGateway.filter;

import com.alibaba.fastjson.JSONObject;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import lombok.extern.slf4j.Slf4j;
import org.com.cloudCommon.enums.ResultCode;
import org.com.cloudCommon.enums.RoleEnum;
import org.com.cloudCommon.model.R;
import org.com.cloudGateway.pro.GatewayPro;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


/**
 * @author 小牛
 * @version 1.0
 * &#064;description  认证过滤器
 * 用于验证用户身份和权限的过滤器,在请求到达微服务之前进行拦截。
 * &#064;date  2025/4/7 23:09
 */
@Component
@Slf4j
public class AuthenticationFilter implements GatewayFilter {
    @Resource
    private GatewayPro gatewayPro;

    // 在网关初始化时创建专用线程池
    private static final ExecutorService URL_ENCODING_EXECUTOR =
            Executors.newFixedThreadPool(
                    Runtime.getRuntime().availableProcessors() * 2,  // 通常CPU核数×2
                    new ThreadFactoryBuilder()
                            .setNameFormat("url-encoder-pool-%d")
                            .setDaemon(true)
                            .build()
            );
    private final WebClient.Builder webClientBuilder;

    public AuthenticationFilter(WebClient.Builder webClientBuilder) {
        this.webClientBuilder = webClientBuilder;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 获取请求头中的Authorization(JWT Token)
        List<String> token = exchange.getRequest().getHeaders().get("token");
        if (token == null || token.isEmpty() || token.get(0).isEmpty()) {
            return unauthorizedResponse(exchange, R.fail(ResultCode.CODE_10015));
        }
        String uri = gatewayPro.getAuthIp() + gatewayPro.getAuthPath() + "?" + "token" + "=" + token.get(0) + "&" + "role" + "=" + buildRoleEnum(exchange.getRequest().getPath().toString()).getRole();
        // 调用认证微服务验证Token的有效性
        return webClientBuilder.build()
                .get()
                .uri(uri)  // 调用认证微服务的验证接口
                .retrieve()
                .bodyToMono(R.class)
                .flatMap(R -> {
                    if (Objects.equals(R.getCode(), ResultCode.Success.getCode())) {
                        // Token有效,继续处理请求
                        Map<String, Object> data = (Map<String, Object>) R.getResult();
                        if (data != null) {
                            try {
                                exchange.getRequest().mutate()
                                        .header("userId", String.valueOf(data.get("userId")))
                                        .header("role", String.valueOf(data.get("role")))
                                        .header("nickName", asyncUrlEncode(String.valueOf(data.get("nickName"))).get()).build();
                            } catch (ExecutionException | InterruptedException e) {
                                log.error("URL编码失败", e);
                                return Mono.error(new RuntimeException(e));
                            }
                        }
                        return chain.filter(exchange);
                    } else {
                        return unauthorizedResponse(exchange, R);
                    }
                });
    }

    private Mono<Void> unauthorizedResponse(ServerWebExchange exchange, R R) {
// 创建响应的 JSON 内容
        String jsonResponse = JSONObject.toJSONString(R);
        // 将 JSON 转化为 ByteBuffer
        DataBufferFactory bufferFactory = exchange.getResponse().bufferFactory();
        DataBuffer dataBuffer = bufferFactory.wrap(jsonResponse.getBytes(StandardCharsets.UTF_8));
        // 设置响应头 Content-Type 为 application/json
        exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
        // 将 JSON 写入响应体
        return exchange.getResponse().writeWith(Mono.just(dataBuffer));
    }

    private RoleEnum buildRoleEnum(String path) {
        for (String adminPath : gatewayPro.getAdminPaths()) {
            if (path.startsWith(adminPath)) {
                return RoleEnum.ADMIN;
            }
        }
        return RoleEnum.USER;
    }

    // 使用CompletableFuture进行异步URL编码
    public CompletableFuture<String> asyncUrlEncode(String value) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                return URLEncoder.encode(value, StandardCharsets.UTF_8.name());
            } catch (UnsupportedEncodingException e) {
                log.error("URL编码失败", e);
                throw new RuntimeException(e);
            }
        }, URL_ENCODING_EXECUTOR); // 使用专用线程池
    }
}

GatewayPro

package org.com.cloudGateway.pro;

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


@Component
@Data
@ConfigurationProperties(prefix = "gateway")
public class GatewayPro {
    /**
     * admin路径
     */
    private String[] adminPaths;
    /**
     * 认证微服务路径
     */
    private String authPath;
    /**
     * 认证微服务ip地址
     */
    private String authIp;
    /**
     * 排除路径
     */
    private String[] excludePaths;
    /**
     * 服务名称列表
     */
    private String[] serviceNames;
    /**
     * 服务地址列表
     */
    private String[] socketNames;
}

启动类

package org.com.cloudGateway;

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


@SpringBootApplication
public class CloudGatewayApplication {
    public static void main(String[] args) {
        SpringApplication springApplication = new SpringApplication(CloudGatewayApplication.class);
        springApplication.setBannerMode(Banner.Mode.OFF);
        springApplication.run(args);
    }
}

6、cloud-user模块代码

项目结构

pom文件

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.com</groupId>
        <artifactId>cloud</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <artifactId>cloud-user</artifactId>
    <packaging>jar</packaging>

    <name>cloud-user</name>
    <description>cloud-user</description>

    <properties>

    </properties>

    <dependencies>
        <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-openfeign</artifactId>
        </dependency>
        <!--web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--验证参数-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        <!--数据库交互-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>
        <!-- 数据库-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.com</groupId>
            <artifactId>cloud-common</artifactId>
            <version>0.0.1-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <encoding>${project.build.sourceEncoding}</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <configuration>
                    <mainClass>org.com.cloudUser.CloudUserApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

yml文件

server:
    port: 9091
    servlet:
        context-path: /api
spring:
    application:
        name: userService
    main:
        allow-bean-definition-overriding: true
    mvc:
        throw-exception-if-no-handler-found: true
    profiles:
        active: dev
    datasource:
        url: jdbc:mysql://localhost:3306/neocalf?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&useSSL=false
        username: root
        password: LU960422
        driver-class-name: com.mysql.cj.jdbc.Driver
        hikari:
            pool-name: HikariCPDatasource
            minimum-idle: 5
            idle-timeout: 180000
            maximum-pool-size: 10
            auto-commit: true
            max-lifetime: 180000
            connection-timeout: 3000
            connection-test-query: SELECT 1
    redis:
        database: 0
        host: 127.0.0.1
        port: 6379
        jedis:
            pool:
                max-active: 20
                max-wait: -1
                max-idle: 10
                min-idle: 0
        timeout: 2000
    cloud:
        nacos:
            server-addr: localhost:8848
            discovery:
                cluster-name: HN
mybatis-plus:
    mapper-locations: classpath*:mapper/**/*.xml
    type-aliases-package: com.neoCalf.neoCommon.entity
    configuration:
        #开启驼峰命名
        map-underscore-to-camel-case: true
feign:
    client:
        config:
            default:
                connectTimeout: 20000
                readTimeout: 20000

CommonConfig

package org.com.cloudUser.config;

import lombok.extern.slf4j.Slf4j;
import org.com.cloudCommon.redis.RedisTool;
import org.com.cloudCommon.utils.JwtUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;


@Configuration
@Slf4j
public class CommonConfig<V> {
    @Bean
    public RedisTool<V> redisTool(RedisTemplate<String, V> redisTemplate) {
        return new RedisTool<>(redisTemplate);
    }

    @Bean("redisTemplate")
    public RedisTemplate<String, V> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, V> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        //设置key的序列化方式
        template.setKeySerializer(RedisSerializer.string());
        //设置value的序列化方式
        template.setValueSerializer(RedisSerializer.json());
        //设置hash的key的序列化方式
        template.setHashKeySerializer(RedisSerializer.string());
        //设置hash的value的序列化方式
        template.setHashValueSerializer(RedisSerializer.json());
        return template;
    }


    @Bean
    public JwtUtil<V> jwtUtil(RedisTool<V> redisTool) {
        return new JwtUtil<>(redisTool);
    }
}

ExceptionAdvice

package org.com.cloudUser.config;

import lombok.extern.slf4j.Slf4j;
import org.com.cloudCommon.enums.ResultCode;
import org.com.cloudCommon.exception.AuthException;
import org.com.cloudCommon.exception.BusinessException;
import org.com.cloudCommon.model.R;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.validation.ConstraintViolationException;


@Slf4j
@RestControllerAdvice
public class ExceptionAdvice {
    @ExceptionHandler(Exception.class)
    public R<String> handleException(Exception e) {
        log.error("系统异常", e);
        return R.fail(ResultCode.SystemError);
    }

    @ExceptionHandler(BusinessException.class)
    public R<String> BusinessException(BusinessException e) {
        log.error(e.getMessage());
        return R.fail(e.getResultCode());
    }

    @ExceptionHandler(AuthException.class)
    public R<String> AuthException(AuthException e) {
        log.error(e.getMessage());
        return R.fail(e.getResultCode());
    }


    @ExceptionHandler(NoHandlerFoundException.class)
    public R<String> NoHandlerFoundException(NoHandlerFoundException e) {
        log.error("请求路径不存在 {}", e.getMessage());
        return R.fail(ResultCode.NotFindError);
    }

    @ExceptionHandler(IllegalStateException.class)
    public R<String> IllegalStateException(IllegalStateException e) {
        log.error("参数错误 {}", e.getMessage());
        return R.fail(ResultCode.CODE_10017);
    }

    @ExceptionHandler(HttpMessageNotReadableException.class)
    public R<String> HttpMessageNotReadableException(HttpMessageNotReadableException e) {
        log.error("参数错误 {}", e.getMessage());
        return R.fail(ResultCode.CODE_10017);
    }

    @ExceptionHandler(IllegalArgumentException.class)
    public R<String> IllegalArgumentException(IllegalArgumentException e) {
        log.error("参数错误 {}", e.getMessage());
        return R.fail(ResultCode.CODE_10017);
    }

    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public R<String> HttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
        log.error("参数错误 {}", e.getMessage());
        return R.fail(ResultCode.CODE_10017);
    }

    @ExceptionHandler(ConstraintViolationException.class)
    public R<String> ConstraintViolationException(ConstraintViolationException e) {
        log.error("参数错误 {}", e.getMessage());
        return R.fail(ResultCode.CODE_10017);
    }

    @ExceptionHandler(ServletRequestBindingException.class)
    public R<String> ServletRequestBindingException(ServletRequestBindingException e) {
        log.error("参数错误 {}", e.getMessage());
        return R.fail(ResultCode.CODE_10017);
    }
}

LoginController

package org.com.cloudUser.controller;

import org.com.cloudCommon.entity.UserInfo;
import org.com.cloudCommon.model.R;
import org.com.cloudCommon.model.dto.TokenUserInfoDto;
import org.com.cloudUser.service.LoginService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.validation.constraints.NotNull;


@RestController
@RequestMapping("/public")
@Validated
public class LoginController {
    @Resource
    private LoginService loginService;

    @PostMapping("/login")
    public R<TokenUserInfoDto> login(@RequestBody @NotNull UserInfo userInfo) {
        TokenUserInfoDto tokenUserInfoDto = loginService.login(userInfo.getEmail(), userInfo.getPassword());
        return R.ok(tokenUserInfoDto);
    }

    @PostMapping("/register")
    public R<String> register(@RequestBody @NotNull UserInfo userInfo) {
        loginService.register(userInfo);
        return R.ok();
    }
}

UserInfoController

package org.com.cloudUser.controller;

import org.com.cloudCommon.entity.UserInfo;
import org.com.cloudCommon.model.R;
import org.com.cloudUser.service.UserInfoService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;


@RestController
@RequestMapping("/info")
@Validated
public class UserInfoController {
    @Resource
    private UserInfoService userInfoService;

    @GetMapping("/findUserInfo")
    public R<UserInfo> findUserInfo(HttpServletRequest request) {
        String userId = request.getHeader("userId");
        UserInfo userInfo = userInfoService.findUserInfo(userId);
        return R.ok(userInfo);
    }
}

AuthServiceFeign

package org.com.cloudUser.feigns;

import org.com.cloudCommon.entity.UserInfo;
import org.com.cloudCommon.model.R;
import org.jetbrains.annotations.NotNull;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;


/**
 * 本方法没有用到,只是为了方便理解Feign的使用。
 */
@FeignClient(value = "authService", path = "/api")
public interface AuthServiceFeign {

    /**
     * 获取token
     */
    @PostMapping(value = "/auth/token")
    R<String> getToken(@RequestBody @NotNull UserInfo userInfo);
}

UserInfoMapper

package org.com.cloudUser.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.com.cloudCommon.entity.UserInfo;


public interface UserInfoMapper extends BaseMapper<UserInfo> {
}

LoginServiceImpl

package org.com.cloudUser.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import lombok.extern.slf4j.Slf4j;
import org.com.cloudCommon.entity.UserInfo;
import org.com.cloudCommon.enums.*;
import org.com.cloudCommon.exception.BusinessException;
import org.com.cloudCommon.model.dto.TokenUserInfoDto;
import org.com.cloudCommon.utils.DataTool;
import org.com.cloudCommon.utils.JwtUtil;
import org.com.cloudCommon.utils.Md5Util;
import org.com.cloudUser.mapper.UserInfoMapper;
import org.com.cloudUser.service.LoginService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Date;


@Service(value = "loginService")
@Slf4j
public class LoginServiceImpl implements LoginService {
    @Resource
    private UserInfoMapper userInfoMapper;

    @Resource
    private JwtUtil<Long> jwtUtil;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void register(UserInfo userInfo) {
        String email = userInfo.getEmail();
        if (
                email == null ||
                        !DataTool.zeCheckStr(ZeEnum.ZE_USERNAME, userInfo.getNickName()) ||
                        !DataTool.zeCheckStr(ZeEnum.ZE_PASSWORD, userInfo.getPassword()) ||
                        !DataTool.zeCheckStr(ZeEnum.ZE_EMAIL, email)
        ) {
            throw new BusinessException(ResultCode.CODE_10017);
        }

        // 校验邮箱是否已注册
        LambdaQueryWrapper<UserInfo> userInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userInfoLambdaQueryWrapper.eq(UserInfo::getEmail, email);
        UserInfo user = userInfoMapper.selectOne(userInfoLambdaQueryWrapper);
        if (user != null) {
            throw new BusinessException(ResultCode.CODE_10017, "邮箱已注册");
        }

        userInfo.setUserId(DataTool.getUserId());
        userInfo.setRole(RoleEnum.USER.getRole());
        long date = new Date().getTime();
        userInfo.setJoinType(JoinTypeEnum.APPLY.getJoinType());
        userInfo.setStatus(UserStatusEnum.NORMAL.getType());
        userInfo.setCreateTime(date);
        userInfo.setPassword(Md5Util.encodeByMd5(userInfo.getPassword()));
        userInfo.setLastOffTime(date);
        int result = userInfoMapper.insert(userInfo);
        DataTool.checkAffectRow(result, "服务器出差了...");
    }

    @Override
    public TokenUserInfoDto login(String email, String password) {
        LambdaQueryWrapper<UserInfo> userInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userInfoLambdaQueryWrapper.eq(UserInfo::getEmail, email);
        userInfoLambdaQueryWrapper.eq(UserInfo::getPassword, Md5Util.encodeByMd5(password));
        UserInfo userInfo = userInfoMapper.selectOne(userInfoLambdaQueryWrapper);
        if (userInfo == null) {
            throw new BusinessException(ResultCode.CODE_10017, "账号或密码错误");
        }
        return commonLogin(userInfo);
    }

    public TokenUserInfoDto commonLogin(UserInfo userInfo) {
        // 修改最后一次登录时间
        LambdaUpdateWrapper<UserInfo> userInfoLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        userInfoLambdaUpdateWrapper.eq(UserInfo::getUserId, userInfo.getUserId());
        userInfoLambdaUpdateWrapper.set(UserInfo::getLastLoginTime, new Date().getTime());
        userInfoMapper.update(null, userInfoLambdaUpdateWrapper);
        return getTokenUserInfoDto(userInfo);
    }

    public TokenUserInfoDto getTokenUserInfoDto(UserInfo userInfo) {
        String token = jwtUtil.generateJwtToken(userInfo);
        return new TokenUserInfoDto(userInfo, token);
    }
}

UserInfoServiceImpl

package org.com.cloudUser.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.com.cloudCommon.entity.UserInfo;
import org.com.cloudUser.mapper.UserInfoMapper;
import org.com.cloudUser.service.UserInfoService;
import org.springframework.stereotype.Service;


@Service(value = "userInfoService")
@Slf4j
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {
    @Override
    public UserInfo findUserInfo(String userId) {
        return getById(userId);
    }
}

LoginService

package org.com.cloudUser.service;

import org.com.cloudCommon.entity.UserInfo;
import org.com.cloudCommon.model.dto.TokenUserInfoDto;


public interface LoginService {
    /**
     * 注册
     */
    void register(UserInfo userInfo);

    /**
     * 邮箱登录
     */
    TokenUserInfoDto login(String email, String password);
}

UserInfoService

package org.com.cloudUser.service;

import org.com.cloudCommon.entity.UserInfo;


public interface UserInfoService {
    /**
     * 根据用户id查询用户信息
     */
    UserInfo findUserInfo(String userId);
}

启动类

package org.com.cloudUser;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import static org.springframework.boot.Banner.Mode.OFF;


@EnableAsync //开启异步支持
@EnableAspectJAutoProxy //开启切片支持
@EnableTransactionManagement
@EnableFeignClients
@EnableDiscoveryClient
@MapperScan("org.com.cloudUser.mapper")
@SpringBootApplication
public class CloudUserApplication {
    public static void main(String[] args) {
        SpringApplication springApplication = new SpringApplication(CloudUserApplication.class);
        springApplication.setBannerMode(OFF);
        springApplication.run(args);
    }
}

7、父工程pom文件

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.com</groupId>
    <artifactId>cloud</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>cloud</name>
    <description>cloud</description>
    <packaging>pom</packaging>

    <modules>
        <module>cloud-auth</module>
        <module>cloud-gateway</module>
        <module>cloud-common</module>
        <module>cloud-user</module>
    </modules>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.12.RELEASE</version>
        <relativePath/>
    </parent>

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring-boot.version>2.3.12.RELEASE</spring-boot.version>
        <jwt-version>0.11.2</jwt-version>
        <redisson.version>3.12.3</redisson.version>
        <fastjson.version>1.2.66</fastjson.version>
        <spring-cloud.version>Hoxton.SR10</spring-cloud.version>
        <spring-cloud.alibaba.version>2.2.5.RELEASE</spring-cloud.alibaba.version>
        <mysql.version>8.0.23</mysql.version>
        <mysql-plus.version>3.5.2</mysql-plus.version>
        <hutool-all.version>4.6.3</hutool-all.version>
        <commons.lang3.version>3.4</commons.lang3.version>
        <commons.codec.version>1.9</commons.codec.version>
        <commons.io.version>2.5</commons.io.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--fastjson-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>${fastjson.version}</version>
        </dependency>
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>${redisson.version}</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!--工具-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>${hutool-all.version}</version>
        </dependency>
        <!--apache common-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>${commons.lang3.version}</version>
        </dependency>
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>${commons.codec.version}</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>${commons.io.version}</version>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--spring-cloud-alibaba管理依赖-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud.alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--数据库交互-->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>${mysql-plus.version}</version>
            </dependency>
            <!-- 数据库-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql.version}</version>
            </dependency>
            <!--jwt-->
            <dependency>
                <groupId>io.jsonwebtoken</groupId>
                <artifactId>jjwt-api</artifactId>
                <version>${jwt-version}</version>
            </dependency>
            <dependency>
                <groupId>io.jsonwebtoken</groupId>
                <artifactId>jjwt-impl</artifactId>
                <version>${jwt-version}</version>
                <scope>runtime</scope>
            </dependency>
            <dependency>
                <groupId>io.jsonwebtoken</groupId>
                <artifactId>jjwt-jackson</artifactId>
                <version>${jwt-version}</version>
                <scope>runtime</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <encoding>${project.build.sourceEncoding}</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <configuration>
                    <mainClass>org.cloud.CloudApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

 含大厂在职人员的交流社区,关注公众号 回复:大厂 。小牛 - 全栈工程师 | 个人官网

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐