# ants-parent
**Repository Path**: ly-springcloud-alibaba/ants-parent
## Basic Information
- **Project Name**: ants-parent
- **Description**: SpringCloud Alibaba
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2025-05-22
- **Last Updated**: 2025-07-31
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
### SpringCloudAlibaba框架的搭建
#### **1.技术选型**
Maven:3.6.3
持久层:mybatisplus
数据库:5.7
Springboot:2.7.18
Springcloudalibaba:2021.0.6.2
Nacos:2.5.1
Sentinel:1.8.6
#### **2.模块设计**
ants-parent: 父工程
ants-common: 公共模块
ants-gatewaty:网关模块
ants-oauth2-auth: 授权模块
ants-oauth2-gatewaty: 授权网关模块
ants-order: 订单模块
ants-product: 产品(库存)模块
ants-user: 用户模块
#### **3.基础准备**
为了提高依赖包的下载速度,推荐使用国内镜像下载,这里提供几个国内镜像,maven的setting.xml配置如下
```
alimaven
central
Aliyun Maven
https://maven.aliyun.com/repository/public
huaweicloud
central
Huawei Cloud Maven
https://repo.huaweicloud.com/repository/maven/
tencentcloud
central
tencent Cloud Maven
https://mirrors.cloud.tencent.com/maven/
wangyicloud
central
wangyi Cloud Maven
https://maven.163.com/repository/maven-public/
```
#### **4.框架搭建**
##### 4.1 创建父工程
创建maven工程,名称为ants-parent。pom.xml配置如下,各个版本对应的关系可查看该链接https://developer.aliyun.com/article/1532079
```
4.0.0
com.ants
ants-parent
1.0.0
pom
ants-parent
ants-common
ants-user
ants-product
ants-order
ants-gateway
ants-oauth2-auth
ants-oauth2-gateway
UTF-8
org.springframework.boot
spring-boot-starter-parent
2.7.18
org.springframework.boot
spring-boot-starter-log4j2
2.7.18
org.springframework.boot
spring-boot-starter-validation
2.7.18
org.springframework.boot
spring-boot-starter-aop
2.7.18
org.springframework.boot
spring-boot-starter-data-redis
2.7.18
io.netty
netty-common
io.netty
netty-handler
org.springframework
spring-context
io.netty
netty-common
4.1.118.Final
io.netty
netty-handler
4.1.118.Final
com.fasterxml.jackson.dataformat
jackson-dataformat-xml
2.18.0
com.fasterxml.jackson.core
jackson-databind
com.fasterxml.jackson.core
jackson-core
com.fasterxml.jackson.core
jackson-annotations
com.fasterxml.jackson.core
jackson-databind
2.18.0
com.fasterxml.jackson.core
jackson-core
com.fasterxml.jackson.core
jackson-annotations
com.fasterxml.jackson.core
jackson-core
2.18.0
com.fasterxml.jackson.core
jackson-annotations
2.18.0
com.alibaba
fastjson
1.2.83
net.sf.json-lib
json-lib
2.4
jdk15
commons-logging
commons-logging
commons-beanutils
commons-beanutils
commons-collections
commons-collections
org.aspectj
aspectjweaver
1.9.7
org.apache.commons
commons-lang3
3.17.0
com.mysql
mysql-connector-j
8.2.0
com.google.protobuf
protobuf-java
com.google.protobuf
protobuf-java
3.25.5
com.alibaba
druid-spring-boot-starter
1.2.20
org.springframework.boot
spring-boot-starter-security
2.7.18
org.springframework.cloud
spring-cloud-starter-oauth2
2.2.5.RELEASE
com.nimbusds
nimbus-jose-jwt
10.3
eu.bitwalker
UserAgentUtils
1.21
org.apache.httpcomponents
httpclient
4.5.13
org.apache.httpcomponents
httpmime
4.5.5
compile
cn.hutool
hutool-all
5.8.24
org.springframework.boot
spring-boot-starter-test
2.7.18
test
com.jayway.jsonpath
json-path
org.xmlunit
xmlunit-core
org.xmlunit
xmlunit-core
2.10.0
test
com.jayway.jsonpath
json-path
2.9.0
test
org.springframework.cloud
spring-cloud-dependencies
2021.0.9
pom
import
com.alibaba.cloud
spring-cloud-alibaba-dependencies
2021.0.6.2
pom
import
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-config
2021.0.6.2
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
2021.0.6.2
org.springframework.cloud
spring-cloud-starter-openfeign
4.1.4
org.springframework.cloud
spring-cloud-starter-loadbalancer
3.1.0
com.alibaba.cloud
spring-cloud-starter-alibaba-sentinel
2021.0.6.2
org.springframework.cloud
spring-cloud-starter-gateway
3.1.0
org.springframework.boot
spring-boot-starter-webflux
3.5.0
org.springframework.security
spring-security-oauth2-resource-server
5.7.11
org.springframework.security
spring-security-oauth2-client
5.7.11
org.springframework.security
spring-security-oauth2-jose
5.7.11
org.springframework.security
spring-security-config
5.7.11
```
##### 4.2 创建公共模块
###### 4.2.1新建module工程,命名为ants-common,pom.xml引入以下依赖
```
4.0.0
com.ants
ants-parent
1.0.0
ants-common
jar
ants-common
http://maven.apache.org
UTF-8
com.mysql
mysql-connector-j
8.2.0
com.google.protobuf
protobuf-java
com.google.protobuf
protobuf-java
3.25.5
com.alibaba
druid-spring-boot-starter
1.2.20
com.baomidou
mybatis-plus-boot-starter
3.5.5
com.github.pagehelper
pagehelper-spring-boot-starter
1.4.5
org.mybatis
mybatis
org.mybatis
mybatis-spring
org.aspectj
aspectjweaver
1.9.7
org.apache.commons
commons-lang3
org.springframework.boot
spring-boot-starter-validation
2.7.6
org.springframework.boot
spring-boot-starter-aop
com.fasterxml.jackson.dataformat
jackson-dataformat-xml
com.fasterxml.jackson.core
jackson-databind
com.fasterxml.jackson.core
jackson-core
com.fasterxml.jackson.core
jackson-annotations
ants-common
```
###### 4.2.2分别新建简单的实体类

a. Orers类
```
@TableId(value = "oid",type = IdType.AUTO)
private Integer oid;
/**
* 用户id
*/
private Integer uid;
private String username;
private Integer pid;
private String pname;
private String price;
private Integer number;
```
b. Product类
```
@TableId(value = "pid",type = IdType.AUTO)
private Integer pid;
private String pname;
private String price;
private Integer stock;
@JsonFormat(shape = JsonFormat.Shape.STRING,pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
```
c. User类
```
@TableId(value = "uid",type = IdType.AUTO)
private Integer uid;
private String username;
private String password;
private String phone;
@TableField(exist = false)
private List roles;
```
###### 4.2.3分别创建子模块ants-order、ants-product和ants-user服务
a. 这里我们以ants-user为例,该项目配置完成后,其他项目按照该方法配置即可,ants-user的pom.xml配置如下
```
4.0.0
com.ants
ants-parent
1.0.0
ants-user
8
8
UTF-8
com.ants
ants-common
1.0.0
compile
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-logging
org.springframework.boot
spring-boot-starter-log4j2
org.springframework.boot
spring-boot-starter-validation
org.springframework.boot
spring-boot-starter-aop
org.springframework.boot
spring-boot-starter-data-redis
2.7.18
io.netty
netty-common
4.1.118.Final
io.netty
netty-handler
4.1.118.Final
com.fasterxml.jackson.dataformat
jackson-dataformat-xml
com.fasterxml.jackson.core
jackson-databind
com.fasterxml.jackson.core
jackson-core
com.fasterxml.jackson.core
jackson-annotations
com.alibaba
fastjson
1.2.83
net.sf.json-lib
json-lib
jdk15
org.aspectj
aspectjweaver
com.mysql
mysql-connector-j
com.google.protobuf
protobuf-java
com.alibaba
druid-spring-boot-starter
io.springfox
springfox-swagger2
2.9.2
com.github.xiaoymin
swagger-bootstrap-ui
1.9.6
com.google.guava
guava
32.0.0-jre
com.github.whvcse
easy-captcha
1.6.2
com.google.code.gson
gson
2.9.0
eu.bitwalker
UserAgentUtils
1.21
org.apache.httpcomponents
httpclient
4.5.13
org.apache.httpcomponents
httpmime
4.5.5
compile
cn.hutool
hutool-all
5.8.24
org.springframework.boot
spring-boot-starter-test
test
org.xmlunit
xmlunit-core
test
com.jayway.jsonpath
json-path
test
ants-user
org.springframework.boot
spring-boot-maven-plugin
2.7.6
com.ants.user.UserApplicationStart
true
org.apache.maven.plugins
maven-compiler-plugin
3.13.0
1.8
1.8
${java.home}/lib/rt.jar${path.separator}${java.home}/lib/jce.jar
```
b. 资源文件配置
新建ants-user数据库以及user表,在resources目录下,新建application.yml和application-dev.yml文件,编写测试方法,比如根据id获取用户信息,并启动项目,测试是否能正常访问。
application.yml配置如下:
```
spring:
application:
#项目名,为了区分每个项目,这里注意不同的项目记得修改
name: ants-user
main:
allow-circular-references: true
profiles:
#使用dev环境
active: dev
```
application-dev.yml配置如下
```
#端口号
server:
port: 8081
servlet:
#项目路径
context-path: /ants-user-dev
#Mybatis配置.xml文件路径、模型路径
mybatis-plus:
mapper-locations: classpath*:/mapper/*Mapper.xml
type-aliases-package: com.ants.common.**
configuration:
map-underscore-to-camel-case: true
#sql打印
#控制台输出,方便调试
#log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
#日志文件输出
log-impl: org.apache.ibatis.logging.log4j2.Log4j2Impl
#Mybatis分页配置
pagehelper:
helperDialect: mysql
reasonable: true
supportMethodsArguments: true
params: count=countSql
#log4j2 日志配置
logging:
level:
root: info
com.alibaba.cloud.nacos: debug
# config: classpath:log4j2.xml
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.99.72:3306/ants_user?serverTimezone=Asia/Shanghai&useSSL=false&autoReconnect=true&useUnicode=true&characterEncoding=UTF-8
#验证连接的有效性,当连接空闲时,是否执行连接测试
username: root
password: root
druid:
#初始化大小,最小,最大
initial-size: 5
min-idle: 5
max-active: 20
#配置获取连接等待超时的时间
max-wait: 6000
#配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
time-between-eviction-runs-millis: 60000
#配置一个连接在池中最小生存的时间,单位是毫秒
min-evictable-idle-time-millis: 300000
validation-query: SELECT 1 FROM DUAL
test-while-idle: true
test-on-borrow: false
test-on-return: false
#打开PSCache,并且指定每个连接上PSCache的大小
pool-prepared-statements: true
max-pool-prepared-statement-per-connection-size: 20
#sql健监控filter
filter:
stat:
# 慢SQL记录
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
enabled: true
db-type: mysql
wall:
config:
multi-statement-allow: true
#WebStatFilter配置
web-stat-filter:
enabled: true
exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"
stat-view-servlet:
enabled: true
url-pattern: /druid/*
reset-enable: false
login-username: admin
login-password: 123456
redis:
#Redis数据库索引(默认为0)
database: 0
host: localhost
port: 6379
password:
#连接超时时间 2.0 中该参数的类型为Duration,这里在配置的时候需要指明单位
timeout: 6S
jedis:
pool:
#接池最大连接数(使用负值表示没有限制)
max-active: 200
#连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1
#连接池中的最大空闲连接
max-idle: 20
#连接池中的最小空闲连接
min-idle: 8
```
##### 4.3 **集成注册中心nacos**
我们同样以ants-user为例,首先下载nacos2.5.1,解压后,使用cmd命令进入到bin目录下,通过startup.cmd -m standalone命令启动nacos。如下启动成功,通过控制台给出的地址可访问。进入到内部后,点击新建命名空间,并新建ants-parent-dev空间名称。新建该命名空间是为了区分不同的环境,为后续开发做铺垫(主要是为了区分不同的环境,因为有些项目有开发、测试、生产)。

###### 4.3 .1改造ants-user
a.我们使用nacos为注册中心,并配置config,将项目中各种配置全部都放到一个集中的地方进行统一管理,并提供一套标准的接口,当各个服务需要获取配置的时候,就来配置中心的接口拉取自己的配置。 当配置中心中的各种参数有更新的时候,也能通知到各个服务实时的过来同步最新的信息,使之动态更新。

b.在子工程ants-user的pom.xml加入以下依赖并在启动类上加@EnableDiscoveryClient注解
```
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-config
```
c.application-dev.yml修改如下
```
#nacos注册中心的地址,如果有用户名和密码,记得要配置
nacos:
url: localhost:8848
#端口号
server:
port: 8081
#项目名称,每个项目都不一样
servlet:
context-path: /ants-user-dev
#以下是nacos的config配置
spring:
cloud:
nacos:
config:
server-addr: ${nacos.url}
#指定配置文件扩展名为yml
file-extension: yml
#配置的所属组
group: ANTS_GROUP
#命名空间 对应系nacos配置运行环境:dev|test|prod
namespace: ants-parent-${spring.profiles.active}
discovery:
server-addr: ${nacos.url}
#服务的命名空间
namespace: ants-parent-${spring.profiles.active}
#服务的所属组
group: ANTS_GROUP
config:
import:
- optional:nacos:${spring.application.name}-common-${spring.profiles.active}.yml # 公共配置
- optional:nacos:${spring.application.name}-database-${spring.profiles.active}.yml # mysql数据库开发环境
- optional:nacos:${spring.application.name}-redis-${spring.profiles.active}.yml # redis开发环境配置
```
4.3 .2 nacos注册中心增加配置
点击配置管理-->配置列表-->命名空间选择ants-parent-dev-->创建配置。
```
Data id :ants-user-common-dev.yml
Group: ANTS_GROUP
内容:
#Mybatis配置.xml文件路径、模型路径
mybatis-plus:
mapper-locations: classpath*:/mapper/*Mapper.xml
type-aliases-package: com.ants.common.**
configuration:
map-underscore-to-camel-case: true
#sql打印
#控制台输出,方便调试
#log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
#日志文件输出
log-impl: org.apache.ibatis.logging.log4j2.Log4j2Impl
#Mybatis分页配置
pagehelper:
helperDialect: mysql
reasonable: true
supportMethodsArguments: true
params: count=countSql
#log4j2 日志配置
logging:
level:
root: info
com.alibaba.cloud.nacos: debug
# config: classpath:log4j2.xml
--------------------------------分割线-------------------------
Data id : ants-user-redis-dev.yml
Group: ANTS_GROUP
内容:
spring:
redis:
#Redis数据库索引(默认为0)
database: 0
host: localhost
port: 6379
password:
#连接超时时间 2.0 中该参数的类型为Duration,这里在配置的时候需要指明单位
timeout: 6S
jedis:
pool:
#接池最大连接数(使用负值表示没有限制)
max-active: 200
#连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1
#连接池中的最大空闲连接
max-idle: 20
#连接池中的最小空闲连接
min-idle: 8
--------------------------------分割线-------------------------
Data id :ants-user-database-dev.yml
Group: ANTS_GROUP
内容:
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.99.72:3306/ants_user?serverTimezone=Asia/Shanghai&useSSL=false&autoReconnect=true&useUnicode=true&characterEncoding=UTF-8
#验证连接的有效性,当连接空闲时,是否执行连接测试
username: root
password: root
druid:
#初始化大小,最小,最大
initial-size: 5
min-idle: 5
max-active: 20
#配置获取连接等待超时的时间
max-wait: 6000
#配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
time-between-eviction-runs-millis: 60000
#配置一个连接在池中最小生存的时间,单位是毫秒
min-evictable-idle-time-millis: 300000
validation-query: SELECT 1 FROM DUAL
test-while-idle: true
test-on-borrow: false
test-on-return: false
#打开PSCache,并且指定每个连接上PSCache的大小
pool-prepared-statements: true
max-pool-prepared-statement-per-connection-size: 20
#sql健监控filter
filter:
stat:
# 慢SQL记录
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
enabled: true
db-type: mysql
wall:
config:
multi-statement-allow: true
#WebStatFilter配置
web-stat-filter:
enabled: true
exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"
stat-view-servlet:
enabled: true
url-pattern: /druid/*
reset-enable: false
login-username: admin
login-password: 123456
```
以上配置完成后,重新启动程序,在nacos管理界面,服务管理-->服务列表-->ants-parent-dev(命名空间)可查看注册的服务,并测试之前写的方法是否能正常访问,同理,ants-order和ants-product配置方式和步骤遵循ants-user的配置即可。
##### 4.4 基于Feign实现服务调用
Feign是Spring Cloud提供的一个声明式的伪Http客户端, 它使得调用远程服务就像调用本地服务一样简单, 只需要创建一个接口并添加一个注解即可。Nacos很好的兼容了Feign, Feign默认集成了 Ribbon, 所以在Nacos下使用Fegin默认就实现了负载均衡的效果。但是随着版本的更新,负载均衡被单独集成了一个依赖,因此要集成loadbalancer包,否则项目无法启动。
###### 4.4.1引入依赖
a.pom.xml加入依赖包,并在启动类上加入@EnableFeignClients注解,
```
org.springframework.cloud
spring-cloud-starter-openfeign
org.springframework.cloud
spring-cloud-starter-loadbalancer
```
b.在订单服务工程(ants-order)中,新建FeignProductService,通过商品id获取库存。模拟用户通过商品的id购买商品,并生成订单,在ants-product项目中,编写具体的实现方法和逻辑



c.分别启动订单服务和商品服务,通过url地址,模拟用户买了商品id为1的商品

##### 4.5 引入gateway实现网关服务
Spring Cloud Gateway是Spring公司基于Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。它的目标是替代Netflix Zuul,其不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控和限流。
所谓的API网关,就是指系统的**统一入口**,它封装了应用程序的内部结构,为客户端提供统一服务,一些与业务本身功能无关的公共逻辑可以在这里实现,诸如认证、鉴权、监控、路由转发等等
**优点:**
性能强劲:是第一代网关Zuul的1.6倍
功能强大:内置了很多实用的功能,例如转发、监控、限流等
设计优雅,容易扩展
**缺点:**
其实现依赖Netty与WebFlux,不是传统的Servlet编程模型,学习成本高
不能将其部署在Tomcat、Jetty等Servlet容器里,只能打成jar包执行
需要Spring Boot 2.0及以上的版本,才支持
###### 4.5.1 pom.xml配置
```
4.0.0
com.ants
ants-parent
1.0.0
ants-gateway
8
8
UTF-8
org.springframework.cloud
spring-cloud-starter-gateway
org.springframework.cloud
spring-cloud-starter-loadbalancer
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
```
###### 4.5.2 application.yml以及application-dev.yml配置
a.application.yml配置
```
spring:
application:
name: ants-gateway
main:
allow-circular-references: true
profiles:
active: dev
```
b.application-dev.yml配置
```
nacos:
url: localhost:8848
#端口号
server:
port: 7000
spring:
cloud:
nacos:
discovery:
server-addr: ${nacos.url}
#服务的命名空间
namespace: ants-parent-${spring.profiles.active}
#服务的所属组
group: ANTS_GROUP
gateway:
discovery:
locator:
enabled: true
# 路由数组[路由 就是指定当请求满足什么条件的时候转到哪个微服务]
routes:
- id: product_route
uri: lb://ants-product # 请求要转发到的地址
predicates: # 断言(就是路由转发要满足的条件)
- Path=/ants-product-dev/** # 当请求路径满足Path指定的规则时,转发到product服务
- id: order_route
uri: lb://ants-order # 请求要转发到的地址
predicates: # 断言(就是路由转发要满足的条件)
- Path=/ants-order-dev/** # 当请求路径满足Path指定的规则时,转发到order服务
```
4.5.3 测试
分别启动ants-order、ants-product、ants-gateway服务,通过网关进行访问,测试结果如下,这样通过统一的入口,就能访问到不同服务

##### 4.6 集成oauth2
a.新建ants-oauth2-auth项目,引入依赖
```
4.0.0
com.ants
ants-parent
1.0.0
ants-oauth2-auth
8
8
UTF-8
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-security
org.springframework.cloud
spring-cloud-starter-oauth2
com.nimbusds
nimbus-jose-jwt
org.springframework.boot
spring-boot-starter-data-redis
2.7.18
io.netty
netty-common
4.1.118.Final
io.netty
netty-handler
4.1.118.Final
org.springframework.cloud
spring-cloud-starter-loadbalancer
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
```
b.使用java自带的命令生成密钥库,进入到java的bin目录下,使用keytool命令生成
```
keytool -genkeypair -alias jwt -keyalg RSA -keystore jwt.jks -validity 365 -storetype JKS
```
命令解读:
-genkeypair 用于生成密钥对。
-alias jwt 指定密钥对的别名为jwt
-keyalg RSA 指定密钥算法为RSA。
-keystore jwt.jks 指定输出文件名为jwt.jks。
-validity 365 指定证书有效期为365天。
-storetype JKS 指定密钥库类型为JKS。
-keysize 2048 指定密钥长度,可以不用设置,使用默认
输入以上命令之后,要求你输入钥匙库的口令,输入口令之后下面的都默认,知道最后输入y,结束后在bin目录下,会有一个jwt.jks文件,将文件复制到该项目的resources目录下即可

c.application.yml文件配置
```
spring:
application:
name: ants-oauth2-auth
main:
allow-circular-references: true
profiles:
active: dev
```
d.application-dev.yml文件配置
```
nacos:
url: localhost:8848
#端口号
server:
port: 8083
spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848
#服务的命名空间
namespace: ants-parent-${spring.profiles.active}
#服务的所属组
group: ANTS_GROUP
redis:
#Redis数据库索引(默认为0)
database: 0
host: localhost
port: 6379
password:
#连接超时时间 2.0 中该参数的类型为Duration,这里在配置的时候需要指明单位
timeout: 6S
jedis:
pool:
#接池最大连接数(使用负值表示没有限制)
max-active: 200
#连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1
#连接池中的最大空闲连接
max-idle: 20
#连接池中的最小空闲连接
min-idle: 8
```
e.以下是java程序相关配置
- 新建Tokenconfig,这里我使用了redis为载体,生成的token都在redis中保存
```
@Configuration
public class TokenConfig {
@Autowired(required = false)
private RedisConnectionFactory redisConnectionFactory;
@Bean
public TokenStore tokenStore() {
return new RedisTokenStore(redisConnectionFactory);
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setKeyPair(keyPair()); //对称秘钥,资源服务器使用该秘钥来验证
return converter;
}
@Bean
public KeyPair keyPair() {
//从classpath下的证书中获取秘钥对
//jwk.jks为刚刚我们生成的密钥库文件名称,changeit为密钥库的密码
KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("jwt.jks"),"changeit".toCharArray());
//jwt 为生产密钥库时指定的别名,changeit为密码
return keyStoreKeyFactory.getKeyPair("jwt", "changeit".toCharArray());
}
}
```
- 新建JwtTokenEnhancer类,实现TokenEnhancer接口,增强JWT的内容,往生成的token中加入用户的id,其他的根据需要添加。
```
@Component
public class JwtTokenEnhancer implements TokenEnhancer {
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) {
SecurityUser securityUser = (SecurityUser) oAuth2Authentication.getPrincipal();
Map info = new HashMap<>();
//把用户ID设置到JWT中
info.put("id", securityUser.getUid());
((DefaultOAuth2AccessToken) oAuth2AccessToken).setAdditionalInformation(info);
return oAuth2AccessToken;
}
}
```
- 配置oauth2服务
```
@Configuration
@EnableAuthorizationServer
public class OAuth2ServerConfig extends AuthorizationServerConfigurerAdapter {
private static final String DEMO_RESOURCE_ID = "order";
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private TokenStore tokenStore;
@Autowired
private JwtAccessTokenConverter accessTokenConverter;
@Autowired
private UserServiceImpl userServiceDetail;
@Autowired
private JwtTokenEnhancer jwtTokenEnhancer;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//配置客户端,password认证,这里设置客户端的密码为123456
clients.inMemory().withClient("client_1")
.resourceIds(DEMO_RESOURCE_ID)
.authorizedGrantTypes("password", "refresh_token")
.scopes("all")
//.secret是为了判断传过来的值和这里设置的要相等
//这里我使用了new BCryptPasswordEncoder,是因为 WebSecurityConfig 中配置了
//因此为了密码校验通过,必须使用同等的方法进行加密后判断
.secret(new BCryptPasswordEncoder().encode(("123456")))
//设置 client_1 令牌过期时间秒 2个小时,refreshTokenValiditySeconds设置的是刷新时间
.accessTokenValiditySeconds(60 * 60 * 2)
.refreshTokenValiditySeconds(60 * 60 * 5);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.tokenServices(tokenService())
.userDetailsService(userServiceDetail)
.authenticationManager(authenticationManager);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.allowFormAuthenticationForClients();
}
@Bean
public AuthorizationServerTokenServices tokenService() {
DefaultTokenServices service = new DefaultTokenServices();
service.setSupportRefreshToken(true);
service.setTokenStore(tokenStore);
//令牌增强
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
List tokenEnhancers = new ArrayList<>();
tokenEnhancers.add(jwtTokenEnhancer);
tokenEnhancers.add(accessTokenConverter);
tokenEnhancerChain.setTokenEnhancers(tokenEnhancers);
service.setTokenEnhancer(tokenEnhancerChain);
service.setAccessTokenValiditySeconds(60 * 60 * 2); // 令牌默认有效期2小时
service.setRefreshTokenValiditySeconds(60 * 60 * 5); // 刷新令牌默认有效期5小时
return service;
}
}
```
- 其中UserServiceImpl 类,是我们的实现类,这里应该从数据库中获取是否有用户,以及用户所具有的权限,为了方便,我们暂时不从数据库中读取,使用全局变量,以及初始化时赋值。
```
@Service
public class UserServiceImpl implements UserDetailsService {
private List userList;
//程序启动时,往全局变量userList塞入用户,以及用户所具有的权限
@PostConstruct
public void initData() {
String password = new BCryptPasswordEncoder().encode("123456");
userList = new ArrayList<>();
userList.add(new UserDto(1,"macro", password,"18698589620", Arrays.asList("ROLE_ADMIN")));
userList.add(new UserDto(2,"andy", password,"18698589620", Arrays.asList("ROLE_TEST","ROLE_USER")));
}
//重写loadUserByUsername方法,根据传入的用户名查询是否有该用户
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
List findUserList = userList.stream()
.filter(item -> item.getUsername().equals(username)).collect(Collectors.toList());
if (findUserList.isEmpty()) {
throw new UsernameNotFoundException("用户名或密码错误!");
}
UserDto user = findUserList.get(0);
List authorities = user.getRoles().stream()
.map(SimpleGrantedAuthority::new).collect(Collectors.toList());
SecurityUser securityUser = getSecurityUser(user, authorities);
return securityUser;
}
private static SecurityUser getSecurityUser(UserDto user, List authorities) {
SecurityUser securityUser = new SecurityUser(user.getUsername(), user.getPassword(), authorities);
if (!securityUser.isEnabled()) {
throw new DisabledException("账户已禁用");
} else if (!securityUser.isAccountNonLocked()) {
throw new LockedException("账户已锁定");
} else if (!securityUser.isAccountNonExpired()) {
throw new AccountExpiredException("用户账户已经过期");
} else if (!securityUser.isCredentialsNonExpired()) {
throw new CredentialsExpiredException("用户凭证已过期");
}
securityUser.setUid(user.getUid());
securityUser.setUsername(user.getUsername());
return securityUser;
}
}
```
- 最后WebSecurityConfig配置如下
```
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
//将oauth开头的请求以及/rsa/publicKey请求不拦截
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll()
.antMatchers("/rsa/publicKey").permitAll()
.antMatchers("/oauth/**").permitAll()
.anyRequest().authenticated();
}
//AuthenticationManager 授权管理器
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
//密码加密的方式
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
```
这样我们就完成了oauth2的项目,启动ants-oauth2-auth服务,并请求url获取token
参数说明如下:
username:为需要授权的用户名,就是UserServiceImpl 中的配置
password:为用户名的密码
grant_type: 授权类型为密码,OAuth2ServerConfig类中的authorizedGrantTypes
client_id: OAuth2ServerConfig的withClient配置
scope: OAuth2ServerConfig的scopes配置
client_secret: 客户端的密码,OAuth2ServerConfig的secret配置,如果不想使用明文,可以前端使用加密配置,后端secret使用解密配置即可。

##### 4.7 oauth2和gateway集成
###### 4.7.1 基础文件配置
a.新建ants-oauth2-gateway项目,子项目pom.xml配置如下
```
4.0.0
com.ants
ants-parent
1.0.0
ants-oauth2-gateway
8
8
UTF-8
org.springframework.cloud
spring-cloud-starter-gateway
org.springframework.cloud
spring-cloud-starter-loadbalancer
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
org.springframework.boot
spring-boot-starter-webflux
org.springframework.security
spring-security-oauth2-resource-server
org.springframework.security
spring-security-oauth2-client
org.springframework.security
spring-security-oauth2-jose
org.springframework.security
spring-security-config
com.nimbusds
nimbus-jose-jwt
org.springframework.boot
spring-boot-starter-data-redis
2.7.18
io.netty
netty-common
4.1.118.Final
io.netty
netty-handler
4.1.118.Final
com.alibaba
fastjson
1.2.83
```
b.application.yml配置如下
```
spring:
application:
name: ants-oauth2-gateway
main:
allow-circular-references: true
profiles:
active: dev
```
c.application-dev.yml配置如下
```
nacos:
url: localhost:8848
#端口号
server:
port: 7000
spring:
cloud:
nacos:
discovery:
server-addr: ${nacos.url}
#服务的命名空间
namespace: ants-parent-${spring.profiles.active}
#服务的所属组
group: ANTS_GROUP
gateway:
discovery:
locator:
#开启从注册中心动态创建路由的功能
enabled: true
##使用小写服务名,默认是大写
lower-case-service-id: true
# 路由数组[路由 就是指定当请求满足什么条件的时候转到哪个微服务]
routes:
- id: ants-product-route
uri: lb://ants-product # 请求要转发到的地址
predicates: # 断言(就是路由转发要满足的条件)
- Path=/ants-product-dev/** # 当请求路径满足Path指定的规则时,才进行路由转发
- id: ants-oauth2-route
uri: lb://ants-oauth2
predicates:
- Path=/**
security:
oauth2:
resourceserver:
jwt:
jwk-set-uri: 'http://localhost:8083/rsa/publicKey' #配置RSA的公钥访问地址
```
###### 4.7.2 程序设计
a.上述基础配置文件配置好后,需要在ants-oauth2-auth项目中,编写RSA钥匙库地址,供gateway访问
```
@RestController
public class KeyPairController {
@Resource
private KeyPair keyPair;
@GetMapping("/rsa/publicKey")
public Map getKey() {
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAKey key = new RSAKey.Builder(publicKey).build();
return new JWKSet(key).toJSONObject();
}
}
```
b.编写请求授权管理器类RestfulAuthorizationManager
```
@Component
public class RestfulAuthorizationManager implements ReactiveAuthorizationManager {
Logger logger = LoggerFactory.getLogger(RestfulAuthorizationManager.class);
@Autowired
private RedisTemplate redisTemplate;
private static final String GATEWAY_RESOURCE_ROLE_KEY = "gateway:resource";
@Override
public Mono check(Mono authentication, AuthorizationContext authorizationContext) {
ServerWebExchange exchange = authorizationContext.getExchange();
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();
//从redis中获取事先已经配置好的授权路由
Object obj = redisTemplate.opsForList().range(GATEWAY_RESOURCE_ROLE_KEY,0,-1);
// 不在权限范围内的url,全部拒绝
if (obj == null) {
return Mono.just(new AuthorizationDecision(false));
}
String redisMapStr = JSON.toJSONString(obj);
List