This commit is contained in:
yzy 2024-03-14 11:57:05 +08:00
commit 583ca60498
1788 changed files with 314935 additions and 0 deletions

21
.editorconfig Normal file
View File

@ -0,0 +1,21 @@
# http://editorconfig.org
root = true
# 空格替代Tab缩进在各种编辑工具下效果一致
[*]
indent_style = space
indent_size = 4
charset = utf-8
end_of_line = crlf
trim_trailing_whitespace = true
insert_final_newline = true
[*.java]
indent_style = tab
[*.{json,yml,yaml}]
indent_size = 2
[*.md]
insert_final_newline = false
trim_trailing_whitespace = false

27
.gitignore vendored Normal file
View File

@ -0,0 +1,27 @@
# maven #
target
logs
# windows #
Thumbs.db
# Mac #
.DS_Store
# eclipse #
.settings
.project
.classpath
.log
*.class
# idea #
.idea
*.iml
# Package Files #
*.jar
*.war
*.ear
/target

1
README.md Normal file
View File

@ -0,0 +1 @@
## 安防信息管理系统

BIN
doc/nacos/config/dev.zip Normal file

Binary file not shown.

View File

@ -0,0 +1,119 @@
#spring配置
spring:
redis:
##redis 单机环境配置
host: http://127.0.0.1/
port: 6379
timeout: 10000
password: P@ssw0rd_redis
database: 0
ssl: false
##redis 集群环境配置
#cluster:
# nodes: 47.108.70.243:7001,47.108.70.243:7002,47.108.70.243:7003
# commandTimeout: 5000
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
druid:
validation-query: select 1
url: jdbc:mysql://36.147.33.60:13306/security?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&tinyInt1isBit=false&allowMultiQueries=true&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
username: root
password: P@ssw0rd_mysql
#项目模块集中配置
blade:
esbUrl: 197.52.19.89
esbPort: 39245
#分布式锁配置
lock:
enabled: false
address: redis://47.108.70.243:6379
#多团队协作服务配置
ribbon:
rule:
#开启配置
enabled: false
#负载均衡优先调用的ip段
prior-ip-pattern:
- 192.168.0.*
- 47.108.70.243
#通用开发生产环境数据库地址(特殊情况可在对应的子工程里配置覆盖)
datasource:
dev:
url: jdbc:mysql://36.147.33.60:13306/security?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&tinyInt1isBit=false&allowMultiQueries=true&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
username: root
password: P@ssw0rd_mysql
#
# Emb(消息总线请求参数)配置 -sit
sub:
brokerServiceurl: pulsar://198.69.20.7:6000
accessToken: eyJrZXlJZCI6InB1bHNhci13ejlwMmptYnBuIiwiYWxnIjoiSFMyNTYifQ.eyJzdWIiOiJwdWxzYXItd3o5cDJqbWJwbl9TSU1TIn0.ibMqmjqOJkNMWsxBJj-zLbGsG3VY5gX74G3MTIkMIHQ
subscriptionName: SIMS
corePoolSize: 1
evnValue: pulsar-wz9p2jmbpn
listenerName: pulsar-wz9p2jmbpn/vpc-espi8lwv/subnet-6o1k8cm0
topicNames:
- pulsar-wz9p2jmbpn/sso-common-info/M01010001
- pulsar-wz9p2jmbpn/sso-common-info/M01010002
- pulsar-wz9p2jmbpn/sso-common-info/M01030006
maximumPoolSize: 300
deptLeaderName: 机构负责人 #行政机构信息同步的机构负责人对应的角色名称
#security-desk-yml配置文件
#影像平台
images:
local_path: E:\安防信息化项目\uploadFile\ #本地存放文件路径
service_path: /home/test/apps/file/ # 服务器存放文件路径
DM: HTBANK
ip: 197.52.21.92 #为影像平台的ip地址
socketPort: 8023 #为影像平台的socket端口
httpPort: 9080 #为影像平台的http端口
aimChannel: SIMS #渠道号
modelCode: SIMS #索引对象内容模型代码
filePartName: SIMS_PART #文档对象模型代码
userName: sims #登录影像平台的用户名
passWord: sims_admin #登录影像平台的密码
xyun:
app-id: 500000053
app-secret: B6rbJdILa1VEO9fmn6zj
url: http://197.52.23.10
# 公共号id
pub-id: XT-cb5053a4-3d00-47e7-a0c3-d0ac693305b7
pub-secret: 812739e21bf781370bebec37146c38a8
# 企业注册号eid
e-id: 1556583279
# 单点登录配置参数
single:
auth-url: http://bam.sit.ynhtbank.com:9080/idp/oauth2/authorize #单点登录url
client-id: SIMS #应用系统ID
redirect-uri: http://197.52.29.131:8888/api/security-desk/authorize #重定向url
response-type: code #响应类型
client-secret: 3ac76afc760f4c8b9265992e28c924df
auth-res-url: http://197.52.19.89:39245/SIMS #单点登录授权url
index-url: http://197.52.29.131:8888/#/single-login #首页地址
scvcd: 30430034 #上esb获取用户信息的服务码
user-url: http://197.52.29.131:8000/security-auth/oauth/token
chnl-tp: 41022
#secutity-yml配置文件
#导出文件的存储路径
export:
excel:
path: D:\安全管理信息化平台\excel导出测试\
#swagger包扫描路径
swagger:
base-packages: org.security.trans
#通用配置参数
common:
projectRoleName: 安全员 #项目预算管理-项目结项后可以对结项项目进行操作的指属上一级机构的角色名称
securityRoleName: 安全员 #安保指挥中心非现场检查整改通知书通知机构的角色
payPlanRemindKeyName: payPlanRemindDay #付款计划提醒提前天数字典配置键值
concatDeadlineRemindKeyName: concatDeadlineRemindDay #合同到期提醒提前天数字典配置键值
service_path: /home/test/apps/file/ # 服务器存放文件路径-用于附件删除

View File

@ -0,0 +1,39 @@
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
#driver-class-name: org.postgresql.Driver
#driver-class-name: oracle.jdbc.OracleDriver
#driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
druid:
# MySql、PostgreSQL、SqlServer校验
#validation-query: select 1
# Oracle校验
#validation-query: select 1 from dual
#项目模块集中配置
blade:
#工作流模块开发生产环境数据库地址
datasource:
flow:
dev:
# MySql
url: jdbc:mysql://36.147.33.60:13306/security?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&tinyInt1isBit=false&allowMultiQueries=true&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
username: root
password: P@ssw0rd_mysql
# PostgreSQL
#url: jdbc:postgresql://127.0.0.1:5432/bladex_flow
#username: postgres
#password: 123456
# Oracle
#url: jdbc:oracle:thin:@127.0.0.1:1521:orcl
#username: BLADEX_FLOW
#password: BLADEX_FLOW
# SqlServer
#url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=bladex_flow
#username: bladex_flow
#password: bladex_flow
#准生产
# dev:
# url: jdbc:oceanbase://oceanbase.qprod.htb.srv:2883/siszsc?useUnicode=true&characterEncoding=utf-8&mode=mysql&rewriteBatchedStatements=true&socketTimeout=6000000&connectTimeout=60000&allowMultiQueries=true&useLocalSessionState=true
# username: htb_prod_obcluster:siszsc:secretiy
# password: SIS_pswd@123

View File

@ -0,0 +1,111 @@
#服务器配置
server:
undertow:
# 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理
buffer-size: 1024
# 是否分配的直接内存
direct-buffers: true
# 线程配置
threads:
# 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程
io: 16
# 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载
worker: 400
#spring配置
spring:
cloud:
sentinel:
eager: true
devtools:
restart:
log-condition-evaluation-delta: false
livereload:
port: 23333
servlet:
multipart:
location: /home/test/apps/undertow/tmp
#feign配置
feign:
sentinel:
enabled: true
okhttp:
enabled: true
httpclient:
enabled: true
hystrix:
enable: true
client:
config:
default:
readTimeout: 120000
connectTimeout: 3000
#对外暴露端口
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: always
#knife4j配置
knife4j:
#启用
enable: true
#基础认证
basic:
enable: false
username: blade
password: blade
#增强配置
setting:
enableSwaggerModels: true
enableDocumentManage: true
enableHost: false
enableHostText: http://localhost
enableRequestCache: true
enableFilterMultipartApis: false
enableFilterMultipartApiMethodType: POST
language: zh-CN
enableFooter: false
enableFooterCustom: true
footerCustomContent: Copyright © 2021 SpringBlade All Rights Reserved
#swagger配置信息
swagger:
title: SpringBlade 接口文档系统
description: SpringBlade 接口文档系统
version: 3.0.3
license: Powered By SpringBlade
licenseUrl: https://bladex.vip
terms-of-service-url: https://bladex.vip
contact:
name: smallchill
email: smallchill@163.com
url: https://gitee.com/smallc
#blade配置
blade:
xss:
enabled: true
skip-url:
- /weixin
secure:
skip-url:
- /test/**
client:
- client-id: sword
path-patterns:
- /sword/**
- client-id: saber
path-patterns:
- /saber/**
tenant:
column: tenant_id
tables:
- blade_notice

View File

@ -0,0 +1,125 @@
#spring配置
spring:
redis:
##redis 单机环境配置
host: 197.32.39.34
port: 16379
timeout: 10000
password:
database: 0
ssl: false
##redis 集群环境配置
#cluster:
# nodes: 47.108.70.243:7001,47.108.70.243:7002,47.108.70.243:7003
# commandTimeout: 5000
datasource:
driver-class-name: com.alipay.oceanbase.jdbc.Driver
druid:
validation-query: select 1
url: jdbc:oceanbase://obap.htb.srv:2883/sis?useUnicode=true&characterEncoding=utf-8&mode=mysql&rewriteBatchedStatements=true&socketTimeout=6000000&connectTimeout=60000&allowMultiQueries=true&useLocalSessionState=true
username: htb_ap_obcluster:sis:secretiy
password: SECRETIY_P@ssw0rd@123
#项目模块集中配置
blade:
esbUrl: esb.htb.srv
esbPort: 39245
face:
virtualTelNo: C00074
leadDeptNo: 12023
telIdentTp: HTBK
#分布式锁配置
lock:
enabled: false
address: redis://197.32.39.34:6379
#多团队协作服务配置
ribbon:
rule:
#开启配置
enabled: true
#负载均衡优先调用的ip段
prior-ip-pattern:
- 192.168.0.*
- 197.32.39.34
#通用开发生产环境数据库地址(特殊情况可在对应的子工程里配置覆盖)
datasource:
prod:
url: jdbc:oceanbase://obap.htb.srv:2883/sis?useUnicode=true&characterEncoding=utf-8&mode=mysql&rewriteBatchedStatements=true&socketTimeout=6000000&connectTimeout=60000&allowMultiQueries=true&useLocalSessionState=true
username: htb_ap_obcluster:sis:secretiy
password: SECRETIY_P@ssw0rd@123
#security-busi-yml配置
#通用配置参数
common:
projectRoleName: 安全员 #项目预算管理-项目结项后可以对结项项目进行操作的指属上一级机构的角色名称
securityRoleName: 安全员 #安保指挥中心非现场检查整改通知书通知机构的角色
payPlanRemindKeyName: payPlanRemindDay #付款计划提醒提前天数字典配置键值
concatDeadlineRemindKeyName: concatDeadlineRemindDay #合同到期提醒提前天数字典配置键值
service_path: /home/sis/apps/file/ # 服务器存放文件路径
#导出文件的存储路径
export:
excel:
path: D:\云南红塔银行安保项目\excel导出测试\
appOauth:
accessTokenUrl: http://197.52.23.10/gateway/oauth2/token/getAccessToken
userContextUrl: http:/197.52.23.10/gateway/ticket/user/acquirecontext
appIndexUrl: http://197.52.29.131:8889/index.html
appId: SP1556583279
secret: 111
#security-desk-yml配置信息
# Emb(消息总线请求参数)配置 -sit
sub:
brokerServiceurl: pulsar://198.52.11.2:6000
accessToken: eyJrZXlJZCI6InB1bHNhci1yd21hNW84dzVxIiwiYWxnIjoiSFMyNTYifQ.eyJzdWIiOiJwdWxzYXItcndtYTVvOHc1cV9TSU1TIn0.-0N6b4ebRDjsU7eVVbxXg6ft4ZeA_asrxR30zMDAyhc
subscriptionName: SIMS
corePoolSize: 1
evnValue: pulsar-rwma5o8w5q
listenerName: pulsar-rwma5o8w5q/vpc-8yjhs80x/subnet-druh4gye
topicNames:
- pulsar-rwma5o8w5q/sso-common-info/M01010001
- pulsar-rwma5o8w5q/sso-common-info/M01010002
- pulsar-rwma5o8w5q/sso-common-info/M01030006
maximumPoolSize: 300
deptLeaderName: 机构负责人 #行政机构信息同步的机构负责人对应的角色名称
#影像平台
images:
# local_path: E:\安防信息化项目\uploadFile\ #本地存放文件路径
service_path: /home/sis/apps/file/ # 服务器存放文件路径
DM: HTBANK
ip: nicmp.htb.srv #准生产影像平台的ip地址
socketPort: 8023 #为影像平台的socket端口
httpPort: 9080 #为影像平台的http端口
aimChannel: SIMS #渠道号
modelCode: SIMS #索引对象内容模型代码
filePartName: SIMS_PART #文档对象模型代码
userName: sims #登录影像平台的用户名
passWord: F8upgG29 #登录影像平台的密码
xyun:
app-id: 500000145
app-secret: pzWlUBKsGQZFgIRWwDvV
url: http://im.ynhtbank.cn
# 公共号id
pub-id: XT-d5a6d607-3549-465a-8477-601fc366039d
pub-secret: 7e37f7ef5653889013060bfaaa69ccf7
# 企业注册号eid
e-id: 15750331
# 单点登录配置参数
single:
auth-url: http://bamsso.htb.com:9080/idp/oauth2/authorize #单点登录url
client-id: SIMS #应用系统ID
redirect-uri: http://197.32.39.34:8888/api/security-desk/authorize #重定向url
response-type: code #响应类型
client-secret: 2c461fb0cb43442d8a82436ffd83f3cb
auth-res-url: esb.htb.srv:39245/SIMS #单点登录授权url
index-url: http://197.32.39.34:8888/#/single-login #首页地址
scvcd: 30430034 #上esb获取用户信息的服务码
user-url: http://197.32.39.34:8000/security-auth/oauth/token #应用系统获取token地址
chnl-tp: 41022

View File

@ -0,0 +1,111 @@
#服务器配置
server:
undertow:
# 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理
buffer-size: 1024
# 是否分配的直接内存
direct-buffers: true
# 线程配置
threads:
# 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程
io: 16
# 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载
worker: 400
#spring配置
spring:
cloud:
sentinel:
eager: true
devtools:
restart:
log-condition-evaluation-delta: false
livereload:
port: 23333
servlet:
multipart:
location: /home/sis/apps/undertow/tmp
#feign配置
feign:
sentinel:
enabled: true
okhttp:
enabled: true
httpclient:
enabled: true
hystrix:
enable: true
client:
config:
default:
readTimeout: 120000
connectTimeout: 3000
#对外暴露端口
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: always
#knife4j配置
knife4j:
#启用
enable: true
#基础认证
basic:
enable: false
username: blade
password: blade
#增强配置
setting:
enableSwaggerModels: true
enableDocumentManage: true
enableHost: false
enableHostText: http://localhost
enableRequestCache: true
enableFilterMultipartApis: false
enableFilterMultipartApiMethodType: POST
language: zh-CN
enableFooter: false
enableFooterCustom: true
footerCustomContent: Copyright © 2021 SpringBlade All Rights Reserved
#swagger配置信息
swagger:
title: SpringBlade 接口文档系统
description: SpringBlade 接口文档系统
version: 3.0.3
license: Powered By SpringBlade
licenseUrl: https://bladex.vip
terms-of-service-url: https://bladex.vip
contact:
name: smallchill
email: smallchill@163.com
url: https://gitee.com/smallc
#blade配置
blade:
xss:
enabled: true
skip-url:
- /weixin
secure:
skip-url:
- /test/**
client:
- client-id: sword
path-patterns:
- /sword/**
- client-id: saber
path-patterns:
- /saber/**
tenant:
column: tenant_id
tables:
- blade_notice

View File

@ -0,0 +1,127 @@
#spring配置
spring:
redis:
##redis 单机环境配置
host: 197.50.18.66
port: 16379
timeout: 10000
password:
database: 0
ssl: false
##redis 集群环境配置
#cluster:
# nodes: 47.108.70.243:7001,47.108.70.243:7002,47.108.70.243:7003
# commandTimeout: 5000
datasource:
driver-class-name: com.alipay.oceanbase.jdbc.Driver
druid:
validation-query: select 1
url: jdbc:oceanbase://oceanbase.qprod.htb.srv:2883/siszsc?useUnicode=true&characterEncoding=utf-8&mode=mysql&rewriteBatchedStatements=true&socketTimeout=6000000&connectTimeout=60000&allowMultiQueries=true&useLocalSessionState=true
username: htb_prod_obcluster:siszsc:secretiy
password: SIS_pswd@123
#项目模块集中配置
blade:
esbUrl: 197.34.18.16
esbPort: 39245
face:
virtualTelNo: C00074
leadDeptNo: 12023
telIdentTp: HTBK
#分布式锁配置
lock:
enabled: false
address: redis://47.108.70.243:6379
#多团队协作服务配置
ribbon:
rule:
#开启配置
enabled: true
#负载均衡优先调用的ip段
prior-ip-pattern:
- 192.168.0.*
- 47.108.70.243
#通用开发生产环境数据库地址(特殊情况可在对应的子工程里配置覆盖)
datasource:
test:
url: jdbc:oceanbase://oceanbase.qprod.htb.srv:2883/siszsc?useUnicode=true&characterEncoding=utf-8&mode=mysql&rewriteBatchedStatements=true&socketTimeout=6000000&connectTimeout=60000&allowMultiQueries=true&useLocalSessionState=true
username: htb_prod_obcluster:siszsc:secretiy
password: SIS_pswd@123
# dev:
# url: jdbc:oceanbase://oceanbase.test.htb.srv:2883/sisdev?useUnicode=true&characterEncoding=utf-8&mode=mysql&rewriteBatchedStatements=true&socketTimeout=6000000&connectTimeout=60000&allowMultiQueries=true&useLocalSessionState=true
# username: htb_test_obcluster:secretiy:secretiydev
# password: secretiydev
#
# Emb(消息总线请求参数)配置 -sit
sub:
brokerServiceurl: pulsar://198.69.20.7:6000
accessToken: eyJrZXlJZCI6InB1bHNhci13ejlwMmptYnBuIiwiYWxnIjoiSFMyNTYifQ.eyJzdWIiOiJwdWxzYXItd3o5cDJqbWJwbl9TSU1TIn0.ibMqmjqOJkNMWsxBJj-zLbGsG3VY5gX74G3MTIkMIHQ
subscriptionName: SIMS
corePoolSize: 1
evnValue: pulsar-wz9p2jmbpn
listenerName: pulsar-wz9p2jmbpn/vpc-espi8lwv/subnet-6o1k8cm0
topicNames:
- pulsar-wz9p2jmbpn/sso-common-info/M01010001
- pulsar-wz9p2jmbpn/sso-common-info/M01010002
- pulsar-wz9p2jmbpn/sso-common-info/M01030006
maximumPoolSize: 300
deptLeaderName: 机构负责人 #行政机构信息同步的机构负责人对应的角色名称
#security-desk-yml配置文件
#影像平台
images:
local_path: E:\安防信息化项目\uploadFile\ #本地存放文件路径
service_path: /home/test/apps/file/ # 服务器存放文件路径
DM: HTBANK
ip: nicmp.qprod.htb.srv #为影像平台的ip地址
socketPort: 8023 #为影像平台的socket端口
httpPort: 9080 #为影像平台的http端口
aimChannel: SIMS #渠道号
modelCode: SIMS #索引对象内容模型代码
filePartName: SIMS_PART #文档对象模型代码
userName: sims #登录影像平台的用户名
passWord: sims_admin #登录影像平台的密码
xyun:
app-id: 500000053
app-secret: B6rbJdILa1VEO9fmn6zj
url: http://197.52.23.10
# 公共号id
pub-id: XT-cb5053a4-3d00-47e7-a0c3-d0ac693305b7
pub-secret: 812739e21bf781370bebec37146c38a8
# 企业注册号eid
e-id: 1556583279
# 单点登录配置参数
single:
auth-url: http://bam.sit.ynhtbank.com:9080/idp/oauth2/authorize #单点登录url
client-id: SIMS #应用系统ID
redirect-uri: http://197.52.29.131:8105/authorize #重定向url
response-type: code #响应类型
state: 123 #3位随机数字
client-secret: 3ac76afc760f4c8b9265992e28c924df
auth-res-url: http://197.52.26.105:20009/v1/SWD/SWD20001 #单点登录授权url
index-url: http://197.52.29.131:8888/single-login #首页地址
scvcd: 3043003401 #上esb获取用户信息的服务码
user-url: localhost:8000/api/security-auth/oauth/token
#ht-secutity-yml配置文件
#导出文件的存储路径
export:
excel:
path: D:\云南红塔银行安保项目\excel导出测试\
#swagger包扫描路径
swagger:
base-packages: org.security.trans
#通用配置参数
common:
projectRoleName: 安全员 #项目预算管理-项目结项后可以对结项项目进行操作的指属上一级机构的角色名称
securityRoleName: 安全员 #安保指挥中心非现场检查整改通知书通知机构的角色
payPlanRemindKeyName: payPlanRemindDay #付款计划提醒提前天数字典配置键值
concatDeadlineRemindKeyName: concatDeadlineRemindDay #合同到期提醒提前天数字典配置键值

View File

@ -0,0 +1,111 @@
#服务器配置
server:
undertow:
# 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理
buffer-size: 1024
# 是否分配的直接内存
direct-buffers: true
# 线程配置
threads:
# 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程
io: 16
# 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载
worker: 400
#spring配置
spring:
cloud:
sentinel:
eager: true
devtools:
restart:
log-condition-evaluation-delta: false
livereload:
port: 23333
servlet:
multipart:
location: /home/test/apps/undertow/tmp
#feign配置
feign:
sentinel:
enabled: true
okhttp:
enabled: true
httpclient:
enabled: true
hystrix:
enable: true
client:
config:
default:
readTimeout: 120000
connectTimeout: 3000
#对外暴露端口
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: always
#knife4j配置
knife4j:
#启用
enable: true
#基础认证
basic:
enable: false
username: blade
password: blade
#增强配置
setting:
enableSwaggerModels: true
enableDocumentManage: true
enableHost: false
enableHostText: http://localhost
enableRequestCache: true
enableFilterMultipartApis: false
enableFilterMultipartApiMethodType: POST
language: zh-CN
enableFooter: false
enableFooterCustom: true
footerCustomContent: Copyright © 2021 SpringBlade All Rights Reserved
#swagger配置信息
swagger:
title: SpringBlade 接口文档系统
description: SpringBlade 接口文档系统
version: 3.0.3
license: Powered By SpringBlade
licenseUrl: https://bladex.vip
terms-of-service-url: https://bladex.vip
contact:
name: smallchill
email: smallchill@163.com
url: https://gitee.com/smallc
#blade配置
blade:
xss:
enabled: true
skip-url:
- /weixin
secure:
skip-url:
- /test/**
client:
- client-id: sword
path-patterns:
- /sword/**
- client-id: saber
path-patterns:
- /saber/**
tenant:
column: tenant_id
tables:
- blade_notice

185
doc/nginx/dev/nginx.conf Normal file
View File

@ -0,0 +1,185 @@
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
client_max_body_size 50m;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
#server {
# listen 80;
# server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
# location / {
# root html;
# index index.html index.htm;
# }
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
# error_page 500 502 503 504 /50x.html;
# location = /50x.html {
# root html;
# }
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
# }
server {
listen 8887;
server_name web-test;
location ^~ /sis/ {
proxy_pass http://127.0.0.1:8889;
}
}
server {
listen 8888;
server_name web;
#root /home/test/apps/security-manage/security/dist;
##location /file/ {
## root /home/test/apps;
## autoindex on;
##}
#location ^~ /sis/ {
# proxy_pass http://127.0.0.1:8889;
#}
location /map-resouce/ {
root /home/test/apps/map;
}
location / {
root /home/test/apps/security-pc/dist;
index index.html;
}
location ^~ /oauth/redirect {
rewrite ^(.*)$ /index.html break;
}
location /api/ {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_buffering off;
rewrite ^/api/(.*)$ /$1 break;
proxy_pass http://127.0.0.1:8000;
}
}
server {
listen 8889; #监听端口
server_name app; #服务器名
location /sis/api/ {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_buffering off;
rewrite ^/sis/api/(.*)$ /$1 break;
proxy_pass http://127.0.0.1:8000;
}
location ^~ /sis {
alias /home/test/apps/security-app/dist/sis;
index index.html;
try_files $uri $uri/ /@router;
}
location @router {
rewrite ^.*$ /index.html last;
}
}
# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;
# location / {
# root html;
# index index.html index.htm;
# }
#}
# HTTPS server
#
#server {
# listen 443 ssl;
# server_name localhost;
# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
# location / {
# root html;
# index index.html index.htm;
# }
#}
}

64
doc/nginx/prod/nginx.conf Normal file
View File

@ -0,0 +1,64 @@
#user nobody;
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
client_max_body_size 50m;
sendfile on;
keepalive_timeout 65;
server {
listen 8888;
server_name web;
location /map-resouce/ {
root /home/sis/apps/map;
}
location / {
root /home/sis/apps/security-pc/dist;
index index.html;
}
location ^~ /oauth/redirect {
rewrite ^(.*)$ /index.html break;
}
location /api/ {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_buffering off;
rewrite ^/api/(.*)$ /$1 break;
proxy_pass http://127.0.0.1:8000;
}
}
server {
listen 8889; #监听端口
server_name app; #服务器名
location /sis/api/ {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_buffering off;
rewrite ^/sis/api/(.*)$ /$1 break;
proxy_pass http://127.0.0.1:8000;
}
location ^~ /sis {
alias /home/sis/apps/security-app/dist/sis;
index index.html;
try_files $uri $uri/ /index.html;
}
}
}

177
doc/nginx/test/nginx.conf Normal file
View File

@ -0,0 +1,177 @@
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
client_max_body_size 50m;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
#server {
# listen 80;
# server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
# location / {
# root html;
# index index.html index.htm;
# }
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
# error_page 500 502 503 504 /50x.html;
# location = /50x.html {
# root html;
# }
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
# }
server {
listen 8888;
server_name web;
#root /home/test/apps/security-manage/security/dist;
#location /file/ {
# root /home/test/apps;
# autoindex on;
#}
location /map-resouce/ {
root /home/test/apps/map;
}
location / {
root /home/test/apps/security-pc/dist;
index index.html;
}
location ^~ /nacos {
proxy_pass http://197.50.18.66:8848/nacos;
}
location ^~ /oauth/redirect {
rewrite ^(.*)$ /index.html break;
}
location /api/ {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_buffering off;
rewrite ^/api/(.*)$ /$1 break;
proxy_pass http://127.0.0.1:8000;
}
location ^~ /sis/ {
proxy_pass http://127.0.0.1:8889;
}
}
server {
listen 8889; #监听端口
server_name app; #服务器名
location /sis/api/ {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_buffering off;
rewrite ^/sis/api/(.*)$ /$1 break;
proxy_pass http://127.0.0.1:8000;
}
location ^~ /sis {
alias /home/test/apps/security-app/dist/sis;
index index.html;
try_files $uri $uri/ /index.html;
}
}
# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;
# location / {
# root html;
# index index.html index.htm;
# }
#}
# HTTPS server
#
#server {
# listen 443 ssl;
# server_name localhost;
# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
# location / {
# root html;
# index index.html index.htm;
# }
#}
}

131
doc/sql/clean-all.sql Normal file
View File

@ -0,0 +1,131 @@
-- 业务表部分 开始
-- 检查整改表
delete from t_inspection;
-- 检查整改明细表
delete from t_inspection_line;
-- 应急演练、教育培训
delete from t_task_handin;
-- 任务下发
delete from t_task;
-- 案件材料报送
delete from t_prevent_material;
-- 风险提示
delete from t_prevent_risk;
-- 考核评价表
delete from t_assessment;
-- 考核评价明细表
delete from t_assessment_detail;
-- 值班备勤
delete from t_duty_records;
-- 值班备勤明细
delete from t_duty_records_lines;
-- 工作报备表
delete from t_work_filing;
-- 工作提醒表
delete from t_note_remind;
-- 非现场检查表
delete from t_scc_check;
-- 非现场检查明细表
delete from t_scc_check_lines;
-- 整改通知书表
delete from t_scc_unlocale_check;
-- 整改通知书明细表
delete from t_scc_unlocale_check_line;
-- 联系接警记录表
delete from t_scc_answer_alarm;
-- 警情处理表
delete from t_scc_alarm_push;
-- 模板管理表
-- delete from t_file_template;
-- 法律法规表
delete from t_law;
-- 设备检查表
delete from t_device_inspection;
-- 设备检查明细表
delete from t_device_inspection_line;
-- 项目管理-预算
delete from t_budget_item;
-- 项目管理-项目信息
delete from t_project;
delete from t_project_check;
delete from t_project_contract;
delete from t_project_establishment;
delete from t_project_implementation;
delete from t_project_over_history;
delete from t_project_pay_plan;
delete from t_project_purchase;
delete from t_publicity;
-- 工作交接记录表
delete from t_handover_history;
-- 交接人员组
delete from t_work_handover_users;
-- 设备信息
delete from t_device;
-- 宣传报道
delete from t_publicity;
-- 相关方公司
delete from t_out_company;
-- 相关方人员
delete from t_out_employee;
delete from t_out_family;
delete from t_out_job_record;
-- 附件表中删除 除模板管理 以外的数据
delete from t_file where link_id not in (select id from t_file_template);
-- 业务表结束
-- >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
-- flowable业务表 开始
-- 历史流程实例
delete from act_hi_actinst;
-- 历史流程附件
delete from act_hi_attachment;
-- 历史说明性信息
delete from act_hi_comment;
-- 历史流程运行中细节信息
delete from act_hi_detail;
-- 历史参与人员表
delete from act_hi_entitylink;
-- 历史流程中的用户关系
delete from act_hi_identitylink;
-- 历史流程实例
delete from act_hi_procinst;
-- 历史人物实例
delete from act_hi_taskinst;
-- 每一次执行execution可能带上的数据
delete from act_hi_tsk_log;
-- 历史流程变量
delete from act_hi_varinst;
-- 运行中的流程实例
delete from act_ru_actinst;
-- 正在运行的任务表
delete from act_ru_deadletter_job;
delete from act_ru_entitylink;
-- 运行时事件
delete from act_ru_event_subscr;
-- 历史作业表
delete from act_ru_history_job;
-- 运行时用户关系
delete from act_ru_identitylink;
-- 运行时作业表
delete from act_ru_job;
-- 暂停作业表
delete from act_ru_suspended_job;
-- 运行时任务表
delete from act_ru_task;
-- 定时作业表
delete from act_ru_timer_job;
-- 运行时变量表
delete from act_ru_variable;
-- 运行时流程执行实例
UPDATE act_ru_execution SET REV_ = '2';
update act_ru_execution set parent_id_ = null;
delete from act_ru_execution;
-- flowable业务表 结束
-- >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

View File

@ -0,0 +1,72 @@
-- 检查整改表
delete from t_inspection;
-- 检查整改明细表
delete from t_inspection_line;
-- 应急演练、教育培训
delete from t_task_handin;
-- 任务下发
delete from t_task;
-- 案件材料报送
delete from t_prevent_material;
-- 风险提示
delete from t_prevent_risk;
-- 考核评价表
delete from t_assessment;
-- 考核评价明细表
delete from t_assessment_detail;
-- 值班备勤
delete from t_duty_records;
-- 值班备勤明细
delete from t_duty_records_lines;
-- 工作报备表
delete from t_work_filing;
-- 工作提醒表
delete from t_note_remind;
-- 非现场检查表
delete from t_scc_check;
-- 非现场检查明细表
delete from t_scc_check_lines;
-- 整改通知书表
delete from t_scc_unlocale_check;
-- 整改通知书明细表
delete from t_scc_unlocale_check_line;
-- 联系接警记录表
delete from t_scc_answer_alarm;
-- 警情处理表
delete from t_scc_alarm_push;
-- 模板管理表
-- delete from t_file_template;
-- 法律法规表
delete from t_law;
-- 设备检查表
delete from t_device_inspection;
-- 设备检查明细表
delete from t_device_inspection_line;
-- 项目管理-预算
delete from t_budget_item;
-- 项目管理-项目信息
delete from t_project;
delete from t_project_check;
delete from t_project_contract;
delete from t_project_establishment;
delete from t_project_implementation;
delete from t_project_over_history;
delete from t_project_pay_plan;
delete from t_project_purchase;
delete from t_publicity;
-- 工作交接记录表
delete from t_handover_history;
-- 交接人员组
delete from t_work_handover_users;
-- 设备信息
delete from t_device;
-- 宣传报道
delete from t_publicity;
-- 相关方公司
delete from t_out_company;
-- 相关方人员
delete from t_out_employee;
delete from t_out_family;
delete from t_out_job_record;
-- 附件表中删除 除模板管理 以外的数据
delete from t_file where link_id not in (select id from t_file_template);

View File

@ -0,0 +1,80 @@
-- 历史流程实例
delete from act_hi_actinst;
-- 历史流程附件
delete from act_hi_attachment;
-- 历史说明性信息
delete from act_hi_comment;
-- 历史流程运行中细节信息
delete from act_hi_detail;
-- 历史参与人员表
delete from act_hi_entitylink;
-- 历史流程中的用户关系
delete from act_hi_identitylink;
-- 历史流程实例
delete from act_hi_procinst;
-- 历史人物实例
delete from act_hi_taskinst;
-- 每一次执行execution可能带上的数据
delete from act_hi_tsk_log;
-- 历史流程变量
delete from act_hi_varinst;
-- 运行中的流程实例
delete from act_ru_actinst;
-- 正在运行的任务表
delete from act_ru_deadletter_job;
delete from act_ru_entitylink;
-- 运行时事件
delete from act_ru_event_subscr;
-- 历史作业表
delete from act_ru_history_job;
-- 运行时用户关系
delete from act_ru_identitylink;
-- 运行时作业表
delete from act_ru_job;
-- 暂停作业表
delete from act_ru_suspended_job;
-- 运行时任务表
delete from act_ru_task;
-- 定时作业表
delete from act_ru_timer_job;
-- 运行时变量表
delete from act_ru_variable;
-- 运行时流程执行实例
update act_ru_execution set REV_ = '2';
update act_ru_execution set parent_id_ = null;
delete from act_ru_execution;
-- 系统业务表与flowable关联字段
-- 考核评价
update t_assessment set process_definition_id = null, process_instance_id = null, state = '0';
-- 设备检查/整改/持续整改
update t_device_inspection set process_definition_id = null, process_instance_id = null, ins_process_definition_id = null, ins_process_instance_id = null, rec_process_definition_id = null, rec_process_instance_id = null, state = '0';
-- 设备检查明细
update t_device_inspection_line set process_definition_id = null, process_instance_id = null, state = '0';
-- 值班备勤
update t_duty_records set process_definition_id = null, process_instance_id = null, state = '0';
-- 现场检查
update t_inspection set process_definition_id = null, process_instance_id = null, state = '0';
-- 现场检查整改
update t_inspection_line set process_definition_id = null, process_instance_id = null, state = '0';
-- 案件材料报送
update t_prevent_material set process_definition_id = null, process_instance_id = null, state = '0';
-- 风险提示
update t_prevent_risk set process_definition_id = null, process_instance_id = null, state = '0';
-- 警情处理
update t_scc_alarm_push set process_definition_id = null, process_instance_id = null, state = '0';
-- 联系接警记录
update t_scc_answer_alarm set process_definition_id = null, process_instance_id = null, state = '0';
-- 非现场检查
update t_scc_check set process_definition_id = null, process_instance_id = null, state = '0';
-- 非现场检查整改
update t_scc_unlocale_check set process_definition_id = null, process_instance_id = null, state = '0';
-- 非现场检查整改明细
update t_scc_unlocale_check_line set process_definition_id = null, process_instance_id = null, state = '0';
-- 教育培训/应急演练
update t_task_handin set process_definition_id = null, process_instance_id = null, state = '0';

View File

@ -0,0 +1,28 @@
INSERT INTO
`blade_user`
VALUES
(
1123598821738675201,
'000000',
NULL,
1,
'admin',
'90b9aa7e25f80cf4f64e990b78a9fc5ebd6cecad',
'管理员',
'管理员',
'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png',
'admin@bladex.vip',
'123333333333',
'2018-08-08 00:00:00',
1,
'1123598816738675201',
'1580387563597537281',
'1123598817738675201',
1123598821738675201,
1123598813738675201,
'2018-08-08 00:00:00',
1123598821738675201,
'2018-08-08 00:00:00',
1,
0
);

201
doc/sql/init-nacos.sql Normal file
View File

@ -0,0 +1,201 @@
CREATE TABLE `config_info` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
`group_id` varchar(255) DEFAULT NULL,
`content` longtext NOT NULL COMMENT 'content',
`md5` varchar(32) DEFAULT NULL COMMENT 'md5',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
`src_user` text COMMENT 'source user',
`src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip',
`app_name` varchar(128) DEFAULT NULL,
`tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
`c_desc` varchar(256) DEFAULT NULL,
`c_use` varchar(64) DEFAULT NULL,
`effect` varchar(64) DEFAULT NULL,
`type` varchar(64) DEFAULT NULL,
`c_schema` text,
`encrypted_data_key` text NOT NULL COMMENT '秘钥',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_configinfo_datagrouptenant` (`data_id`,`group_id`,`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info';
/******************************************/
/* 数据库全名 = nacos_config */
/* 表名称 = config_info_aggr */
/******************************************/
CREATE TABLE `config_info_aggr` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
`group_id` varchar(255) NOT NULL COMMENT 'group_id',
`datum_id` varchar(255) NOT NULL COMMENT 'datum_id',
`content` longtext NOT NULL COMMENT '内容',
`gmt_modified` datetime NOT NULL COMMENT '修改时间',
`app_name` varchar(128) DEFAULT NULL,
`tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_configinfoaggr_datagrouptenantdatum` (`data_id`,`group_id`,`tenant_id`,`datum_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='增加租户字段';
/******************************************/
/* 数据库全名 = nacos_config */
/* 表名称 = config_info_beta */
/******************************************/
CREATE TABLE `config_info_beta` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
`group_id` varchar(128) NOT NULL COMMENT 'group_id',
`app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
`content` longtext NOT NULL COMMENT 'content',
`beta_ips` varchar(1024) DEFAULT NULL COMMENT 'betaIps',
`md5` varchar(32) DEFAULT NULL COMMENT 'md5',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
`src_user` text COMMENT 'source user',
`src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip',
`tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
`encrypted_data_key` text NOT NULL COMMENT '秘钥',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_configinfobeta_datagrouptenant` (`data_id`,`group_id`,`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info_beta';
/******************************************/
/* 数据库全名 = nacos_config */
/* 表名称 = config_info_tag */
/******************************************/
CREATE TABLE `config_info_tag` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
`group_id` varchar(128) NOT NULL COMMENT 'group_id',
`tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id',
`tag_id` varchar(128) NOT NULL COMMENT 'tag_id',
`app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
`content` longtext NOT NULL COMMENT 'content',
`md5` varchar(32) DEFAULT NULL COMMENT 'md5',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
`src_user` text COMMENT 'source user',
`src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_configinfotag_datagrouptenanttag` (`data_id`,`group_id`,`tenant_id`,`tag_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info_tag';
/******************************************/
/* 数据库全名 = nacos_config */
/* 表名称 = config_tags_relation */
/******************************************/
CREATE TABLE `config_tags_relation` (
`id` bigint(20) NOT NULL COMMENT 'id',
`tag_name` varchar(128) NOT NULL COMMENT 'tag_name',
`tag_type` varchar(64) DEFAULT NULL COMMENT 'tag_type',
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
`group_id` varchar(128) NOT NULL COMMENT 'group_id',
`tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id',
`nid` bigint(20) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`nid`),
UNIQUE KEY `uk_configtagrelation_configidtag` (`id`,`tag_name`,`tag_type`),
KEY `idx_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_tag_relation';
/******************************************/
/* 数据库全名 = nacos_config */
/* 表名称 = group_capacity */
/******************************************/
CREATE TABLE `group_capacity` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`group_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Group ID空字符表示整个集群',
`quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额0表示使用默认值',
`usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量',
`max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限单位为字节0表示使用默认值',
`max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数0表示使用默认值',
`max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限单位为字节0表示使用默认值',
`max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_group_id` (`group_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='集群、各Group容量信息表';
/******************************************/
/* 数据库全名 = nacos_config */
/* 表名称 = his_config_info */
/******************************************/
CREATE TABLE `his_config_info` (
`id` bigint(20) unsigned NOT NULL,
`nid` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`data_id` varchar(255) NOT NULL,
`group_id` varchar(128) NOT NULL,
`app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
`content` longtext NOT NULL,
`md5` varchar(32) DEFAULT NULL,
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`src_user` text,
`src_ip` varchar(50) DEFAULT NULL,
`op_type` char(10) DEFAULT NULL,
`tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
`encrypted_data_key` text NOT NULL COMMENT '秘钥',
PRIMARY KEY (`nid`),
KEY `idx_gmt_create` (`gmt_create`),
KEY `idx_gmt_modified` (`gmt_modified`),
KEY `idx_did` (`data_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='多租户改造';
/******************************************/
/* 数据库全名 = nacos_config */
/* 表名称 = tenant_capacity */
/******************************************/
CREATE TABLE `tenant_capacity` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`tenant_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Tenant ID',
`quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额0表示使用默认值',
`usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量',
`max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限单位为字节0表示使用默认值',
`max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数',
`max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限单位为字节0表示使用默认值',
`max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='租户容量信息表';
CREATE TABLE `tenant_info` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`kp` varchar(128) NOT NULL COMMENT 'kp',
`tenant_id` varchar(128) default '' COMMENT 'tenant_id',
`tenant_name` varchar(128) default '' COMMENT 'tenant_name',
`tenant_desc` varchar(256) DEFAULT NULL COMMENT 'tenant_desc',
`create_source` varchar(32) DEFAULT NULL COMMENT 'create_source',
`gmt_create` bigint(20) NOT NULL COMMENT '创建时间',
`gmt_modified` bigint(20) NOT NULL COMMENT '修改时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_tenant_info_kptenantid` (`kp`,`tenant_id`),
KEY `idx_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='tenant_info';
CREATE TABLE `users` (
`username` varchar(50) NOT NULL PRIMARY KEY,
`password` varchar(500) NOT NULL,
`enabled` boolean NOT NULL
);
CREATE TABLE `roles` (
`username` varchar(50) NOT NULL,
`role` varchar(50) NOT NULL,
UNIQUE INDEX `idx_user_role` (`username` ASC, `role` ASC) USING BTREE
);
CREATE TABLE `permissions` (
`role` varchar(50) NOT NULL,
`resource` varchar(255) NOT NULL,
`action` varchar(8) NOT NULL,
UNIQUE INDEX `uk_role_permission` (`role`,`resource`,`action`) USING BTREE
);
INSERT INTO users (username, password, enabled) VALUES ('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', TRUE);
INSERT INTO roles (username, role) VALUES ('nacos', 'ROLE_ADMIN');

193
pom.xml Normal file
View File

@ -0,0 +1,193 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.security</groupId>
<artifactId>security-manage</artifactId>
<version>2.8.1.RELEASE</version>
<packaging>pom</packaging>
<properties>
<bladex.project.version>2.8.1.RELEASE</bladex.project.version>
<java.version>1.8</java.version>
<maven.plugin.version>3.8.1</maven.plugin.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<flowable.version>6.4.2</flowable.version>
<spring.boot.version>2.2.13.RELEASE</spring.boot.version>
<spring.cloud.version>Hoxton.SR11</spring.cloud.version>
<spring.platform.version>Cairo-SR8</spring.platform.version>
<jasypt.version>3.0.3</jasypt.version>
</properties>
<modules>
<module>security-auth</module>
<module>security-common</module>
<module>security-gateway</module>
<module>security-ops</module>
<module>security-ops-api</module>
<module>security-service</module>
<module>security-service-api</module>
</modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>${jasypt.version}</version>
</dependency>
<dependency>
<groupId>org.springblade.platform</groupId>
<artifactId>blade-bom</artifactId>
<version>${bladex.project.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.security</groupId>
<artifactId>security-common</artifactId>
<version>${bladex.project.version}</version>
</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>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>io.spring.platform</groupId>
<artifactId>platform-bom</artifactId>
<version>${spring.platform.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>${project.name}</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
<configuration>
<fork>true</fork>
<finalName>${project.build.finalName}</finalName>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<!--suppress UnresolvedMavenProperty -->
<copy overwrite="true"
tofile="${session.executionRootDirectory}/target/${project.artifactId}.jar"
file="${project.build.directory}/${project.artifactId}.jar" />
</tasks>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven.plugin.version}</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>UTF-8</encoding>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<!-- <repository>-->
<!-- <id>aliyun-repos</id>-->
<!--&lt;!&ndash; <url>https://maven.aliyun.com/nexus/content/groups/public/</url>&ndash;&gt;-->
<!-- <snapshots>-->
<!-- <enabled>false</enabled>-->
<!-- </snapshots>-->
<!-- </repository>-->
<repository>
<id>blade-release</id>
<name>Release Repository</name>
<url>http://nexus.bladex.vip/repository/maven-releases/</url>
</repository>
</repositories>
<pluginRepositories>
<!-- <pluginRepository>-->
<!-- <id>aliyun-plugin</id>-->
<!--&lt;!&ndash; <url>https://maven.aliyun.com/nexus/content/groups/public/</url>&ndash;&gt;-->
<!-- <snapshots>-->
<!-- <enabled>false</enabled>-->
<!-- </snapshots>-->
<!-- </pluginRepository>-->
</pluginRepositories>
</project>

32
security-auth/README.md Normal file
View File

@ -0,0 +1,32 @@
## 目前主要支持的oauth协议
一、 授权码模式
授权码模式(authorization_code)主要针对第三方应用,是最为复杂也最为安全的一种模式,操作步骤如下
1. 访问地址http://localhost:8100/oauth/authorize?client_id=blade&redirect_uri=http://example.com&code=233333&response_type=code
2. 获取跳转后的code值(http://example.com/?code=VhYNLR)之后,调用 http://localhost/blade-auth/oauth/token 传入对应的参数
请求头:
Authorization Basic YmxhZGU6YmxhZGU= "YmxhZGU6YmxhZGU="为clientId:clientSecret串转换为的base64编码
表单:
grant_typeauthorization_code
scopeall
codeVhYNLR
redirect_uri http://example.com
二、 密码模式
密码模式(password)主要针对自家应用,可信度较高,所以可以使用简便安全共存的模式,操作步骤如下
1. 直接调用 http://localhost/blade-auth/oauth/token 传入对应的参数
请求头:
Authorization Basic YmxhZGU6YmxhZGU= "YmxhZGU6YmxhZGU="为clientId:clientSecret串转换为的base64编码
表单:
grant_typepassword
scopeall
usernameadmin
password123456
## 获取到token后如何获取用户信息
1. 拼接请求头
Authorization bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0ZXN0IjoidGVzdCIsInVzZXJfbmFtZSI6ImFkbWluIiwic2NvcGUiOlsiYWxsIl0sImV4cCI6MTU1MzE2MTA5NSwiYXV0aG9yaXRpZXMiOlsiUk9MRV9VU0VSIl0sImp0aSI6IjE0YmMyYjAyLTgxY2UtNDFiNC04ZTI3LTA5YWE0ZmU4ZWMwYyIsImNsaWVudF9pZCI6ImJsYWRlIn0.jTmioQDq-fSNNn7YCwl3wP0JE-etSWtzLDe545mDbP4
2. 调用 http://localhost/blade-auth/oauth/user-info 既可获得对应用户信息

169
security-auth/pom.xml Normal file
View File

@ -0,0 +1,169 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>security-manage</artifactId>
<groupId>org.security</groupId>
<version>2.8.1.RELEASE</version>
</parent>
<artifactId>security-auth</artifactId>
<name>${project.artifactId}</name>
<version>${bladex.project.version}</version>
<packaging>jar</packaging>
<dependencies>
<!-- <dependency>-->
<!-- <groupId>com.alipay.oceanbase</groupId>-->
<!-- <artifactId>oceanbase-client</artifactId>-->
<!-- <version>2.2.8.1</version>-->
<!-- </dependency>-->
<dependency>
<groupId>org.security</groupId>
<artifactId>security-common</artifactId>
<exclusions>
<exclusion>
<groupId>org.security</groupId>
<artifactId>security-scope-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.security</groupId>
<artifactId>security-desk-api</artifactId>
<version>${bladex.project.version}</version>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-db</artifactId>
<exclusions>
<exclusion>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
</exclusion>
<exclusion>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc7</artifactId>
</exclusion>
<exclusion>
<artifactId>ojdbc7</artifactId>
<groupId>com.oracle</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.22</version>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-cloud</artifactId>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-starter-metrics</artifactId>
<exclusions>
<exclusion>
<artifactId>druid</artifactId>
<groupId>com.alibaba</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-starter-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-starter-swagger</artifactId>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-starter-social</artifactId>
</dependency>
<dependency>
<groupId>org.security</groupId>
<artifactId>security-user-api</artifactId>
<version>${bladex.project.version}</version>
<exclusions>
<exclusion>
<artifactId>druid</artifactId>
<groupId>com.alibaba</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.security</groupId>
<artifactId>security-system-api</artifactId>
<version>${bladex.project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
</dependency>
<!-- 验证码 -->
<dependency>
<groupId>com.github.whvcse</groupId>
<artifactId>easy-captcha</artifactId>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-secure</artifactId>
</dependency>
<!-- 链路追踪、服务监控 -->
<!--<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-starter-trace</artifactId>
</dependency>-->
<!-- 解决Java11无法运行的问题 -->
<!--<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.2.11</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
<version>2.2.11</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.2.11</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>-->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,38 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.auth;
import org.security.common.constant.CommonConstant;
import org.springblade.core.launch.BladeApplication;
import org.springframework.cloud.client.SpringCloudApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* 用户认证服务器
*
* @author Chill
*/
@EnableFeignClients("org.security")
@SpringCloudApplication
public class AuthApplication {
public static void main(String[] args) {
BladeApplication.run(CommonConstant.APPLICATION_AUTH_NAME, AuthApplication.class, args);
}
}

View File

@ -0,0 +1,119 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.auth.config;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.security.auth.granter.BladeTokenGranter;
import org.security.auth.service.BladeClientDetailsServiceImpl;
import org.security.auth.service.IXYunAuthService;
import org.security.system.user.feign.IUserClient;
import org.springblade.core.redis.cache.BladeRedis;
import org.springblade.core.social.props.SocialProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.TokenGranter;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.List;
/**
* 认证服务器配置
*
* @author Chill
*/
@Order
@Configuration
@AllArgsConstructor
@EnableAuthorizationServer
public class BladeAuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
private final DataSource dataSource;
private final AuthenticationManager authenticationManager;
private final UserDetailsService userDetailsService;
private final TokenStore tokenStore;
private final TokenEnhancer jwtTokenEnhancer;
private final JwtAccessTokenConverter jwtAccessTokenConverter;
private final BladeRedis bladeRedis;
private final IUserClient userClient;
private final SocialProperties socialProperties;
private final XYunProperties xYunProperties;
private final IXYunAuthService xYunService;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
//获取自定义tokenGranter
TokenGranter tokenGranter = BladeTokenGranter.getTokenGranter(authenticationManager, endpoints, bladeRedis, userClient, socialProperties, xYunProperties, xYunService);
//配置端点
endpoints.tokenStore(tokenStore)
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService)
.tokenGranter(tokenGranter);
//扩展token返回结果
if (jwtAccessTokenConverter != null && jwtTokenEnhancer != null) {
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
List<TokenEnhancer> enhancerList = new ArrayList<>();
enhancerList.add(jwtTokenEnhancer);
enhancerList.add(jwtAccessTokenConverter);
tokenEnhancerChain.setTokenEnhancers(enhancerList);
//jwt增强
endpoints.tokenEnhancer(tokenEnhancerChain).accessTokenConverter(jwtAccessTokenConverter);
}
}
/**
* 配置客户端信息
*/
@Override
@SneakyThrows
public void configure(ClientDetailsServiceConfigurer clients) {
BladeClientDetailsServiceImpl clientDetailsService = new BladeClientDetailsServiceImpl(dataSource);
clients.withClientDetails(clientDetailsService);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
oauthServer
.allowFormAuthenticationForClients()
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
}
}

View File

@ -0,0 +1,67 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.auth.config;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
/**
* 自定义登录成功配置
*
* @author Chill
*/
@Configuration
@AllArgsConstructor
@EnableResourceServer
public class BladeResourceServerConfiguration extends ResourceServerConfigurerAdapter {
/**
* 自定义登录成功处理器
*/
private final AuthenticationSuccessHandler appLoginInSuccessHandler;
@Override
@SneakyThrows
public void configure(HttpSecurity http) {
http.headers().frameOptions().disable();
http.formLogin()
.successHandler(appLoginInSuccessHandler)
.and()
.authorizeRequests()
.antMatchers(
"/actuator/**",
"/oauth/captcha",
"/oauth/logout",
"/oauth/clear-cache",
"/oauth/render/**",
"/oauth/callback/**",
"/oauth/revoke/**",
"/oauth/refresh/**",
"/token/**",
"/mobile/**",
"/app/auth",
"/v2/api-docs").permitAll()
.anyRequest().authenticated().and()
.csrf().disable();
}
}

View File

@ -0,0 +1,66 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.auth.config;
import org.security.auth.support.BladeJwtTokenEnhancer;
import org.springblade.core.jwt.props.JwtProperties;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
/**
* JwtTokenStore
*
* @author Chill
*/
@Configuration
@ConditionalOnProperty(prefix = "blade.security.oauth2", name = "storeType", havingValue = "jwt", matchIfMissing = true)
public class JwtTokenStoreConfiguration {
/**
* 使用jwtTokenStore存储token
*/
@Bean
public TokenStore jwtTokenStore(JwtProperties jwtProperties) {
return new JwtTokenStore(jwtAccessTokenConverter(jwtProperties));
}
/**
* 用于生成jwt
*/
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter(JwtProperties jwtProperties) {
JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
accessTokenConverter.setSigningKey(jwtProperties.getSignKey());
return accessTokenConverter;
}
/**
* 用于扩展jwt
*/
@Bean
@ConditionalOnMissingBean(name = "jwtTokenEnhancer")
public TokenEnhancer jwtTokenEnhancer(JwtAccessTokenConverter jwtAccessTokenConverter, JwtProperties jwtProperties) {
return new BladeJwtTokenEnhancer(jwtAccessTokenConverter, jwtProperties);
}
}

View File

@ -0,0 +1,56 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.auth.config;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.security.auth.support.BladePasswordEncoderFactories;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* Security配置
*
* @author Chill
*/
@Configuration
@AllArgsConstructor
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Bean
@Override
@SneakyThrows
public AuthenticationManager authenticationManagerBean() {
return super.authenticationManagerBean();
}
@Bean
public PasswordEncoder passwordEncoder() {
return BladePasswordEncoderFactories.createDelegatingPasswordEncoder();
}
@Override
@SneakyThrows
protected void configure(HttpSecurity http) {
http.httpBasic().and().csrf().disable();
}
}

View File

@ -0,0 +1,17 @@
package org.security.auth.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "xyun")
@Data
public class XYunProperties {
private String appId;
private String appSecret;
private String pubId;
private String pubSecret;
private String url;
private String eId;
}

View File

@ -0,0 +1,53 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.auth.constant;
/**
* 授权校验常量
*
* @author Chill
*/
public interface AuthConstant {
/**
* 密码加密规则
*/
String ENCRYPT = "{blade}";
/**
* blade_client表字段
*/
String CLIENT_FIELDS = "client_id, CONCAT('{noop}',client_secret) as client_secret, resource_ids, scope, authorized_grant_types, " +
"web_server_redirect_uri, authorities, access_token_validity, " +
"refresh_token_validity, additional_information, autoapprove";
/**
* blade_client查询语句
*/
String BASE_STATEMENT = "select " + CLIENT_FIELDS + " from blade_client";
/**
* blade_client查询排序
*/
String DEFAULT_FIND_STATEMENT = BASE_STATEMENT + " order by client_id";
/**
* 查询client_id
*/
String DEFAULT_SELECT_STATEMENT = BASE_STATEMENT + " where client_id = ?";
}

View File

@ -0,0 +1,70 @@
package org.security.auth.endpoint;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.security.system.user.feign.IUserClient;
import org.security.system.user.vo.UserVO;
import org.springblade.core.launch.constant.TokenConstant;
import org.springblade.core.secure.TokenInfo;
import org.springblade.core.secure.utils.SecureUtil;
import org.springblade.core.tenant.annotation.NonDS;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.Func;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* @author zj 2022/9/5
*/
@NonDS
@Slf4j
@RestController
@AllArgsConstructor
public class AuthEndPoint {
private final IUserClient iUserClient;
/**
* app用户验证生成token
*
* @param code 工号
* @return
*/
@GetMapping("/app/auth")
public R appAuth(String code) {
R<UserVO> userResult = iUserClient.getUserByCode(code);
UserVO userVO = userResult.getData();
if (!userResult.isSuccess() || Objects.isNull(userVO)) {
return R.fail("用户未在安防系统注册");
}
TokenInfo jwt = SecureUtil.createJWT(getAppTokenMap(userVO), "app", "security", "access_token");
HashMap<String, String> res = new HashMap<>();
res.put("code", userVO.getCode());
res.put("userName", userVO.getRealName());
res.put("token", jwt.getToken());
return R.data(res);
}
private Map<String, Object> getAppTokenMap(UserVO userVO) {
Map<String, Object> info = new HashMap<>(16);
info.put(TokenConstant.CLIENT_ID, "app");
info.put(TokenConstant.USER_ID, Func.toStr(userVO.getId()));
info.put(TokenConstant.DEPT_ID, Func.toStr(userVO.getDeptId()));
info.put(TokenConstant.POST_ID, Func.toStr(userVO.getPostId()));
info.put(TokenConstant.ROLE_ID, Func.toStr(userVO.getRoleId()));
info.put(TokenConstant.TENANT_ID, userVO.getTenantId());
info.put(TokenConstant.OAUTH_ID, "");
info.put(TokenConstant.ACCOUNT, userVO.getAccount());
info.put(TokenConstant.USER_NAME, userVO.getRealName());
info.put(TokenConstant.NICK_NAME, userVO.getName());
info.put(TokenConstant.REAL_NAME, userVO.getRealName());
info.put(TokenConstant.ROLE_NAME, userVO.getRoleName());
info.put(TokenConstant.AVATAR, userVO.getAvatar());
info.put(TokenConstant.DETAIL, "web");
info.put(TokenConstant.LICENSE, "security");
return info;
}
}

View File

@ -0,0 +1,88 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.auth.endpoint;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.request.AuthRequest;
import me.zhyd.oauth.utils.AuthStateUtils;
import org.springblade.core.social.props.SocialProperties;
import org.springblade.core.social.utils.SocialUtil;
import org.springblade.core.tenant.annotation.NonDS;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* SocialEndpoint
*
* @author Chill
*/
@NonDS
@Slf4j
@RestController
@AllArgsConstructor
@ConditionalOnProperty(value = "social.enabled", havingValue = "true")
public class BladeSocialEndpoint {
private final SocialProperties socialProperties;
/**
* 授权完毕跳转
*/
@RequestMapping("/oauth/render/{source}")
public void renderAuth(@PathVariable("source") String source, HttpServletResponse response) throws IOException {
AuthRequest authRequest = SocialUtil.getAuthRequest(source, socialProperties);
String authorizeUrl = authRequest.authorize(AuthStateUtils.createState());
response.sendRedirect(authorizeUrl);
}
/**
* 获取认证信息
*/
@RequestMapping("/oauth/callback/{source}")
public Object login(@PathVariable("source") String source, AuthCallback callback) {
AuthRequest authRequest = SocialUtil.getAuthRequest(source, socialProperties);
return authRequest.login(callback);
}
/**
* 撤销授权
*/
@RequestMapping("/oauth/revoke/{source}/{token}")
public Object revokeAuth(@PathVariable("source") String source, @PathVariable("token") String token) {
AuthRequest authRequest = SocialUtil.getAuthRequest(source, socialProperties);
return authRequest.revoke(AuthToken.builder().accessToken(token).build());
}
/**
* 续期令牌
*/
@RequestMapping("/oauth/refresh/{source}")
public Object refreshAuth(@PathVariable("source") String source, String token) {
AuthRequest authRequest = SocialUtil.getAuthRequest(source, socialProperties);
return authRequest.refresh(AuthToken.builder().refreshToken(token).build());
}
}

View File

@ -0,0 +1,101 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.auth.endpoint;
import com.wf.captcha.SpecCaptcha;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.security.common.cache.CacheNames;
import org.springblade.core.cache.utils.CacheUtil;
import org.springblade.core.jwt.JwtUtil;
import org.springblade.core.jwt.props.JwtProperties;
import org.springblade.core.launch.constant.TokenConstant;
import org.springblade.core.redis.cache.BladeRedis;
import org.springblade.core.secure.BladeUser;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tenant.annotation.NonDS;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.support.Kv;
import org.springblade.core.tool.utils.StringUtil;
import org.springblade.core.tool.utils.WebUtil;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.Duration;
import static org.springblade.core.cache.constant.CacheConstant.*;
/**
* BladeEndPoint
*
* @author Chill
*/
@NonDS
@Slf4j
@RestController
@AllArgsConstructor
public class BladeTokenEndPoint {
private final BladeRedis bladeRedis;
private final JwtProperties jwtProperties;
@GetMapping("/oauth/user-info")
public R<Authentication> currentUser(Authentication authentication) {
return R.data(authentication);
}
@GetMapping("/oauth/captcha")
public Kv captcha() {
log.info("获取验证码");
SpecCaptcha specCaptcha = new SpecCaptcha(130, 48, 5);
String verCode = specCaptcha.text().toLowerCase();
String key = StringUtil.randomUUID();
// 存入redis并设置过期时间为30分钟
bladeRedis.setEx(CacheNames.CAPTCHA_KEY + key, verCode, Duration.ofMinutes(30));
// 将key和base64返回给前端
return Kv.create().set("key", key).set("image", specCaptcha.toBase64());
}
@GetMapping("/oauth/logout")
public Kv logout() {
BladeUser user = AuthUtil.getUser();
if (user != null && jwtProperties.getState()) {
String token = JwtUtil.getToken(WebUtil.getRequest().getHeader(TokenConstant.HEADER));
JwtUtil.removeAccessToken(user.getTenantId(), String.valueOf(user.getUserId()), token);
}
return Kv.create().set("success", "true").set("msg", "success");
}
@GetMapping("/oauth/clear-cache")
public Kv clearCache() {
CacheUtil.clear(BIZ_CACHE);
CacheUtil.clear(USER_CACHE);
CacheUtil.clear(DICT_CACHE);
CacheUtil.clear(FLOW_CACHE);
CacheUtil.clear(SYS_CACHE);
CacheUtil.clear(PARAM_CACHE);
CacheUtil.clear(RESOURCE_CACHE);
CacheUtil.clear(MENU_CACHE);
CacheUtil.clear(DICT_CACHE, Boolean.FALSE);
CacheUtil.clear(MENU_CACHE, Boolean.FALSE);
CacheUtil.clear(SYS_CACHE, Boolean.FALSE);
CacheUtil.clear(PARAM_CACHE, Boolean.FALSE);
return Kv.create().set("success", "true").set("msg", "success");
}
}

View File

@ -0,0 +1,61 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.auth.granter;
import org.security.auth.config.XYunProperties;
import org.security.auth.service.IXYunAuthService;
import org.security.system.user.feign.IUserClient;
import org.springblade.core.redis.cache.BladeRedis;
import org.springblade.core.social.props.SocialProperties;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.provider.CompositeTokenGranter;
import org.springframework.security.oauth2.provider.TokenGranter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* 自定义拓展TokenGranter
*
* @author Chill
*/
public class BladeTokenGranter {
/**
* 自定义tokenGranter
*/
public static TokenGranter getTokenGranter(
final AuthenticationManager authenticationManager, final AuthorizationServerEndpointsConfigurer endpoints,
BladeRedis bladeRedis, IUserClient userClient, SocialProperties socialProperties, XYunProperties xYunProperties,
IXYunAuthService xYunService) {
// 默认tokenGranter集合
List<TokenGranter> granters = new ArrayList<>(Collections.singletonList(endpoints.getTokenGranter()));
// 增加验证码模式
granters.add(new CaptchaTokenGranter(authenticationManager, endpoints.getTokenServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory(), bladeRedis));
// 增加第三方登陆模式
granters.add(new SocialTokenGranter(endpoints.getTokenServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory(), userClient, socialProperties));
// 香云登陆模式
granters.add(new XYunTokenGranter(endpoints.getTokenServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory(), userClient, xYunProperties, xYunService));
//单点登录模式
granters.add(new SingleSignOnTokenGranter(endpoints.getTokenServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory(), userClient));
// 组合tokenGranter集合
return new CompositeTokenGranter(granters);
}
}

View File

@ -0,0 +1,82 @@
package org.security.auth.granter;
import org.security.auth.utils.TokenUtil;
import org.security.common.cache.CacheNames;
import org.springblade.core.redis.cache.BladeRedis;
import org.springblade.core.tool.utils.StringUtil;
import org.springblade.core.tool.utils.WebUtil;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
import org.springframework.security.oauth2.common.exceptions.UserDeniedAuthorizationException;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.oauth2.provider.token.AbstractTokenGranter;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import javax.servlet.http.HttpServletRequest;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* 验证码TokenGranter
*
* @author Chill
*/
public class CaptchaTokenGranter extends AbstractTokenGranter {
private static final String GRANT_TYPE = "captcha";
private final AuthenticationManager authenticationManager;
private BladeRedis bladeRedis;
public CaptchaTokenGranter(AuthenticationManager authenticationManager,
AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, BladeRedis bladeRedis) {
this(authenticationManager, tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
this.bladeRedis = bladeRedis;
}
protected CaptchaTokenGranter(AuthenticationManager authenticationManager, AuthorizationServerTokenServices tokenServices,
ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, String grantType) {
super(tokenServices, clientDetailsService, requestFactory, grantType);
this.authenticationManager = authenticationManager;
}
@Override
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
HttpServletRequest request = WebUtil.getRequest();
// 增加验证码判断
String key = request.getHeader(TokenUtil.CAPTCHA_HEADER_KEY);
String code = request.getHeader(TokenUtil.CAPTCHA_HEADER_CODE);
// 获取验证码
String redisCode = bladeRedis.get(CacheNames.CAPTCHA_KEY + key);
// 判断验证码
if (code == null || !StringUtil.equalsIgnoreCase(redisCode, code)) {
throw new UserDeniedAuthorizationException(TokenUtil.CAPTCHA_NOT_CORRECT);
}
Map<String, String> parameters = new LinkedHashMap<String, String>(tokenRequest.getRequestParameters());
String username = parameters.get("username");
String password = parameters.get("password");
// Protect from downstream leaks of password
parameters.remove("password");
Authentication userAuth = new UsernamePasswordAuthenticationToken(username, password);
((AbstractAuthenticationToken) userAuth).setDetails(parameters);
try {
userAuth = authenticationManager.authenticate(userAuth);
}
catch (AccountStatusException | BadCredentialsException ase) {
//covers expired, locked, disabled cases (mentioned in section 5.2, draft 31)
throw new InvalidGrantException(ase.getMessage());
}
// If the username/password are wrong the spec says we should send 400/invalid grant
if (userAuth == null || !userAuth.isAuthenticated()) {
throw new InvalidGrantException("Could not authenticate user: " + username);
}
OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
return new OAuth2Authentication(storedOAuth2Request, userAuth);
}
}

View File

@ -0,0 +1,79 @@
package org.security.auth.granter;
import lombok.extern.slf4j.Slf4j;
import org.security.auth.config.XYunProperties;
import org.security.auth.constant.AuthConstant;
import org.security.auth.service.BladeUserDetails;
import org.security.auth.service.IXYunAuthService;
import org.security.auth.utils.TokenUtil;
import org.security.desk.xyun.res.AuthToken;
import org.security.desk.xyun.res.AuthUser;
import org.security.system.user.entity.User;
import org.security.system.user.entity.UserInfo;
import org.security.system.user.feign.IUserClient;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.support.Kv;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.WebUtil;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.oauth2.provider.token.AbstractTokenGranter;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import javax.servlet.http.HttpServletRequest;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* 单点登录
*
* @author wzf
*/
@Slf4j
public class SingleSignOnTokenGranter extends AbstractTokenGranter {
private static final String GRANT_TYPE = "singleSignOn";
private final IUserClient userClient;
protected SingleSignOnTokenGranter(AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService,
OAuth2RequestFactory requestFactory, IUserClient userClient) {
super(tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
this.userClient = userClient;
}
@Override
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
HttpServletRequest request = WebUtil.getRequest();
String tenantId = Func.toStr(request.getHeader(TokenUtil.TENANT_HEADER_KEY), TokenUtil.DEFAULT_TENANT_ID);
Map<String, String> parameters = new LinkedHashMap<>(tokenRequest.getRequestParameters());
String userCode = parameters.get("username");
R<UserInfo> clientUser = userClient.userInfo(TokenUtil.DEFAULT_TENANT_ID, userCode);
if (!clientUser.isSuccess() || null == clientUser.getData() || null == clientUser.getData().getUser()) {
throw new InvalidGrantException("单点登录失败,获取平台用户信息失败,工号:" + userCode);
}
User user = clientUser.getData().getUser();
log.info(">>>>>> 查询平台user成功" + user.getId() + ";" + user.getName());
BladeUserDetails bladeUserDetails = new BladeUserDetails(user.getId(), tenantId,
clientUser.getData().getOauthId(), user.getName(), user.getRealName(), user.getDeptId(),
user.getPostId(), user.getRoleId(), Func.join(clientUser.getData().getRoles()),
Func.toStr(user.getAvatar(), TokenUtil.DEFAULT_AVATAR),
user.getName(), AuthConstant.ENCRYPT + user.getPassword(), null, true, true,
true, true,
AuthorityUtils.commaSeparatedStringToAuthorityList(Func.join(clientUser.getData().getRoles())));
log.info("-----------------开始组装认证数据,关闭密码校验-----------------");
log.info("-----------------生成userAuth-----------------");
Authentication userAuth = new UsernamePasswordAuthenticationToken(bladeUserDetails, null, bladeUserDetails.getAuthorities());
log.info("-----------------setDetails-----------------");
((AbstractAuthenticationToken) userAuth).setDetails(parameters);
log.info("-----------------组装storedOAuth2Request-----------------");
OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
log.info("-----------------返回 OAuth2Authentication-----------------");
return new OAuth2Authentication(storedOAuth2Request, userAuth);
}
}

View File

@ -0,0 +1,130 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.auth.granter;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.request.AuthRequest;
import org.security.auth.constant.AuthConstant;
import org.security.auth.service.BladeUserDetails;
import org.security.auth.utils.TokenUtil;
import org.security.system.user.entity.User;
import org.security.system.user.entity.UserInfo;
import org.security.system.user.entity.UserOauth;
import org.security.system.user.feign.IUserClient;
import org.springblade.core.social.props.SocialProperties;
import org.springblade.core.social.utils.SocialUtil;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.support.Kv;
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.WebUtil;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.oauth2.provider.token.AbstractTokenGranter;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import javax.servlet.http.HttpServletRequest;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
/**
* 第三方登录认证类
*
* @author Chill
*/
public class SocialTokenGranter extends AbstractTokenGranter {
private static final String GRANT_TYPE = "social";
private static final Integer AUTH_SUCCESS_CODE = 2000;
private final IUserClient userClient;
private final SocialProperties socialProperties;
protected SocialTokenGranter(AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, IUserClient userClient, SocialProperties socialProperties) {
super(tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
this.userClient = userClient;
this.socialProperties = socialProperties;
}
@Override
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
// 请求头租户信息
HttpServletRequest request = WebUtil.getRequest();
String tenantId = Func.toStr(request.getHeader(TokenUtil.TENANT_HEADER_KEY), TokenUtil.DEFAULT_TENANT_ID);
Map<String, String> parameters = new LinkedHashMap<>(tokenRequest.getRequestParameters());
// 开放平台来源
String sourceParameter = parameters.get("source");
// 匹配是否有别名定义
String source = socialProperties.getAlias().getOrDefault(sourceParameter, sourceParameter);
// 开放平台授权码
String code = parameters.get("code");
// 开放平台状态吗
String state = parameters.get("state");
// 获取开放平台授权数据
AuthRequest authRequest = SocialUtil.getAuthRequest(source, socialProperties);
AuthCallback authCallback = new AuthCallback();
authCallback.setCode(code);
authCallback.setState(state);
AuthResponse authResponse = authRequest.login(authCallback);
AuthUser authUser;
if (authResponse.getCode() == AUTH_SUCCESS_CODE) {
authUser = (AuthUser) authResponse.getData();
} else {
throw new InvalidGrantException("social grant failure, auth response is not success");
}
// 组装数据
UserOauth userOauth = Objects.requireNonNull(BeanUtil.copy(authUser, UserOauth.class));
userOauth.setSource(authUser.getSource());
userOauth.setTenantId(tenantId);
userOauth.setUuid(authUser.getUuid());
// 远程调用获取认证信息
R<UserInfo> result = userClient.userAuthInfo(userOauth);
BladeUserDetails bladeUserDetails;
if (result.isSuccess()) {
User user = result.getData().getUser();
Kv detail = result.getData().getDetail();
if (user == null) {
throw new InvalidGrantException("social grant failure, user is null");
}
bladeUserDetails = new BladeUserDetails(user.getId(),
tenantId, result.getData().getOauthId(), user.getName(), user.getRealName(), user.getDeptId(), user.getPostId(), user.getRoleId(), Func.join(result.getData().getRoles()), Func.toStr(userOauth.getAvatar(), TokenUtil.DEFAULT_AVATAR),
userOauth.getUsername(), AuthConstant.ENCRYPT + user.getPassword(), detail, true, true, true, true,
AuthorityUtils.commaSeparatedStringToAuthorityList(Func.join(result.getData().getRoles())));
} else {
throw new InvalidGrantException("social grant failure, feign client return error");
}
// 组装认证数据关闭密码校验
Authentication userAuth = new UsernamePasswordAuthenticationToken(bladeUserDetails, null, bladeUserDetails.getAuthorities());
((AbstractAuthenticationToken) userAuth).setDetails(parameters);
OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
// 返回 OAuth2Authentication
return new OAuth2Authentication(storedOAuth2Request, userAuth);
}
}

View File

@ -0,0 +1,106 @@
package org.security.auth.granter;
import lombok.extern.slf4j.Slf4j;
import org.security.auth.config.XYunProperties;
import org.security.auth.constant.AuthConstant;
import org.security.auth.service.BladeUserDetails;
import org.security.auth.service.IXYunAuthService;
import org.security.auth.utils.TokenUtil;
import org.security.system.user.entity.User;
import org.security.system.user.entity.UserInfo;
import org.security.system.user.feign.IUserClient;
import org.security.desk.xyun.res.AuthToken;
import org.security.desk.xyun.res.AuthUser;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.support.Kv;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.WebUtil;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.oauth2.provider.token.AbstractTokenGranter;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import javax.servlet.http.HttpServletRequest;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* 第三方登录认证类
*
* @author Arlex
*/
@Slf4j
public class XYunTokenGranter extends AbstractTokenGranter {
private static final String GRANT_TYPE = "xyun";
private final IUserClient userClient;
private final XYunProperties xYunProperties;
private final IXYunAuthService xYunService;
protected XYunTokenGranter(AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService,
OAuth2RequestFactory requestFactory, IUserClient userClient, XYunProperties xYunProperties,
IXYunAuthService xYunService) {
super(tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
this.userClient = userClient;
this.xYunProperties = xYunProperties;
this.xYunService = xYunService;
}
@Override
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
log.info(">>>>>> 香云端认证开始appId" + xYunProperties.getAppId() + "secret" + xYunProperties.getAppSecret());
// 请求头租户信息
HttpServletRequest request = WebUtil.getRequest();
String tenantId = Func.toStr(request.getHeader(TokenUtil.TENANT_HEADER_KEY), TokenUtil.DEFAULT_TENANT_ID);
Map<String, String> parameters = new LinkedHashMap<>(tokenRequest.getRequestParameters());
String ticket = parameters.get("ticket");
log.info(">>>>>> 当前ticket" + ticket);
if (null == ticket) {
throw new InvalidGrantException("香云登录失败ticket为空");
}
R<AuthToken> tokenRes = xYunService.getAccessToken();
if (!tokenRes.isSuccess()) {
throw new InvalidGrantException(tokenRes.getMsg());
}
R<AuthUser> userRes = xYunService.getUserInfo(ticket, tokenRes.getData().getAccessToken());
if (!userRes.isSuccess()) {
throw new InvalidGrantException("香云登录失败,错误信息:" + userRes.getMsg());
}
AuthUser authUser = userRes.getData();
log.info(">>>>>> 获得香云user成功" + authUser.toString());
R<UserInfo> clientUser = userClient.userInfo(TokenUtil.DEFAULT_TENANT_ID, authUser.getJobNo());
if (!clientUser.isSuccess() || null == clientUser.getData() || null == clientUser.getData().getUser()) {
throw new InvalidGrantException("香云登录失败,获取平台用户信息失败,工号:" + authUser.getJobNo());
}
User user = clientUser.getData().getUser();
log.info(">>>>>> 查询平台user成功" + user.getId() + ";" + user.getName());
Kv detail = clientUser.getData().getDetail();
detail.set("xYunAccessToken", tokenRes.getData().getAccessToken());
detail.set("xYunRefreshToken", tokenRes.getData().getRefreshToken());
BladeUserDetails bladeUserDetails = new BladeUserDetails(user.getId(), tenantId,
clientUser.getData().getOauthId(), user.getName(), user.getRealName(), user.getDeptId(),
user.getPostId(), user.getRoleId(), Func.join(clientUser.getData().getRoles()),
Func.toStr(user.getAvatar(), TokenUtil.DEFAULT_AVATAR),
user.getName(), authUser.getJobNo(), AuthConstant.ENCRYPT + user.getPassword(), detail, true, true,
true, true,
AuthorityUtils.commaSeparatedStringToAuthorityList(Func.join(clientUser.getData().getRoles())));
log.info(">>>>>> 开始组装认证数据,关闭密码校验");
log.info(">>>>>> 生成userAuth");
Authentication userAuth = new UsernamePasswordAuthenticationToken(bladeUserDetails, null, bladeUserDetails.getAuthorities());
log.info(">>>>>> setDetails");
((AbstractAuthenticationToken) userAuth).setDetails(parameters);
log.info(">>>>>> 组装storedOAuth2Request");
OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
log.info(">>>>>> 开始组装认证数据,关闭密码校验");
return new OAuth2Authentication(storedOAuth2Request, userAuth);
}
}

View File

@ -0,0 +1,82 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.auth.handler;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.security.auth.utils.TokenUtil;
import org.springblade.core.tool.jackson.JsonUtil;
import org.springframework.http.MediaType;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
/**
* APP登录成功处理器
*
* @author Chill
*/
@Slf4j
@AllArgsConstructor
@Component("appLoginInSuccessHandler")
public class AppLoginInSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
private final PasswordEncoder passwordEncoder;
private final ClientDetailsService clientDetailsService;
private final AuthorizationServerTokenServices authorizationServerTokenServices;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
log.info("【AppLoginInSuccessHandler】 onAuthenticationSuccess authentication={}", authentication);
String[] tokens = TokenUtil.extractAndDecodeHeader();
if (tokens.length != 2) {
throw new UnapprovedClientAuthenticationException("client对应的配置信息不存在");
}
String clientId = tokens[0];
String clientSecret = tokens[1];
ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);
if (clientDetails == null) {
throw new UnapprovedClientAuthenticationException("clientId 对应的配置信息不存在" + clientId);
} else if (!passwordEncoder.matches(clientSecret, clientDetails.getClientSecret())) {
throw new UnapprovedClientAuthenticationException("clientSecret 不匹配" + clientId);
}
TokenRequest tokenRequest = new TokenRequest(new HashMap<>(16), clientId, clientDetails.getScope(), "app");
OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails);
OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request, authentication);
OAuth2AccessToken token = authorizationServerTokenServices.createAccessToken(oAuth2Authentication);
response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
response.getWriter().write(JsonUtil.toJson(token));
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.auth.service;
import org.security.auth.constant.AuthConstant;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
/**
* 客户端信息
*
* @author Chill
*/
@Component
public class BladeClientDetailsServiceImpl extends JdbcClientDetailsService {
public BladeClientDetailsServiceImpl(DataSource dataSource) {
super(dataSource);
setSelectClientDetailsSql(AuthConstant.DEFAULT_SELECT_STATEMENT);
setFindClientDetailsSql(AuthConstant.DEFAULT_FIND_STATEMENT);
}
/**
* 缓存客户端信息
*
* @param clientId 客户端id
*/
@Override
public ClientDetails loadClientByClientId(String clientId) {
try {
return super.loadClientByClientId(clientId);
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
}

View File

@ -0,0 +1,115 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.auth.service;
import lombok.Getter;
import org.springblade.core.tool.support.Kv;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import java.util.Collection;
/**
* 用户信息拓展
*
* @author Chill
*/
@Getter
public class BladeUserDetails extends User {
/**
* 用户id
*/
private final Long userId;
/**
* 租户ID
*/
private final String tenantId;
/**
* 第三方认证ID
*/
private final String oauthId;
/**
* 昵称
*/
private final String name;
/**
* 真名
*/
private final String realName;
/**
* 账号
*/
private final String account;
/**
* 部门id
*/
private final String deptId;
/**
* 岗位id
*/
private final String postId;
/**
* 角色id
*/
private final String roleId;
/**
* 角色名
*/
private final String roleName;
/**
* 头像
*/
private final String avatar;
/**
* 用户详情
*/
private final Kv detail;
public BladeUserDetails(Long userId, String tenantId, String oauthId, String name, String realName, String deptId, String postId, String roleId, String roleName, String avatar, String username, String password, Kv detail, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {
super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
this.userId = userId;
this.tenantId = tenantId;
this.oauthId = oauthId;
this.name = name;
this.realName = realName;
this.account = username;
this.deptId = deptId;
this.postId = postId;
this.roleId = roleId;
this.roleName = roleName;
this.avatar = avatar;
this.detail = detail;
}
public BladeUserDetails(Long userId, String tenantId, String oauthId, String name, String realName, String deptId, String postId, String roleId, String roleName, String avatar, String username, String account, String password, Kv detail, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {
super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
this.userId = userId;
this.tenantId = tenantId;
this.oauthId = oauthId;
this.name = name;
this.realName = realName;
this.account = account;
this.deptId = deptId;
this.postId = postId;
this.roleId = roleId;
this.roleName = roleName;
this.avatar = avatar;
this.detail = detail;
}
}

View File

@ -0,0 +1,111 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.auth.service;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.apache.commons.lang.StringUtils;
import org.security.auth.constant.AuthConstant;
import org.security.auth.utils.TokenUtil;
import org.security.system.entity.Tenant;
import org.security.system.feign.ISysClient;
import org.security.system.user.entity.User;
import org.security.system.user.entity.UserInfo;
import org.security.system.user.enums.UserEnum;
import org.security.system.user.feign.IUserClient;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.StringPool;
import org.springblade.core.tool.utils.StringUtil;
import org.springblade.core.tool.utils.WebUtil;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.oauth2.common.exceptions.UserDeniedAuthorizationException;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
/**
* 用户信息
*
* @author Chill
*/
@Service
@AllArgsConstructor
public class BladeUserDetailsServiceImpl implements UserDetailsService {
private final IUserClient userClient;
private final ISysClient sysClient;
@Override
@SneakyThrows
public BladeUserDetails loadUserByUsername(String username) {
HttpServletRequest request = WebUtil.getRequest();
// 获取租户ID
String headerTenant = request.getHeader(TokenUtil.TENANT_HEADER_KEY);
String paramTenant = request.getParameter(TokenUtil.TENANT_PARAM_KEY);
if (StringUtil.isAllBlank(headerTenant, paramTenant)) {
throw new UserDeniedAuthorizationException(TokenUtil.TENANT_NOT_FOUND);
}
String tenantId = StringUtils.isBlank(headerTenant) ? paramTenant : headerTenant;
// 获取租户信息
R<Tenant> tenant = sysClient.getTenant(tenantId);
if (tenant.isSuccess()) {
if (TokenUtil.judgeTenant(tenant.getData())) {
throw new UserDeniedAuthorizationException(TokenUtil.USER_HAS_NO_TENANT_PERMISSION);
}
} else {
throw new UserDeniedAuthorizationException(TokenUtil.USER_HAS_NO_TENANT);
}
// 获取用户类型
String userType = Func.toStr(request.getHeader(TokenUtil.USER_TYPE_HEADER_KEY), TokenUtil.DEFAULT_USER_TYPE);
// 远程调用返回数据
R<UserInfo> result;
// 根据不同用户类型调用对应的接口返回数据用户可自行拓展
if (userType.equals(UserEnum.WEB.getName())) {
result = userClient.userInfo(tenantId, username, UserEnum.WEB.getName());
} else if (userType.equals(UserEnum.APP.getName())) {
result = userClient.userInfo(tenantId, username, UserEnum.APP.getName());
} else {
result = userClient.userInfo(tenantId, username, UserEnum.OTHER.getName());
}
// 判断返回信息
if (result.isSuccess()) {
UserInfo userInfo = result.getData();
User user = userInfo.getUser();
if (user == null || user.getId() == null) {
throw new UsernameNotFoundException(TokenUtil.USER_NOT_FOUND);
}
if (Func.isEmpty(userInfo.getRoles())) {
throw new UserDeniedAuthorizationException(TokenUtil.USER_HAS_NO_ROLE);
}
return new BladeUserDetails(user.getId(),
user.getTenantId(), StringPool.EMPTY, user.getName(), user.getRealName(), user.getDeptId(), user.getPostId(), user.getRoleId(), Func.join(result.getData().getRoles()), Func.toStr(user.getAvatar(), TokenUtil.DEFAULT_AVATAR),
username, AuthConstant.ENCRYPT + user.getPassword(), userInfo.getDetail(), true, true, true, true,
AuthorityUtils.commaSeparatedStringToAuthorityList(Func.join(result.getData().getRoles())));
} else {
throw new UsernameNotFoundException(result.getMsg());
}
}
}

View File

@ -0,0 +1,10 @@
package org.security.auth.service;
import org.security.desk.xyun.res.AuthToken;
import org.security.desk.xyun.res.AuthUser;
import org.springblade.core.tool.api.R;
public interface IXYunAuthService {
R<AuthToken> getAccessToken();
R<AuthUser> getUserInfo(String ticket, String accessToken);
}

View File

@ -0,0 +1,111 @@
package org.security.auth.service;
import com.alibaba.fastjson.JSONObject;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.security.auth.config.XYunProperties;
import org.security.common.constant.CommonConstant;
import org.security.desk.xyun.res.AuthResponse;
import org.security.desk.xyun.res.AuthToken;
import org.security.desk.xyun.res.AuthUser;
import org.springblade.core.redis.cache.BladeRedis;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.Func;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.RestTemplate;
import java.time.Duration;
import java.util.HashMap;
@Service
@AllArgsConstructor
@Slf4j
public class IXYunAuthServiceImpl implements IXYunAuthService {
private final XYunProperties xYunProperties;
private final BladeRedis bladeRedis;
@Override
public R getAccessToken() {
String redisToken = bladeRedis.get(CommonConstant.XYUN_ACCESSTOKEN_REDIS_KEY);
if (!Func.isNull(redisToken) && !Func.isEmpty(redisToken)) {
AuthToken authToken = new AuthToken();
authToken.setAccessToken(redisToken);
authToken.setRefreshToken(bladeRedis.get(CommonConstant.XYUN_REFRESHTOKEN_REDIS_KEY));
return R.data(authToken);
}
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setReadTimeout(10000);
factory.setConnectTimeout(10000);
RestTemplate template = new RestTemplate(factory);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.parseMediaType("application/json;charset=UTF-8"));
headers.add("Accept", MediaType.APPLICATION_JSON.toString());
HashMap<String, Object> tokenMap = new HashMap<>();
tokenMap.put("appId", xYunProperties.getAppId());
tokenMap.put("secret", xYunProperties.getAppSecret());
tokenMap.put("timestamp", String.valueOf(System.currentTimeMillis()));
tokenMap.put("scope", "app");
HttpEntity<HashMap<String, Object>> tokenHttpEntity = new HttpEntity<>(tokenMap, headers);
ParameterizedTypeReference<AuthResponse<AuthToken>> tokenTypeRef =
new ParameterizedTypeReference<AuthResponse<AuthToken>>() {
};
AuthResponse<AuthToken> tokenResponse;
String tokenUrl = xYunProperties.getUrl() + "/gateway/oauth2/token/getAccessToken";
log.info(">>>>>> 获取token url" + tokenUrl);
try {
tokenResponse = template.exchange(tokenUrl, HttpMethod.POST, tokenHttpEntity, tokenTypeRef).getBody();
} catch (ResourceAccessException e) {
return R.fail("香云登录失败获取accessToken超时url" + tokenUrl);
}
if (!tokenResponse.isSuccess()) {
return R.fail("香云登录失败香云获取token失败错误码" + tokenResponse.getErrorCode());
}
AuthToken authToken = tokenResponse.getData();
bladeRedis.setEx(CommonConstant.XYUN_ACCESSTOKEN_REDIS_KEY, authToken.getAccessToken(), Duration.ofSeconds(authToken.getExpireIn()));
bladeRedis.setEx(CommonConstant.XYUN_REFRESHTOKEN_REDIS_KEY, authToken.getRefreshToken(), Duration.ofSeconds(authToken.getExpireIn()));
return R.data(authToken);
}
@Override
public R getUserInfo(String ticket, String accessToken) {
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setReadTimeout(10000);
factory.setConnectTimeout(10000);
RestTemplate template = new RestTemplate(factory);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.parseMediaType("application/json;charset=UTF-8"));
headers.add("Accept", MediaType.APPLICATION_JSON.toString());
HashMap<String, Object> userMap = new HashMap<>();
userMap.put("appid", xYunProperties.getAppId());
userMap.put("ticket", ticket);
log.info(">>>>>> 获得香云accessToken成功" + accessToken);
HttpEntity<HashMap<String, Object>> userHttpEntity = new HttpEntity<>(userMap, headers);
ParameterizedTypeReference<AuthResponse<AuthUser>> userTypeRef =
new ParameterizedTypeReference<AuthResponse<AuthUser>>() {
};
AuthResponse<AuthUser> userResponse;
String userUrl = xYunProperties.getUrl() + "/gateway/ticket/user/acquirecontext?accessToken=" + accessToken;
log.info(">>>>>> 获取user url" + userUrl);
try {
userResponse = template.exchange(userUrl, HttpMethod.POST, userHttpEntity, userTypeRef).getBody();
} catch (ResourceAccessException e) {
return R.fail("香云登录失败获取用户信息超时url" + userUrl);
}
log.info(JSONObject.toJSONString(userResponse));
if (!userResponse.isSuccess()) {
return R.fail("香云登录失败,香云获取用户信息失败,错误码:"
+ userResponse.getErrorCode() + "; " + userResponse.getError());
}
return R.data(userResponse.getData());
}
}

View File

@ -0,0 +1,79 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.auth.support;
import lombok.AllArgsConstructor;
import org.security.auth.service.BladeUserDetails;
import org.security.auth.utils.TokenUtil;
import org.springblade.core.jwt.JwtUtil;
import org.springblade.core.jwt.props.JwtProperties;
import org.springblade.core.tool.utils.Func;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import java.util.HashMap;
import java.util.Map;
/**
* jwt返回参数增强
*
* @author Chill
*/
@AllArgsConstructor
public class BladeJwtTokenEnhancer implements TokenEnhancer {
private final JwtAccessTokenConverter jwtAccessTokenConverter;
private final JwtProperties jwtProperties;
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
BladeUserDetails principal = (BladeUserDetails) authentication.getUserAuthentication().getPrincipal();
//token参数增强
Map<String, Object> info = new HashMap<>(16);
info.put(TokenUtil.CLIENT_ID, TokenUtil.getClientIdFromHeader());
info.put(TokenUtil.USER_ID, Func.toStr(principal.getUserId()));
info.put(TokenUtil.DEPT_ID, Func.toStr(principal.getDeptId()));
info.put(TokenUtil.POST_ID, Func.toStr(principal.getPostId()));
info.put(TokenUtil.ROLE_ID, Func.toStr(principal.getRoleId()));
info.put(TokenUtil.TENANT_ID, principal.getTenantId());
info.put(TokenUtil.OAUTH_ID, principal.getOauthId());
info.put(TokenUtil.ACCOUNT, principal.getAccount());
info.put(TokenUtil.USER_NAME, principal.getUsername());
info.put(TokenUtil.NICK_NAME, principal.getName());
info.put(TokenUtil.REAL_NAME, principal.getRealName());
info.put(TokenUtil.ROLE_NAME, principal.getRoleName());
info.put(TokenUtil.AVATAR, principal.getAvatar());
info.put(TokenUtil.DETAIL, principal.getDetail());
info.put(TokenUtil.LICENSE, TokenUtil.LICENSE_NAME);
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(info);
//token状态设置
if (jwtProperties.getState()) {
OAuth2AccessToken oAuth2AccessToken = jwtAccessTokenConverter.enhance(accessToken, authentication);
String tokenValue = oAuth2AccessToken.getValue();
String tenantId = principal.getTenantId();
String userId = Func.toStr(principal.getUserId());
JwtUtil.addAccessToken(tenantId, userId, tokenValue, accessToken.getExpiresIn());
}
return accessToken;
}
}

View File

@ -0,0 +1,50 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.auth.support;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* 无密码加密
*
* @author Chill
*/
public class BladeNoOpPasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence rawPassword) {
return rawPassword.toString();
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return rawPassword.toString().equals(encodedPassword);
}
/**
* Get the singleton {@link BladeNoOpPasswordEncoder}.
*/
public static PasswordEncoder getInstance() {
return INSTANCE;
}
private static final PasswordEncoder INSTANCE = new BladeNoOpPasswordEncoder();
private BladeNoOpPasswordEncoder() {
}
}

View File

@ -0,0 +1,39 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.auth.support;
import org.springblade.core.tool.utils.DigestUtil;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* 自定义密码加密
*
* @author Chill
*/
public class BladePasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence rawPassword) {
return DigestUtil.hex((String) rawPassword);
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return encodedPassword.equals(encode(rawPassword));
}
}

View File

@ -0,0 +1,66 @@
/*
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.security.auth.support;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder;
import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;
import java.util.HashMap;
import java.util.Map;
/**
* 自定义密码工厂
*
* @author Rob Winch, Chill
* @since 5.0
*/
public class BladePasswordEncoderFactories {
/**
* Creates a {@link DelegatingPasswordEncoder} with default mappings. Additional
* mappings may be added and the encoding will be updated to conform with best
* practices. However, due to the nature of {@link DelegatingPasswordEncoder} the
* updates should not impact users. The mappings current are:
*
* <ul>
* <li>blade - {@link BladePasswordEncoder} (sha1(md5("password")))</li>
* <li>bcrypt - {@link BCryptPasswordEncoder} (Also used for encoding)</li>
* <li>noop - {@link BladeNoOpPasswordEncoder}</li>
* <li>pbkdf2 - {@link Pbkdf2PasswordEncoder}</li>
* <li>scrypt - {@link SCryptPasswordEncoder}</li>
* </ul>
*
* @return the {@link PasswordEncoder} to use
*/
public static PasswordEncoder createDelegatingPasswordEncoder() {
String encodingId = "blade";
Map<String, PasswordEncoder> encoders = new HashMap<>(16);
encoders.put(encodingId, new BladePasswordEncoder());
encoders.put("bcrypt", new BCryptPasswordEncoder());
encoders.put("noop", BladeNoOpPasswordEncoder.getInstance());
encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
encoders.put("scrypt", new SCryptPasswordEncoder());
return new DelegatingPasswordEncoder(encodingId, encoders);
}
private BladePasswordEncoderFactories() {
}
}

View File

@ -0,0 +1,175 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.auth.utils;
import lombok.SneakyThrows;
import org.security.common.constant.TenantConstant;
import org.security.system.entity.Tenant;
import org.springblade.core.launch.constant.TokenConstant;
import org.springblade.core.tenant.BladeTenantProperties;
import org.springblade.core.tool.constant.BladeConstant;
import org.springblade.core.tool.jackson.JsonUtil;
import org.springblade.core.tool.utils.*;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException;
import org.springframework.security.oauth2.common.exceptions.UserDeniedAuthorizationException;
import java.util.Base64;
import java.util.Calendar;
import java.util.Date;
/**
* 认证工具类
*
* @author Chill
*/
public class TokenUtil {
public final static String AVATAR = TokenConstant.AVATAR;
public final static String ACCOUNT = TokenConstant.ACCOUNT;
public final static String USER_NAME = TokenConstant.USER_NAME;
public final static String NICK_NAME = TokenConstant.NICK_NAME;
public final static String REAL_NAME = TokenConstant.REAL_NAME;
public final static String USER_ID = TokenConstant.USER_ID;
public final static String DEPT_ID = TokenConstant.DEPT_ID;
public final static String POST_ID = TokenConstant.POST_ID;
public final static String ROLE_ID = TokenConstant.ROLE_ID;
public final static String ROLE_NAME = TokenConstant.ROLE_NAME;
public final static String TENANT_ID = TokenConstant.TENANT_ID;
public final static String OAUTH_ID = TokenConstant.OAUTH_ID;
public final static String CLIENT_ID = TokenConstant.CLIENT_ID;
public final static String DETAIL = TokenConstant.DETAIL;
public final static String LICENSE = TokenConstant.LICENSE;
public final static String LICENSE_NAME = TokenConstant.LICENSE_NAME;
public final static String CAPTCHA_HEADER_KEY = "Captcha-Key";
public final static String CAPTCHA_HEADER_CODE = "Captcha-Code";
public final static String CAPTCHA_NOT_CORRECT = "验证码不正确";
public final static String TENANT_HEADER_KEY = "Tenant-Id";
public final static String TENANT_PARAM_KEY = "tenant_id";
public final static String DEFAULT_TENANT_ID = "000000";
public final static String TENANT_NOT_FOUND = "租户ID未找到";
public final static String USER_TYPE_HEADER_KEY = "User-Type";
public final static String DEFAULT_USER_TYPE = "web";
public final static String USER_NOT_FOUND = "用户名或密码错误";
public final static String USER_HAS_NO_ROLE = "未获得用户的角色信息";
public final static String USER_HAS_NO_TENANT = "未获得用户的租户信息";
public final static String USER_HAS_NO_TENANT_PERMISSION = "租户授权已过期,请联系管理员";
public final static String HEADER_KEY = "Authorization";
public final static String HEADER_PREFIX = "Basic ";
public final static String DEFAULT_AVATAR = "";
private static BladeTenantProperties tenantProperties;
/**
* 获取租户配置
*
* @return tenantProperties
*/
private static BladeTenantProperties getTenantProperties() {
if (tenantProperties == null) {
tenantProperties = SpringUtil.getBean(BladeTenantProperties.class);
}
return tenantProperties;
}
/**
* 解码
*/
@SneakyThrows
public static String[] extractAndDecodeHeader() {
String header = WebUtil.getRequest().getHeader(TokenUtil.HEADER_KEY);
if (header == null || !header.startsWith(TokenUtil.HEADER_PREFIX)) {
throw new UnapprovedClientAuthenticationException("请求头中无client信息");
}
byte[] base64Token = header.substring(6).getBytes(Charsets.UTF_8_NAME);
byte[] decoded;
try {
decoded = Base64.getDecoder().decode(base64Token);
} catch (IllegalArgumentException var7) {
throw new BadCredentialsException("Failed to decode basic authentication token");
}
String token = new String(decoded, Charsets.UTF_8_NAME);
int index = token.indexOf(StringPool.COLON);
if (index == -1) {
throw new BadCredentialsException("Invalid basic authentication token");
} else {
return new String[]{token.substring(0, index), token.substring(index + 1)};
}
}
/**
* 获取请求头中的客户端id
*/
public static String getClientIdFromHeader() {
String[] tokens = extractAndDecodeHeader();
return tokens[0];
}
/**
* 获取token过期时间(次日凌晨3点)
*
* @return expire
*/
public static int getTokenValiditySecond() {
Calendar cal = Calendar.getInstance();
cal.add(Calendar.DAY_OF_YEAR, 1);
cal.set(Calendar.HOUR_OF_DAY, 3);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.MILLISECOND, 0);
return (int) (cal.getTimeInMillis() - System.currentTimeMillis()) / 1000;
}
/**
* 获取refreshToken过期时间
*
* @return expire
*/
public static int getRefreshTokenValiditySeconds() {
return 60 * 60 * 24 * 15;
}
/**
* 判断租户权限
*
* @param tenant 租户信息
* @return boolean
*/
public static boolean judgeTenant(Tenant tenant) {
if (tenant == null || tenant.getId() == null) {
throw new UserDeniedAuthorizationException(TokenUtil.USER_HAS_NO_TENANT);
}
if (StringUtil.equalsIgnoreCase(tenant.getTenantId(), BladeConstant.ADMIN_TENANT_ID)) {
return false;
}
Date expireTime = tenant.getExpireTime();
if (getTenantProperties().getLicense()) {
String licenseKey = tenant.getLicenseKey();
String decrypt = DesUtil.decryptFormHex(licenseKey, TenantConstant.DES_KEY);
expireTime = JsonUtil.parse(decrypt, Tenant.class).getExpireTime();
}
if (expireTime != null && expireTime.before(DateUtil.now())) {
throw new UserDeniedAuthorizationException(TokenUtil.USER_HAS_NO_TENANT_PERMISSION);
}
return false;
}
}

View File

@ -0,0 +1,21 @@
#数据源配置
spring:
datasource:
url: ${blade.datasource.dev.url}
username: ${blade.datasource.dev.username}
password: ${blade.datasource.dev.password}
#第三方登陆
social:
enabled: true
domain: http://127.0.0.1:1888
xyun:
app-id: 500000053
app-secret: B6rbJdILa1VEO9fmn6zj
url: http://197.52.23.10
# 公共号id
pub-id: XT-cb5053a4-3d00-47e7-a0c3-d0ac693305b7
pub-secret: 812739e21bf781370bebec37146c38a8
# 企业注册号eid
e-id: 1556583279

View File

@ -0,0 +1,21 @@
#数据源配置
spring:
datasource:
url: ${blade.datasource.prod.url}
username: ${blade.datasource.prod.username}
password: ${blade.datasource.prod.password}
#第三方登陆
social:
enabled: true
domain: http://127.0.0.1:1888
xyun:
app-id: 500000145
app-secret: pzWlUBKsGQZFgIRWwDvV
url: http://im.ynhtbank.cn
# 公共号id
pub-id: XT-d5a6d607-3549-465a-8477-601fc366039d
pub-secret: 7e37f7ef5653889013060bfaaa69ccf7
# 企业注册号eid
e-id: 15750331

View File

@ -0,0 +1,22 @@
#数据源配置
spring:
datasource:
url: ${blade.datasource.test.url}
username: ${blade.datasource.test.username}
password: ${blade.datasource.test.password}
#第三方登陆
social:
enabled: true
domain: http://127.0.0.1:1888
xyun:
app-id: 500000053
app-secret: B6rbJdILa1VEO9fmn6zj
url: http://197.52.23.10
# 公共号id
pub-id: XT-cb5053a4-3d00-47e7-a0c3-d0ac693305b7
pub-secret: 812739e21bf781370bebec37146c38a8
# 企业注册号eid
e-id: 1556583279

View File

@ -0,0 +1,51 @@
# 在使用Spring默认数据源Hikari的情况下配置以下配置项
spring:
datasource:
hikari:
# 自动提交从池中返回的连接
auto-commit: true
# 连接池中维护的最小空闲连接数
minimum-idle: 10
# 连接池中允许的最大连接数。缺省值10推荐的公式((core_count * 2) + effective_spindle_count)
maximum-pool-size: 60
# 空闲连接超时时间默认值60000010分钟大于等于max-lifetime且max-lifetime>0会被重置为0不等于0且小于10秒会被重置为10秒。
# 只有空闲连接数大于最大连接数且空闲时间超过该值,才会被释放
idle-timeout: 30000
# 连接最大存活时间.不等于0且小于30秒会被重置为默认值30分钟.设置应该比mysql设置的超时时间短
max-lifetime: 1800000
# 等待连接池分配连接的最大时长毫秒超过这个时长还没可用的连接则发生SQLException 缺省:30秒
connection-timeout: 30000
# 连接测试查询
connection-test-query: select 1
#connection-test-query: select 1 from dual
#swagger文档
swagger:
base-packages:
- org.springblade
- org.springframework.security.oauth2.provider.endpoint
#第三方登陆
social:
oauth:
GITHUB:
client-id: 233************
client-secret: 233************************************
redirect-uri: ${social.domain}/oauth/redirect/github
GITEE:
client-id: 233************
client-secret: 233************************************
redirect-uri: ${social.domain}/oauth/redirect/gitee
WECHAT_OPEN:
client-id: 233************
client-secret: 233************************************
redirect-uri: ${social.domain}/oauth/redirect/wechat
QQ:
client-id: 233************
client-secret: 233************************************
redirect-uri: ${social.domain}/oauth/redirect/qq
DINGTALK:
client-id: 233************
client-secret: 233************************************
redirect-uri: ${social.domain}/oauth/redirect/dingtalk

View File

@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<property name="logdir" value="../logs/security-auth"/>
<!-- 控制台输出日志 -->
<!-- 彩色日志依赖的渲染类 -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
<conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
<!-- 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}" />
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>utf8</charset>
</encoder>
</appender>
<!-- 按用户输出日志 -->
<appender name="SIFT" class="ch.qos.logback.classic.sift.SiftingAppender">
<discriminator>
<Key>txcode</Key>
<DefaultValue>security-auth</DefaultValue>
</discriminator>
<sift>
<appender name="FILE-${userid}" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>${logdir}/${txcode}.log</File>
<Append>false</Append>
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} %msg%n</Pattern>
</layout>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<FileNamePattern>${logdir}/${txcode}.%d{yyyy-MM-dd}.%i.log</FileNamePattern>
<MaxHistory>20</MaxHistory>
<maxFileSize>5MB</maxFileSize>
<totalSizeCap>1024MB</totalSizeCap>
</rollingPolicy>
</appender>
</sift>
</appender>
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
<includeCallerData>true</includeCallerData>
<discardingThreshold>-1</discardingThreshold>
<queueSize>1024</queueSize>
<appender-ref ref="SIFT"/>
</appender>
<!-- <logger name="dao" level="DEBUG"/>-->
<!-- <logger name="net.sf.ehcache" level="INFO"/>-->
<!-- <logger name="druid.sql" level="INFO"/>-->
<!-- MyBatis log configure -->
<logger name="com.apache.ibatis" level="INFO"/>
<logger name="org.mybatis.spring" level="DEBUG"/>
<logger name="java.sql.Connection" level="DEBUG"/>
<logger name="java.sql.Statement" level="DEBUG"/>
<logger name="java.sql.PreparedStatement" level="DEBUG"/>
<!-- 减少部分debug日志 -->
<logger name="druid.sql" level="INFO"/>
<logger name="org.apache.shiro" level="INFO"/>
<logger name="org.mybatis.spring" level="INFO"/>
<logger name="org.springframework" level="INFO"/>
<logger name="org.springframework.context" level="WARN"/>
<logger name="org.springframework.beans" level="WARN"/>
<logger name="com.baomidou.mybatisplus" level="INFO"/>
<logger name="org.apache.ibatis.io" level="INFO"/>
<logger name="org.apache.velocity" level="INFO"/>
<logger name="org.eclipse.jetty" level="INFO"/>
<logger name="io.undertow" level="INFO"/>
<logger name="org.xnio.nio" level="INFO"/>
<logger name="org.thymeleaf" level="INFO"/>
<logger name="springfox.documentation" level="INFO"/>
<logger name="org.hibernate.validator" level="INFO"/>
<logger name="com.netflix.loadbalancer" level="INFO"/>
<logger name="com.netflix.hystrix" level="INFO"/>
<logger name="com.netflix.zuul" level="INFO"/>
<logger name="de.codecentric" level="INFO"/>
<!-- cache INFO -->
<logger name="net.sf.ehcache" level="INFO"/>
<logger name="org.springframework.cache" level="INFO"/>
<!-- cloud -->
<logger name="org.apache.http" level="INFO"/>
<logger name="com.netflix.discovery" level="INFO"/>
<logger name="com.netflix.eureka" level="INFO"/>
<!-- 业务日志 -->
<Logger name="org.security" level="DEBUG" />
<Logger name="org.springblade.core.version" level="INFO"/>
<!-- 减少nacos日志 -->
<logger name="com.alibaba.nacos" level="ERROR"/>
<root level="INFO">
<appender-ref ref="STDOUT" />
<appender-ref ref="ASYNC"/>
</root>
</configuration>

59
security-common/pom.xml Normal file
View File

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>security-manage</artifactId>
<groupId>org.security</groupId>
<version>2.8.1.RELEASE</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>security-common</artifactId>
<name>${project.artifactId}</name>
<version>${bladex.project.version}</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-launch</artifactId>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-starter-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-auto</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<dependency>
<groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId>
<version>0.4.8</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<skip>true</skip>
<finalName>${project.name}</finalName>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,28 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.common.cache;
/**
* 缓存名
*
* @author Chill
*/
public interface CacheNames {
String CAPTCHA_KEY = "blade:auth::blade:captcha:";
}

View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.common.config;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Configuration;
/**
* 公共封装包配置类
*
* @author Chill
*/
@Configuration
@AllArgsConstructor
public class BladeCommonConfiguration {
}

View File

@ -0,0 +1,29 @@
package org.security.common.config;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
@Slf4j
public class FeignClientRequestInterceptor implements RequestInterceptor {
private final String AuthHeader = "blade-Auth";
@Override
public void apply(RequestTemplate template) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (null != attributes) {
HttpServletRequest request = attributes.getRequest();
if (null != request) {
String bladeAuth = request.getHeader(AuthHeader);
if (null != bladeAuth) {
log.info("<<<<<< Add Header " + AuthHeader + ": " + bladeAuth);
template.header(AuthHeader, bladeAuth);
}
}
}
}
}

View File

@ -0,0 +1,105 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.common.constant;
/**
* 通用常量
*
* @author Chill
*/
public interface CommonConstant {
/**
* 系统id
*/
String SYS_ID = "SIMS";
/**
* 人脸代码库
*/
String FACE_LIB_CODE = "sims001";;
/**
* 生物识别系统场景码
*/
String FACE_SCE_CODE = "3005";
String SEQ_NO = "seqNo";
/**
* sword 系统名
*/
String SWORD_NAME = "sword";
/**
* saber 系统名
*/
String SABER_NAME = "saber";
/**
* 顶级父节点id
*/
Long TOP_PARENT_ID = 0L;
/**
* 顶级父节点名称
*/
String TOP_PARENT_NAME = "顶级";
/**
* 未封存状态值
*/
Integer NOT_SEALED_ID = 0;
/**
* 默认密码
*/
String DEFAULT_PASSWORD = "123456";
/**
* 默认密码参数值
*/
String DEFAULT_PARAM_PASSWORD = "account.initPassword";
/**
* 默认排序字段
*/
String SORT_FIELD = "sort";
/**
* 数据权限类型
*/
Integer DATA_SCOPE_CATEGORY = 1;
/**
* 接口权限类型
*/
Integer API_SCOPE_CATEGORY = 2;
String XYUN_ACCESSTOKEN_REDIS_KEY = "xYunAccessToken";
String XYUN_REFRESHTOKEN_REDIS_KEY = "xYunRefreshToken";
String APPLICATION_AUTH_NAME = "security-auth";
String APPLICATION_GATEWAY_NAME = "security-gateway";
String APPLICATION_DEVELOP_NAME = "security-develop";
String APPLICATION_FLOW_NAME = "security-flow";
String APPLICATION_DESK_NAME = "security-desk";
String APPLICATION_SYSTEM_NAME = "security-system";
String APPLICATION_USER_NAME = "security-user";
String APPLICATION_LOG_NAME = "security-log";
String APPLICATION_FLOWDESIGN_NAME = "security-flow-design";
String APPLICATION_SWAGGER_NAME = "security-swagger";
String APPLICATION_SECURITY = "security-busi";
}

View File

@ -0,0 +1,231 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.common.constant;
import org.springblade.core.launch.constant.AppConstant;
import static org.springblade.core.launch.constant.AppConstant.APPLICATION_NAME_PREFIX;
/**
* 启动常量
*
* @author Chill
*/
public interface LauncherConstant {
/**
* xxljob
*/
String APPLICATION_XXLJOB_NAME = APPLICATION_NAME_PREFIX + "xxljob";
/**
* xxljob
*/
String APPLICATION_XXLJOB_ADMIN_NAME = APPLICATION_NAME_PREFIX + "xxljob-admin";
/**
* nacos dev 地址
*/
String NACOS_DEV_ADDR = "127.0.0.1:8848";
/**
* nacos prod 地址
*/
String NACOS_PROD_ADDR = "127.0.0.1:8848";
/**
* nacos test 地址
*/
String NACOS_TEST_ADDR = "127.0.0.1:8848";
/**
* sentinel dev 地址
*/
String SENTINEL_DEV_ADDR = "47.108.70.243:8858";
/**
* sentinel prod 地址
*/
String SENTINEL_PROD_ADDR = "172.30.0.58:8858";
/**
* sentinel test 地址
*/
String SENTINEL_TEST_ADDR = "172.30.0.58:8858";
/**
* seata dev 地址
*/
String SEATA_DEV_ADDR = "47.108.70.243:8091";
/**
* seata prod 地址
*/
String SEATA_PROD_ADDR = "172.30.0.68:8091";
/**
* seata test 地址
*/
String SEATA_TEST_ADDR = "172.30.0.68:8091";
/**
* zipkin dev 地址
*/
String ZIPKIN_DEV_ADDR = "http://47.108.70.243:9411";
/**
* zipkin prod 地址
*/
String ZIPKIN_PROD_ADDR = "http://172.30.0.71:9411";
/**
* zipkin test 地址
*/
String ZIPKIN_TEST_ADDR = "http://172.30.0.71:9411";
/**
* elk dev 地址
*/
String ELK_DEV_ADDR = "47.108.70.243:9000";
/**
* elk prod 地址
*/
String ELK_PROD_ADDR = "172.30.0.72:9000";
/**
* elk test 地址
*/
String ELK_TEST_ADDR = "172.30.0.72:9000";
/**
* seata file模式
*/
String FILE_MODE = "file";
/**
* seata nacos模式
*/
String NACOS_MODE = "nacos";
/**
* seata default模式
*/
String DEFAULT_MODE = "default";
/**
* seata group后缀
*/
String GROUP_NAME = "-group";
/**
* seata 服务组格式
*
* @param appName 服务名
* @return group
*/
static String seataServiceGroup(String appName) {
return appName.concat(GROUP_NAME);
}
/**
* 动态获取nacos地址
*
* @param profile 环境变量
* @return addr
*/
static String nacosAddr(String profile) {
switch (profile) {
case (AppConstant.PROD_CODE):
return NACOS_PROD_ADDR;
case (AppConstant.TEST_CODE):
return NACOS_TEST_ADDR;
default:
return NACOS_DEV_ADDR;
}
}
/**
* 动态获取sentinel地址
*
* @param profile 环境变量
* @return addr
*/
static String sentinelAddr(String profile) {
switch (profile) {
case (AppConstant.PROD_CODE):
return SENTINEL_PROD_ADDR;
case (AppConstant.TEST_CODE):
return SENTINEL_TEST_ADDR;
default:
return SENTINEL_DEV_ADDR;
}
}
/**
* 动态获取seata地址
*
* @param profile 环境变量
* @return addr
*/
static String seataAddr(String profile) {
switch (profile) {
case (AppConstant.PROD_CODE):
return SEATA_PROD_ADDR;
case (AppConstant.TEST_CODE):
return SEATA_TEST_ADDR;
default:
return SEATA_DEV_ADDR;
}
}
/**
* 动态获取zipkin地址
*
* @param profile 环境变量
* @return addr
*/
static String zipkinAddr(String profile) {
switch (profile) {
case (AppConstant.PROD_CODE):
return ZIPKIN_PROD_ADDR;
case (AppConstant.TEST_CODE):
return ZIPKIN_TEST_ADDR;
default:
return ZIPKIN_DEV_ADDR;
}
}
/**
* 动态获取elk地址
*
* @param profile 环境变量
* @return addr
*/
static String elkAddr(String profile) {
switch (profile) {
case (AppConstant.PROD_CODE):
return ELK_PROD_ADDR;
case (AppConstant.TEST_CODE):
return ELK_TEST_ADDR;
default:
return ELK_DEV_ADDR;
}
}
}

View File

@ -0,0 +1,66 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.common.constant;
import java.util.Arrays;
import java.util.List;
/**
* 租户常量
*
* @author Chill
*/
public interface TenantConstant {
/**
* 租户默认密码KEY
*/
String PASSWORD_KEY = "tenant.default.password";
/**
* 租户默认账号额度KEY
*/
String ACCOUNT_NUMBER_KEY = "tenant.default.accountNumber";
/**
* 租户默认菜单集合KEY
*/
String ACCOUNT_MENU_CODE_KEY = "tenant.default.menuCode";
/**
* 租户默认密码
*/
String DEFAULT_PASSWORD = "123456";
/**
* 租户授权码默认16位密钥
*/
String DES_KEY = "0000000000000000";
/**
* 租户默认账号额度
*/
Integer DEFAULT_ACCOUNT_NUMBER = -1;
/**
* 租户默认菜单集合
*/
List<String> MENU_CODES = Arrays.asList(
"desk", "flow", "work", "monitor", "resource", "role", "user", "dept", "dictbiz", "topmenu"
);
}

View File

@ -0,0 +1,61 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.common.launch;
import org.security.common.constant.LauncherConstant;
import org.springblade.core.auto.service.AutoService;
import org.springblade.core.launch.service.LauncherService;
import org.springblade.core.launch.utils.PropsUtil;
import org.springframework.boot.builder.SpringApplicationBuilder;
import java.util.Properties;
/**
* 启动参数拓展
*
* @author smallchil
*/
@AutoService(LauncherService.class)
public class LauncherServiceImpl implements LauncherService {
@Override
public void launcher(SpringApplicationBuilder builder, String appName, String profile, boolean isLocalDev) {
Properties props = System.getProperties();
// 通用注册
PropsUtil.setProperty(props, "spring.cloud.nacos.discovery.server-addr", LauncherConstant.nacosAddr(profile));
PropsUtil.setProperty(props, "spring.cloud.nacos.config.server-addr", LauncherConstant.nacosAddr(profile));
PropsUtil.setProperty(props, "spring.cloud.sentinel.transport.dashboard", LauncherConstant.sentinelAddr(profile));
PropsUtil.setProperty(props, "spring.zipkin.base-url", LauncherConstant.zipkinAddr(profile));
PropsUtil.setProperty(props, "spring.datasource.dynamic.enabled", "false");
// 开启elk日志
// PropsUtil.setProperty(props, "blade.log.elk.destination", LauncherConstant.elkAddr(profile));
// seata注册地址
// PropsUtil.setProperty(props, "seata.service.grouplist.default", LauncherConstant.seataAddr(profile));
// seata注册group格式
// PropsUtil.setProperty(props, "seata.tx-service-group", LauncherConstant.seataServiceGroup(appName));
// seata配置服务group
// PropsUtil.setProperty(props, "seata.service.vgroup-mapping.".concat(LauncherConstant.seataServiceGroup(appName)), LauncherConstant.DEFAULT_MODE);
// seata注册模式配置
// PropsUtil.setProperty(props, "seata.registry.type", LauncherConstant.NACOS_MODE);
// PropsUtil.setProperty(props, "seata.registry.nacos.server-addr", LauncherConstant.nacosAddr(profile));
// PropsUtil.setProperty(props, "seata.config.type", LauncherConstant.NACOS_MODE);
// PropsUtil.setProperty(props, "seata.config.nacos.server-addr", LauncherConstant.nacosAddr(profile));
}
}

View File

@ -0,0 +1,180 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.common.utils;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;
/**
* 通用工具类
*
* @author Chill
*/
@Slf4j
public class CommonUtil {
public static String getIpAddress() {
Enumeration en;
try {
en = NetworkInterface.getNetworkInterfaces();
} catch (SocketException var8) {
log.error(var8.getMessage());
return "127.0.0.1";
}
ArrayList tmpMacList = new ArrayList();
while(en.hasMoreElements()) {
NetworkInterface iface = (NetworkInterface)en.nextElement();
List<InterfaceAddress> addrs = iface.getInterfaceAddresses();
Iterator var4 = addrs.iterator();
while(var4.hasNext()) {
InterfaceAddress addr = (InterfaceAddress)var4.next();
InetAddress ip = addr.getAddress();
if (ip != null) {
String newIP = ip.toString().replace("/", "");
if (!newIP.contains("127")) {
tmpMacList.add(newIP);
}
}
}
}
if (tmpMacList.size() <= 0) {
return "127.0.0.1";
} else {
List<String> unique = (List)tmpMacList.stream().distinct().collect(Collectors.toList());
return (String)unique.get(0);
}
}
public static String getMacAddress() {
ArrayList tmpMacList = new ArrayList();
try {
Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces();
StringBuilder sb = new StringBuilder();
label53:
while(en.hasMoreElements()) {
NetworkInterface iface = (NetworkInterface)en.nextElement();
List<InterfaceAddress> addrs = iface.getInterfaceAddresses();
Iterator var5 = addrs.iterator();
while(true) {
byte[] mac;
do {
NetworkInterface network;
do {
if (!var5.hasNext()) {
continue label53;
}
InterfaceAddress addr = (InterfaceAddress)var5.next();
InetAddress ip = addr.getAddress();
network = NetworkInterface.getByInetAddress(ip);
} while(network == null);
mac = network.getHardwareAddress();
} while(mac == null);
sb.delete(0, sb.length());
for(int i = 0; i < mac.length; ++i) {
sb.append(String.format("%02X%s", mac[i], i < mac.length - 1 ? "-" : ""));
}
tmpMacList.add(sb.toString());
}
}
} catch (Exception var11) {
log.error(var11.getMessage());
return "00-50-56-9C-0D-0A";
}
if (tmpMacList.size() <= 0) {
return "00-50-56-9C-0D-0A";
} else {
List<String> unique = (List)tmpMacList.stream().distinct().collect(Collectors.toList());
return (String)unique.get(0);
}
}
public static String compressBase64Image(String formatName, String base64, int maxLength, Double quality, Double step) throws IOException {
if (base64.length() > maxLength) {
BASE64Decoder decoder = new BASE64Decoder();
byte[] bytes = decoder.decodeBuffer(base64);
ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
BufferedImage src = ImageIO.read(stream);
int width = src.getWidth();
int height = src.getHeight();
BufferedImage output = Thumbnails.of(src).size(src.getWidth(), src.getHeight()).asBufferedImage();
String result = imageToBase64(formatName, output);
quality = null == quality ? 0.95 : quality;
output = Thumbnails.of(output).scale(quality).asBufferedImage();
step = null == step ? 0.05 : step;
base64 = imageToBase64(formatName, output);
if (base64.length() > maxLength) {
base64 = compressBase64Image(formatName, base64, maxLength, quality - step, step);
}
}
return base64;
}
private static String imageToBase64(String formatName, BufferedImage bufferedImage) throws IOException {
Base64 encoder = new Base64();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ImageIO.write(bufferedImage, formatName, byteArrayOutputStream);
return new String(encoder.encode(byteArrayOutputStream.toByteArray()));
}
public static String multipartFileToBase64(MultipartFile file) {
String base64 = "";
if (null != file) {
BASE64Encoder encoder = new BASE64Encoder();
try {
base64 = encoder.encode(file.getBytes());
} catch (IOException e) {
e.printStackTrace();
return "";
}
}
return base64.replaceAll("\r\n", "");
}
}

View File

@ -0,0 +1,37 @@
package org.security.common.utils;
import org.apache.commons.io.IOUtils;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
public class InputStreamAESUtil {
// 加密盐
private static final String key = "mb69ZaWX7NhdsbJR";
// 加密向量
private static final String initVector = "fquxS65OPV9WEOgx";
public static InputStream encryptStream(InputStream is) throws Exception {
byte[] nonEncryptedByteArray = IOUtils.toByteArray(is);
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes(StandardCharsets.UTF_8));
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);
byte[] encryptedByteArray = cipher.doFinal(nonEncryptedByteArray);
return new ByteArrayInputStream(encryptedByteArray);
}
public static InputStream decryptStream(InputStream is) throws Exception {
byte[] encryptToDecryptByteArray = IOUtils.toByteArray(is);
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes(StandardCharsets.UTF_8));
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
byte[] decryptedByteArray = cipher.doFinal(encryptToDecryptByteArray);
return new ByteArrayInputStream(decryptedByteArray);
}
}

View File

@ -0,0 +1 @@
org.security.common.launch.LauncherServiceImpl

View File

@ -0,0 +1,5 @@
==================================================================
= =
= 安防信息化管理 =
= =
==================================================================

115
security-gateway/pom.xml Normal file
View File

@ -0,0 +1,115 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>security-manage</artifactId>
<groupId>org.security</groupId>
<version>2.8.1.RELEASE</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>security-gateway</artifactId>
<name>${project.artifactId}</name>
<version>${bladex.project.version}</version>
<packaging>jar</packaging>
<dependencies>
<!--Blade-->
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-launch</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.security</groupId>
<artifactId>security-common</artifactId>
<exclusions>
<exclusion>
<groupId>org.springblade</groupId>
<artifactId>blade-core-launch</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-starter-metrics</artifactId>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-starter-jwt</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<!--Spring-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
</dependency>
<!--Hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<exclusions>
<exclusion>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<exclusions>
<exclusion>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,39 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.gateway;
import org.security.common.constant.CommonConstant;
import org.springblade.core.launch.BladeApplication;
import org.springframework.cloud.client.SpringCloudApplication;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* 项目启动
*
* @author Chill
*/
@EnableHystrix
@EnableScheduling
@SpringCloudApplication
public class GateWayApplication {
public static void main(String[] args) {
BladeApplication.run(CommonConstant.APPLICATION_GATEWAY_NAME, GateWayApplication.class, args);
}
}

View File

@ -0,0 +1,86 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.gateway.config;
import org.security.gateway.handler.ErrorExceptionHandler;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.result.view.ViewResolver;
import java.util.Collections;
import java.util.List;
/**
* 异常处理配置类
*
* @author Chill
*/
@Configuration
@AutoConfigureBefore(ErrorWebFluxAutoConfiguration.class)
@EnableConfigurationProperties({ServerProperties.class, ResourceProperties.class})
public class ErrorHandlerConfiguration {
private final ServerProperties serverProperties;
private final ApplicationContext applicationContext;
private final ResourceProperties resourceProperties;
private final List<ViewResolver> viewResolvers;
private final ServerCodecConfigurer serverCodecConfigurer;
public ErrorHandlerConfiguration(ServerProperties serverProperties,
ResourceProperties resourceProperties,
ObjectProvider<List<ViewResolver>> viewResolversProvider,
ServerCodecConfigurer serverCodecConfigurer,
ApplicationContext applicationContext) {
this.serverProperties = serverProperties;
this.applicationContext = applicationContext;
this.resourceProperties = resourceProperties;
this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
this.serverCodecConfigurer = serverCodecConfigurer;
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public ErrorWebExceptionHandler errorWebExceptionHandler(ErrorAttributes errorAttributes) {
ErrorExceptionHandler exceptionHandler = new ErrorExceptionHandler(
errorAttributes,
this.resourceProperties,
this.serverProperties.getError(),
this.applicationContext);
exceptionHandler.setViewResolvers(this.viewResolvers);
exceptionHandler.setMessageWriters(this.serverCodecConfigurer.getWriters());
exceptionHandler.setMessageReaders(this.serverCodecConfigurer.getReaders());
return exceptionHandler;
}
}

View File

@ -0,0 +1,81 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.gateway.config;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.security.gateway.props.AuthProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.cors.reactive.CorsUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
/**
* 路由配置信息
*
* @author Chill
*/
@Slf4j
@Configuration
@AllArgsConstructor
@EnableConfigurationProperties({AuthProperties.class})
public class RouterFunctionConfiguration {
/**
* 这里为支持的请求头如果有自定义的header字段请自己添加
*/
private static final String ALLOWED_HEADERS = "X-Requested-With, Tenant-Id, Blade-Auth, Content-Type, Authorization, credential, X-XSRF-TOKEN, token, username, client, knfie4j-gateway-request, request-origion";
private static final String ALLOWED_METHODS = "GET,POST,PUT,DELETE,OPTIONS,HEAD";
private static final String ALLOWED_ORIGIN = "*";
private static final String ALLOWED_EXPOSE = "*";
private static final String MAX_AGE = "18000L";
/**
* 跨域配置
*/
@Bean
public WebFilter corsFilter() {
return (ServerWebExchange ctx, WebFilterChain chain) -> {
ServerHttpRequest request = ctx.getRequest();
if (CorsUtils.isCorsRequest(request)) {
ServerHttpResponse response = ctx.getResponse();
HttpHeaders headers = response.getHeaders();
headers.add("Access-Control-Allow-Headers", ALLOWED_HEADERS);
headers.add("Access-Control-Allow-Methods", ALLOWED_METHODS);
headers.add("Access-Control-Allow-Origin", ALLOWED_ORIGIN);
headers.add("Access-Control-Expose-Headers", ALLOWED_EXPOSE);
headers.add("Access-Control-Max-Age", MAX_AGE);
headers.add("Access-Control-Allow-Credentials", "true");
if (request.getMethod() == HttpMethod.OPTIONS) {
response.setStatusCode(HttpStatus.OK);
return Mono.empty();
}
}
return chain.filter(ctx);
};
}
}

View File

@ -0,0 +1,101 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.gateway.dynamic;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
import java.util.List;
/**
* 动态路由业务类
*
* @author Chill
*/
@Service
public class DynamicRouteService implements ApplicationEventPublisherAware {
private final RouteDefinitionWriter routeDefinitionWriter;
private ApplicationEventPublisher publisher;
public DynamicRouteService(RouteDefinitionWriter routeDefinitionWriter) {
this.routeDefinitionWriter = routeDefinitionWriter;
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher = applicationEventPublisher;
}
/**
* 增加路由
*/
public String save(RouteDefinition definition) {
try {
routeDefinitionWriter.save(Mono.just(definition)).subscribe();
this.publisher.publishEvent(new RefreshRoutesEvent(this));
return "save success";
} catch (Exception e) {
e.printStackTrace();
return "save failure";
}
}
/**
* 更新路由
*/
public String update(RouteDefinition definition) {
try {
this.routeDefinitionWriter.delete(Mono.just(definition.getId()));
this.routeDefinitionWriter.save(Mono.just(definition)).subscribe();
this.publisher.publishEvent(new RefreshRoutesEvent(this));
return "update success";
} catch (Exception e) {
e.printStackTrace();
return "update failure";
}
}
/**
* 更新路由
*/
public String updateList(List<RouteDefinition> routeDefinitions) {
routeDefinitions.forEach(this::update);
return "update done";
}
/**
* 删除路由
*/
public String delete(String id) {
try {
this.routeDefinitionWriter.delete(Mono.just(id));
return "delete success";
} catch (Exception e) {
e.printStackTrace();
return "delete failure";
}
}
}

View File

@ -0,0 +1,94 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.gateway.dynamic;
import com.alibaba.cloud.nacos.NacosConfigProperties;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.launch.constant.NacosConstant;
import org.springblade.core.launch.props.BladeProperties;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Executor;
/**
* 动态路由监听器
*
* @author Chill
*/
@Order
@Slf4j
@Component
public class DynamicRouteServiceListener {
private final DynamicRouteService dynamicRouteService;
private final NacosDiscoveryProperties nacosDiscoveryProperties;
private final NacosConfigProperties nacosConfigProperties;
private final BladeProperties bladeProperties;
public DynamicRouteServiceListener(DynamicRouteService dynamicRouteService, NacosDiscoveryProperties nacosDiscoveryProperties, NacosConfigProperties nacosConfigProperties, BladeProperties bladeProperties) {
this.dynamicRouteService = dynamicRouteService;
this.nacosDiscoveryProperties = nacosDiscoveryProperties;
this.nacosConfigProperties = nacosConfigProperties;
this.bladeProperties = bladeProperties;
dynamicRouteServiceListener();
}
/**
* 监听Nacos下发的动态路由配置
*/
private void dynamicRouteServiceListener() {
try {
String dataId = NacosConstant.dataId(bladeProperties.getName(), bladeProperties.getEnv(), NacosConstant.NACOS_CONFIG_JSON_FORMAT);
String group = nacosConfigProperties.getGroup();
Properties properties = new Properties();
properties.setProperty(PropertyKeyConst.SERVER_ADDR, nacosDiscoveryProperties.getServerAddr());
properties.setProperty(PropertyKeyConst.NAMESPACE, nacosDiscoveryProperties.getNamespace());
ConfigService configService = NacosFactory.createConfigService(properties);
configService.addListener(dataId, group, new Listener() {
@Override
public void receiveConfigInfo(String configInfo) {
List<RouteDefinition> routeDefinitions = JSON.parseArray(configInfo, RouteDefinition.class);
dynamicRouteService.updateList(routeDefinitions);
}
@Override
public Executor getExecutor() {
return null;
}
});
String configInfo = configService.getConfig(dataId, group, 5000);
if (configInfo != null) {
List<RouteDefinition> routeDefinitions = JSON.parseArray(configInfo, RouteDefinition.class);
dynamicRouteService.updateList(routeDefinitions);
}
} catch (NacosException ignored) {
}
}
}

View File

@ -0,0 +1,41 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.gateway.dynamic;
import lombok.Data;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* 过滤器定义模型
*
* @author Chill
*/
@Data
public class GatewayFilter {
/**
* 过滤器对应的Name
*/
private String name;
/**
* 对应的路由规则
*/
private Map<String, String> args = new LinkedHashMap<>();
}

View File

@ -0,0 +1,41 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.gateway.dynamic;
import lombok.Data;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* 路由断言定义模型
*
* @author Chill
*/
@Data
public class GatewayPredicate {
/**
* 断言对应的Name
*/
private String name;
/**
* 配置的断言规则
*/
private Map<String, String> args = new LinkedHashMap<>();
}

View File

@ -0,0 +1,57 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.gateway.dynamic;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
* Gateway的路由定义模型
*
* @author Chill
*/
@Data
public class GatewayRoute {
/**
* 路由的id
*/
private String id;
/**
* 路由断言集合配置
*/
private List<GatewayPredicate> predicates = new ArrayList<>();
/**
* 路由过滤器集合配置
*/
private List<GatewayFilter> filters = new ArrayList<>();
/**
* 路由规则转发的目标uri
*/
private String uri;
/**
* 路由执行的顺序
*/
private int order = 0;
}

View File

@ -0,0 +1,117 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.gateway.filter;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.Claims;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.security.gateway.props.AuthProperties;
import org.springblade.core.jwt.JwtUtil;
import org.springblade.core.jwt.props.JwtProperties;
import org.springblade.core.launch.constant.TokenConstant;
import org.security.gateway.provider.AuthProvider;
import org.security.gateway.provider.RequestProvider;
import org.security.gateway.provider.ResponseProvider;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
/**
* 鉴权认证
*
* @author Chill
*/
@Slf4j
@Component
@AllArgsConstructor
public class AuthFilter implements GlobalFilter, Ordered {
private final AuthProperties authProperties;
private final ObjectMapper objectMapper;
private final JwtProperties jwtProperties;
private final AntPathMatcher antPathMatcher = new AntPathMatcher();
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//校验 Token 放行
String originalRequestUrl = RequestProvider.getOriginalRequestUrl(exchange);
String path = exchange.getRequest().getURI().getPath();
if (isSkip(path) || isSkip(originalRequestUrl)) {
return chain.filter(exchange);
}
//校验 Token 合法性
ServerHttpResponse resp = exchange.getResponse();
String headerToken = exchange.getRequest().getHeaders().getFirst(AuthProvider.AUTH_KEY);
String paramToken = exchange.getRequest().getQueryParams().getFirst(AuthProvider.AUTH_KEY);
if (StringUtils.isBlank(headerToken) && StringUtils.isBlank(paramToken)) {
return unAuth(resp, "缺失令牌,鉴权失败");
}
String auth = StringUtils.isBlank(headerToken) ? paramToken : headerToken;
String token = JwtUtil.getToken(auth);
Claims claims = JwtUtil.parseJWT(token);
if (token == null || claims == null) {
return unAuth(resp, "请求未授权");
}
//判断 Token 状态
if (jwtProperties.getState()) {
String tenantId = String.valueOf(claims.get(TokenConstant.TENANT_ID));
String userId = String.valueOf(claims.get(TokenConstant.USER_ID));
String accessToken = JwtUtil.getAccessToken(tenantId, userId, token);
if (!token.equalsIgnoreCase(accessToken)) {
return unAuth(resp, "令牌已失效");
}
}
return chain.filter(exchange);
}
private boolean isSkip(String path) {
return AuthProvider.getDefaultSkipUrl().stream().anyMatch(pattern -> antPathMatcher.match(pattern, path))
|| authProperties.getSkipUrl().stream().anyMatch(pattern -> antPathMatcher.match(pattern, path));
}
private Mono<Void> unAuth(ServerHttpResponse resp, String msg) {
resp.setStatusCode(HttpStatus.UNAUTHORIZED);
resp.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
String result = "";
try {
result = objectMapper.writeValueAsString(ResponseProvider.unAuth(msg));
} catch (JsonProcessingException e) {
log.error(e.getMessage(), e);
}
DataBuffer buffer = resp.bufferFactory().wrap(result.getBytes(StandardCharsets.UTF_8));
return resp.writeWith(Flux.just(buffer));
}
@Override
public int getOrder() {
return -100;
}
}

View File

@ -0,0 +1,114 @@
/*
* Copyright (c) 2018-2028, DreamLu All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: DreamLu 卢春梦 (596392912@qq.com)
*/
package org.security.gateway.filter;
import io.jsonwebtoken.Claims;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springblade.core.jwt.JwtUtil;
import org.security.gateway.provider.AuthProvider;
import org.security.gateway.provider.RequestProvider;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
/**
* webflux 日志请求记录方便开发调试请求日志过滤器排序尽量低
*
* <p>
* 注意暂时不支持结构体打印想实现请看下面的链接
* https://stackoverflow.com/questions/45240005/how-to-log-request-and-response-bodies-in-spring-webflux
* https://github.com/Silvmike/webflux-demo/blob/master/tests/src/test/java/ru/hardcoders/demo/webflux/web_handler/filters/logging
* </p>
*
* @author dream.lu
*/
@Slf4j
@Configuration
@RequiredArgsConstructor
@ConditionalOnProperty(value = "blade.log.request.enabled", havingValue = "true", matchIfMissing = true)
public class GlobalRequestLogFilter implements GlobalFilter, Ordered {
private final WebEndpointProperties endpointProperties;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
// 打印请求路径
String path = request.getPath().pathWithinApplication().value();
// 忽略 endpoint 请求
String endpointBasePath = endpointProperties.getBasePath();
if (StringUtils.isNotBlank(endpointBasePath) && path.startsWith(endpointBasePath)) {
return chain.filter(exchange);
}
String requestUrl = RequestProvider.getOriginalRequestUrl(exchange);
// 构建成一条长 日志避免并发下日志错乱
StringBuilder beforeReqLog = new StringBuilder(300);
// 日志参数
List<Object> beforeReqArgs = new ArrayList<>();
beforeReqLog.append("\n\n================ Gateway Request Start ================\n");
// 打印路由
beforeReqLog.append("===> {}: {}\n");
// 参数
String requestMethod = request.getMethodValue();
beforeReqArgs.add(requestMethod);
beforeReqArgs.add(requestUrl);
// 打印请求头
HttpHeaders headers = request.getHeaders();
Locale.setDefault(Locale.ENGLISH);
headers.forEach((headerName, headerValue) -> {
beforeReqLog.append("===Headers=== {}: {}\n");
beforeReqArgs.add(headerName);
if (AuthProvider.AUTH_KEY.toLowerCase().equals(headerName)) {
String value = headerValue.get(0);
String token = JwtUtil.getToken(value);
Claims claims = JwtUtil.parseJWT(token);
beforeReqArgs.add((claims == null) ? "" : claims.toString());
beforeReqLog.append("===Headers=== {}: {}\n");
beforeReqArgs.add(headerName.concat("-original"));
beforeReqArgs.add(StringUtils.join(headerValue.toArray()));
} else {
beforeReqArgs.add(StringUtils.join(headerValue.toArray()));
}
});
beforeReqLog.append("================ Gateway Request End =================\n");
// 打印执行时间
log.info(beforeReqLog.toString(), beforeReqArgs.toArray());
return chain.filter(exchange);
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}

View File

@ -0,0 +1,99 @@
/*
* Copyright (c) 2018-2028, DreamLu All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: DreamLu 卢春梦 (596392912@qq.com)
*/
package org.security.gateway.filter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;
import java.util.ArrayList;
import java.util.List;
/**
* webflux 相应日志方便开发调试注意排序要优先
*
* @author dream.lu
*/
@Slf4j
@Configuration
@RequiredArgsConstructor
@ConditionalOnProperty(value = "blade.log.request.enabled", havingValue = "true", matchIfMissing = true)
public class GlobalResponseLogFilter implements GlobalFilter, Ordered {
private final WebEndpointProperties endpointProperties;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
// 打印请求路径
String path = request.getPath().pathWithinApplication().value();
// 忽略 endpoint 请求
String endpointBasePath = endpointProperties.getBasePath();
if (StringUtils.isNotBlank(endpointBasePath) && path.startsWith(endpointBasePath)) {
return chain.filter(exchange);
}
return chain.filter(exchange).then(
Mono.fromRunnable(() -> {
MultiValueMap<String, String> queryParams = request.getQueryParams();
String requestUrl = UriComponentsBuilder.fromPath(path).queryParams(queryParams).build().toUriString();
// 构建成一条长 日志避免并发下日志错乱
StringBuilder responseLog = new StringBuilder(300);
// 日志参数
List<Object> responseArgs = new ArrayList<>();
responseLog.append("\n\n================ Gateway Response Start ================\n");
ServerHttpResponse response = exchange.getResponse();
// 打印路由 200 get: /api/xxx/xxx
responseLog.append("<=== {} {}: {}\n");
// 参数
String requestMethod = request.getMethodValue();
responseArgs.add(response.getStatusCode().value());
responseArgs.add(requestMethod);
responseArgs.add(requestUrl);
// 打印请求头
HttpHeaders headers = response.getHeaders();
headers.forEach((headerName, headerValue) -> {
responseLog.append("===Headers=== {}: {}\n");
responseArgs.add(headerName);
responseArgs.add(StringUtils.join(headerValue.toArray()));
});
responseLog.append("================ Gateway Response End =================\n");
// 打印执行时间
log.info(responseLog.toString(), responseArgs.toArray());
})
);
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}

View File

@ -0,0 +1,63 @@
package org.security.gateway.filter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Arrays;
import java.util.stream.Collectors;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.addOriginalRequestUrl;
/**
* <p>
* 全局拦截器作用所有的微服务
* <p>
* 1. 对请求头中参数进行处理 from 参数进行清洗
* 2. 重写StripPrefix = 1,支持全局
*
* @author lengleng
*/
@Component
public class RequestFilter implements GlobalFilter, Ordered {
/**
* Process the Web request and (optionally) delegate to the next
* {@code WebFilter} through the given {@link GatewayFilterChain}.
*
* @param exchange the current server exchange
* @param chain provides a way to delegate to the next filter
* @return {@code Mono<Void>} to indicate when request processing is complete
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 1. 清洗请求头中from 参数
ServerHttpRequest request = exchange.getRequest().mutate()
.headers(httpHeaders -> httpHeaders.remove("X"))
.build();
// 2. 重写StripPrefix
addOriginalRequestUrl(exchange, request.getURI());
String rawPath = request.getURI().getRawPath();
String newPath = "/" + Arrays.stream(StringUtils.tokenizeToStringArray(rawPath, "/"))
.skip(1L).collect(Collectors.joining("/"));
ServerHttpRequest newRequest = request.mutate()
.path(newPath)
.build();
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, newRequest.getURI());
return chain.filter(exchange.mutate().request(newRequest.mutate().build()).build());
}
@Override
public int getOrder() {
return -1000;
}
}

View File

@ -0,0 +1,103 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.gateway.handler;
import org.security.gateway.provider.ResponseProvider;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.context.ApplicationContext;
import org.springframework.web.reactive.function.server.*;
import org.springframework.web.server.ResponseStatusException;
import java.util.Map;
/**
* 异常处理
*
* @author Chill
*/
public class ErrorExceptionHandler extends DefaultErrorWebExceptionHandler {
public ErrorExceptionHandler(ErrorAttributes errorAttributes, ResourceProperties resourceProperties,
ErrorProperties errorProperties, ApplicationContext applicationContext) {
super(errorAttributes, resourceProperties, errorProperties, applicationContext);
}
/**
* 获取异常属性
*/
@Override
protected Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
int code = 500;
Throwable error = super.getError(request);
if (error instanceof NotFoundException) {
code = 404;
}
if (error instanceof ResponseStatusException) {
code = ((ResponseStatusException) error).getStatus().value();
}
return ResponseProvider.response(code, this.buildMessage(request, error));
}
/**
* 指定响应处理方法为JSON处理的方法
*
* @param errorAttributes
*/
@Override
protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
}
/**
* 根据code获取对应的HttpStatus
*
* @param errorAttributes
*/
@Override
protected int getHttpStatus(Map<String, Object> errorAttributes) {
return (int) errorAttributes.get("code");
}
/**
* 构建异常信息
*
* @param request
* @param ex
* @return
*/
private String buildMessage(ServerRequest request, Throwable ex) {
String uri = request.uri().toString();
if (uri.endsWith("doc.html")) {
return "[Swagger聚合网关] 已迁移至 [blade-swagger] 服务,请开启 [blade-swagger] 服务并访问 [http://127.0.0.1:18000/doc.html]";
}
StringBuilder message = new StringBuilder("Failed to handle request [");
message.append(request.methodName());
message.append(" ");
message.append(request.uri());
message.append("]");
if (ex != null) {
message.append(": ");
message.append(ex.getMessage());
}
return message.toString();
}
}

View File

@ -0,0 +1,41 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.gateway.props;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import java.util.ArrayList;
import java.util.List;
/**
* 权限过滤
*
* @author Chill
*/
@Data
@RefreshScope
@ConfigurationProperties("blade.secure")
public class AuthProperties {
/**
* 放行API集合
*/
private final List<String> skipUrl = new ArrayList<>();
}

View File

@ -0,0 +1,70 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.gateway.provider;
import org.springblade.core.launch.constant.TokenConstant;
import java.util.ArrayList;
import java.util.List;
/**
* 鉴权配置
*
* @author Chill
*/
public class AuthProvider {
public static final String AUTH_KEY = TokenConstant.HEADER;
private static final List<String> DEFAULT_SKIP_URL = new ArrayList<>();
static {
DEFAULT_SKIP_URL.add("/example");
DEFAULT_SKIP_URL.add("/oauth/token/**");
DEFAULT_SKIP_URL.add("/oauth/captcha/**");
DEFAULT_SKIP_URL.add("/oauth/clear-cache/**");
DEFAULT_SKIP_URL.add("/oauth/user-info");
DEFAULT_SKIP_URL.add("/oauth/render/**");
DEFAULT_SKIP_URL.add("/oauth/callback/**");
DEFAULT_SKIP_URL.add("/oauth/revoke/**");
DEFAULT_SKIP_URL.add("/oauth/refresh/**");
DEFAULT_SKIP_URL.add("/token/**");
DEFAULT_SKIP_URL.add("/actuator/**");
DEFAULT_SKIP_URL.add("/v2/api-docs/**");
DEFAULT_SKIP_URL.add("/auth/**");
DEFAULT_SKIP_URL.add("/authorize/**");
DEFAULT_SKIP_URL.add("/log/**");
DEFAULT_SKIP_URL.add("/menu/routes");
DEFAULT_SKIP_URL.add("/menu/auth-routes");
DEFAULT_SKIP_URL.add("/menu/top-menu");
DEFAULT_SKIP_URL.add("/tenant/info");
DEFAULT_SKIP_URL.add("/process/resource-view");
DEFAULT_SKIP_URL.add("/process/diagram-view");
DEFAULT_SKIP_URL.add("/manager/check-upload");
DEFAULT_SKIP_URL.add("/error/**");
DEFAULT_SKIP_URL.add("/assets/**");
//TODO:app接口暂时放行后续接单点登录
DEFAULT_SKIP_URL.add("/app/**");
}
/**
* 默认无需鉴权的API
*/
public static List<String> getDefaultSkipUrl() {
return DEFAULT_SKIP_URL;
}
}

View File

@ -0,0 +1,49 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.gateway.provider;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponentsBuilder;
import java.net.URI;
import java.util.LinkedHashSet;
/**
* RequestProvider
*
* @author Chill
*/
public class RequestProvider {
/**
* 获取原始url
*
* @param exchange
* @return
*/
public static String getOriginalRequestUrl(ServerWebExchange exchange) {
ServerHttpRequest request = exchange.getRequest();
LinkedHashSet<URI> uris = exchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR);
URI requestUri = uris.stream().findFirst().orElse(request.getURI());
MultiValueMap<String, String> queryParams = request.getQueryParams();
return UriComponentsBuilder.fromPath(requestUri.getRawPath()).queryParams(queryParams).build().toUriString();
}
}

View File

@ -0,0 +1,84 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.gateway.provider;
import java.util.HashMap;
import java.util.Map;
/**
* 请求响应返回
*
* @author Chill
*/
public class ResponseProvider {
/**
* 成功
*
* @param message 信息
* @return
*/
public static Map<String, Object> success(String message) {
return response(200, message);
}
/**
* 失败
*
* @param message 信息
* @return
*/
public static Map<String, Object> fail(String message) {
return response(400, message);
}
/**
* 未授权
*
* @param message 信息
* @return
*/
public static Map<String, Object> unAuth(String message) {
return response(401, message);
}
/**
* 服务器异常
*
* @param message 信息
* @return
*/
public static Map<String, Object> error(String message) {
return response(500, message);
}
/**
* 构建返回的JSON数据格式
*
* @param status 状态码
* @param message 信息
* @return
*/
public static Map<String, Object> response(int status, String message) {
Map<String, Object> map = new HashMap<>(16);
map.put("code", status);
map.put("msg", message);
map.put("data", null);
return map;
}
}

View File

@ -0,0 +1,10 @@
blade:
#多团队协作服务配置
ribbon:
rule:
#开启配置
enabled: true
#负载均衡优先调用的ip段
prior-ip-pattern:
- 192.168.0.*
- 127.0.0.1

View File

@ -0,0 +1,10 @@
blade:
#多团队协作服务配置
ribbon:
rule:
#开启配置
enabled: true
#负载均衡优先调用的ip段
prior-ip-pattern:
- 192.168.0.*
- 127.0.0.1

View File

@ -0,0 +1,10 @@
blade:
#多团队协作服务配置
ribbon:
rule:
#开启配置
enabled: true
#负载均衡优先调用的ip段
prior-ip-pattern:
- 192.168.0.*
- 127.0.0.1

View File

@ -0,0 +1,15 @@
server:
port: 8000
spring:
cloud:
discovery:
reactive:
enabled: false
gateway:
discovery:
locator:
enabled: true
loadbalancer:
retry:
enabled: true

View File

@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<property name="logdir" value="../logs/security-gateway"/>
<!-- 控制台输出日志 -->
<!-- 彩色日志依赖的渲染类 -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
<conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
<!-- 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}" />
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>utf8</charset>
</encoder>
</appender>
<!-- 按用户输出日志 -->
<appender name="SIFT" class="ch.qos.logback.classic.sift.SiftingAppender">
<discriminator>
<Key>txcode</Key>
<DefaultValue>security-gateway</DefaultValue>
</discriminator>
<sift>
<appender name="FILE-${userid}" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>${logdir}/${txcode}.log</File>
<Append>false</Append>
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} %msg%n</Pattern>
</layout>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<FileNamePattern>${logdir}/${txcode}.%d{yyyy-MM-dd}.%i.log</FileNamePattern>
<MaxHistory>20</MaxHistory>
<maxFileSize>5MB</maxFileSize>
<totalSizeCap>1024MB</totalSizeCap>
</rollingPolicy>
</appender>
</sift>
</appender>
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
<includeCallerData>true</includeCallerData>
<discardingThreshold>-1</discardingThreshold>
<queueSize>1024</queueSize>
<appender-ref ref="SIFT"/>
</appender>
<!-- <logger name="dao" level="DEBUG"/>-->
<!-- <logger name="net.sf.ehcache" level="INFO"/>-->
<!-- <logger name="druid.sql" level="INFO"/>-->
<!-- MyBatis log configure -->
<logger name="com.apache.ibatis" level="INFO"/>
<logger name="org.mybatis.spring" level="DEBUG"/>
<logger name="java.sql.Connection" level="DEBUG"/>
<logger name="java.sql.Statement" level="DEBUG"/>
<logger name="java.sql.PreparedStatement" level="DEBUG"/>
<!-- 减少部分debug日志 -->
<logger name="druid.sql" level="INFO"/>
<logger name="org.apache.shiro" level="INFO"/>
<logger name="org.mybatis.spring" level="INFO"/>
<logger name="org.springframework" level="INFO"/>
<logger name="org.springframework.context" level="WARN"/>
<logger name="org.springframework.beans" level="WARN"/>
<logger name="com.baomidou.mybatisplus" level="INFO"/>
<logger name="org.apache.ibatis.io" level="INFO"/>
<logger name="org.apache.velocity" level="INFO"/>
<logger name="org.eclipse.jetty" level="INFO"/>
<logger name="io.undertow" level="INFO"/>
<logger name="org.xnio.nio" level="INFO"/>
<logger name="org.thymeleaf" level="INFO"/>
<logger name="springfox.documentation" level="INFO"/>
<logger name="org.hibernate.validator" level="INFO"/>
<logger name="com.netflix.loadbalancer" level="INFO"/>
<logger name="com.netflix.hystrix" level="INFO"/>
<logger name="com.netflix.zuul" level="INFO"/>
<logger name="de.codecentric" level="INFO"/>
<!-- cache INFO -->
<logger name="net.sf.ehcache" level="INFO"/>
<logger name="org.springframework.cache" level="INFO"/>
<!-- cloud -->
<logger name="org.apache.http" level="INFO"/>
<logger name="com.netflix.discovery" level="INFO"/>
<logger name="com.netflix.eureka" level="INFO"/>
<!-- 业务日志 -->
<Logger name="org.security" level="DEBUG" />
<Logger name="org.springblade.core.version" level="INFO"/>
<!-- 减少nacos日志 -->
<logger name="com.alibaba.nacos" level="ERROR"/>
<root level="INFO">
<appender-ref ref="STDOUT" />
<appender-ref ref="ASYNC"/>
</root>
</configuration>

54
security-ops-api/pom.xml Normal file
View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>security-manage</artifactId>
<groupId>org.security</groupId>
<version>2.8.1.RELEASE</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>security-ops-api</artifactId>
<name>${project.artifactId}</name>
<version>2.8.1.RELEASE</version>
<packaging>pom</packaging>
<modules>
<module>security-flow-api</module>
</modules>
<dependencies>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-starter-mybatis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-auto</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.security</groupId>
<artifactId>security-common</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<skip>true</skip>
<finalName>${project.name}</finalName>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>security-ops-api</artifactId>
<groupId>org.security</groupId>
<version>2.8.1.RELEASE</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>security-flow-api</artifactId>
<name>${project.artifactId}</name>
<version>${bladex.project.version}</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-engine</artifactId>
<version>${flowable.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,172 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.flow.core.constant;
/**
* 流程常量.
*
* @author Chill
*/
public interface ProcessConstant {
/**
* 请假流程标识
*/
String LEAVE_KEY = "Leave";
/**
* 报销流程标识
*/
String EXPENSE_KEY = "Expense";
/**
* 同意标识
*/
String PASS_KEY = "pass";
/**
* 同意代号
*/
String PASS_ALIAS = "ok";
/**
* 同意默认批复
*/
String PASS_COMMENT = "同意";
/**
* 驳回默认批复
*/
String NOT_PASS_COMMENT = "驳回";
/**
* 创建人变量名
*/
String TASK_VARIABLE_CREATE_USER = "createUser";
/**
* 开启流程人id
*/
String START_PROC_USER_ID = "startProcUserId";
/**
* 案件材料报送审批流程标识
*/
String PREVENT_MATERIAL_KEY = "prevent_material";
/**
* 风险提示审批流程标识
*/
String PREVENT_RISK_KEY = "prevent_risk";
/**
* 值班备勤审批流程标识
*/
String DUTY_RECORDS_KEY = "duty_records";
/**
* 机构考核审批流程标识
*/
String BANK_ASSESSMENT_KEY = "bank_assessment";
/**
* 相关方考评审批流程标识
*/
String OUTSOURCING_ASSESSMENT_KEY = "outsourcing_assessment";
/**
* 项目考评审批流程标识
*/
String PROJECT_ASSESSMENT_KEY = "project_assessment";
/**
* 教育培训审批流程标识
*/
String TASK_HANDIN_EDU_KEY = "task_handin_edu";
/**
* 应急演练审批流程标识
*/
String TASK_HANDIN_DRILL_KEY = "task_handin_drill";
/**
* 现场检查审批流程标识
*/
String INSPECTION_KEY = "inspection";
/**
* 现场检查整改审批流程标识
*/
String INSPECTION_LINE_KEY = "inspection_line";
/**
* 非现场检查审批流程标识
*/
String SCC_CHECK_KEY = "scc_check";
/**
* 非现场检查整改审批流程标识
*/
String SCC_UNLOCAL_CHECK_KEY = "scc_unlocal_check";
/**
* 非现场检查持续整改审批流程标识
*/
String SCC_UNLOCAL_CHECK_CONTINUE_KEY = "scc_unlocal_check_con";
/**
* 设备检查流程标识
*/
String DEVICE_INSPECTION_KEY = "device_inspection";
/**
* 设备检查整改流程标识
*/
String DEVICE_INSPECTION_REC_KEY = "device_inspection_rec";
/**
* 设备检查持续整改流程标识
*/
String DEVICE_INS_REC_CON_KEY = "device_ins_rec_con";
/**
* 下发整改通知书审批流程标识
*/
// String SCC_UNLOCAL_CHECK_KEY = "scc_unlocal_check";
/**
* 联系接警记录审批流程标识
*/
String SCC_ANSWER_ALARM_KEY = "scc_answer_alarm";
/**
* 警情处理审批流程标识
*/
String SCC_ALARM_PUSH_KEY = "scc_alarm_push";
/**
* 业务id标识
*/
String BUSINESS_ID_KEY = "businessId";
String PASS_STATE = "2";
String REJECT_STATE = "3";
String CANCEL_STATE = "4";
}

View File

@ -0,0 +1,186 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.flow.core.entity;
import lombok.Data;
import org.security.flow.core.constant.ProcessConstant;
import java.io.Serializable;
import java.util.Date;
import java.util.Map;
/**
* 工作流通用实体类
*
* @author Chill
*/
@Data
public class BladeFlow implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 任务编号
*/
private String taskId;
/**
* 任务名称
*/
private String taskName;
/**
* 任务定义Key
*/
private String taskDefinitionKey;
/**
* 任务执行人编号
*/
private String assignee;
/**
* 任务执行人名称
*/
private String assigneeName;
/**
* 流程分类
*/
private String category;
/**
* 流程分类名
*/
private String categoryName;
/**
* 创建时间
*/
private Date createTime;
/**
* 结束时间
*/
private Date endTime;
/**
* 签收时间
*/
private Date claimTime;
/**
* 历史任务结束时间
*/
private Date historyTaskEndTime;
/**
* 执行ID
*/
private String executionId;
/**
* 流程实例ID
*/
private String processInstanceId;
/**
* 流程ID
*/
private String processDefinitionId;
/**
* 流程标识
*/
private String processDefinitionKey;
/**
* 流程名
*/
private String processDefinitionName;
/**
* 流程版本
*/
private int processDefinitionVersion;
/**
* 流程说明
*/
private String processDefinitionDesc;
/**
* 流程简图名
*/
private String processDefinitionDiagramResName;
/**
* 流程重命名
*/
private String processDefinitionResName;
/**
* 历史任务流程实例ID 查看流程图会用到
*/
private String historyProcessInstanceId;
/**
* 流程实例是否结束
*/
private String processIsFinished;
/**
* 历史活动ID
*/
private String historyActivityId;
/**
* 历史活动流程
*/
private String historyActivityName;
/**
* 历史活动耗时
*/
private String historyActivityDurationTime;
/**
* 业务绑定Table
*/
private String businessTable;
/**
* 业务绑定ID
*/
private String businessId;
/**
* 任务状态
*/
private String status;
/**
* 任务意见
*/
private String comment;
/**
* 是否通过
*/
private boolean isPass;
/**
* 是否通过代号
*/
private String flag;
/**
* 开始查询日期
*/
private Date beginDate;
/**
* 结束查询日期
*/
private Date endDate;
/**
* 流程参数
*/
private Map<String, Object> variables;
/**
* 获取是否通过
*/
public boolean isPass() {
return ProcessConstant.PASS_ALIAS.equals(flag) || ProcessConstant.PASS_COMMENT.equals(comment);
}
/**
* 整个流程是否通过
*/
private boolean flowPass;
private boolean cancelFlow;
}

View File

@ -0,0 +1,52 @@
package org.security.flow.core.entity;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* @Author: qxqx
* @Create: 2022/9/7
* @Description:
*/
@Data
public class BladeFlowDef implements Serializable {
private static final long serialVersionUID = 1L;
private String id;
private String category;
private String name;
private String key;
private String description;
private int version;
private String resourceName;
private String deploymentId;
private String diagramResourceName;
private boolean hasStartFormKey;
private boolean hasGraphicalNotation;
private boolean isSuspended;
private String tenantId;
private String derivedForm;
private String derivedFromRoot;
private int derivedVersion;
private String engineVersion;
private List<BladeFlow> historyFlowList;
}

View File

@ -0,0 +1,43 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.flow.core.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springblade.core.mp.base.BaseEntity;
/**
* FlowEntity
*
* @author Chill
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class FlowEntity extends BaseEntity {
@TableField(exist = false)
private BladeFlow flow;
public BladeFlow getFlow() {
if (flow == null) {
flow = new BladeFlow();
}
return flow;
}
}

View File

@ -0,0 +1,106 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.flow.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.EnumSet;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 审批候选人类型
*
* @author Arlex
*/
@Getter
@AllArgsConstructor
public enum CandidateTypeEnum {
/**
* 总行领导
*/
HEAD_LEADER("headLeader", "总行领导"),
/**
* 总行安全部部门领导
*/
HEAD_SEC_LEADER("headSecLeader", "总行安全部部门领导"),
/**
* 总行安全员
*/
HEAD_SAFER("headSafer", "总行安全员"),
/**
* 分行领导
*/
BRANCH_LEADER("branchLeader", "分行领导"),
/**
* 分行安全员
*/
BRANCH_SAFER("branchSafer", "分行安全员"),
/**
* 支行领导
*/
SUB_LEADER("subLeader", "支行领导"),
/**
* 支行安全员
*/
SUB_SAFER("subSafer", "支行安全员"),
/**
* 部门领导
*/
DEPT_LEADER("deptLeader", "部门领导"),
/**
* 总行安保指挥中心全员
*/
HEAD_SEC_CENTER_ALL("securityUser", "总行安保指挥中心全员"),
/**
* 分行安保指挥中心全员
*/
BRANCH_SEC_CENTER_ALL("branchSecurityUser", "分行安保指挥中心全员"),
;
final String value;
final String name;
public static CandidateTypeEnum fromValue(String value) {
if (null == value) {
throw new IllegalArgumentException("Invalid value: " + value);
}
return EnumSet.allOf(CandidateTypeEnum.class).stream()
.filter(s -> s.getValue().equals(value))
.findAny()
.orElseThrow(() -> new IllegalArgumentException("Invalid value: " + value));
}
public static List<String> getValues() {
List<String> list = Stream.of(CandidateTypeEnum.values()).map(CandidateTypeEnum::getValue).collect(Collectors.toList());
return list;
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.flow.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 流程类型枚举
*
* @author Chill
*/
@Getter
@AllArgsConstructor
public enum FlowModeEnum {
/**
* 通用流程
*/
COMMON("common", 1),
/**
* 定制流程
*/
CUSTOM("custom", 2),
;
final String name;
final int mode;
}

View File

@ -0,0 +1,124 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.flow.core.feign;
import org.security.common.constant.CommonConstant;
import org.security.flow.core.entity.BladeFlow;
import org.security.flow.core.entity.BladeFlowDef;
import org.springblade.core.tool.api.R;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.flowable.engine.repository.ProcessDefinition;
import java.util.Map;
/**
* 工作流远程调用接口.
*
* @author Chill
*/
@FeignClient(
value = CommonConstant.APPLICATION_FLOW_NAME,
fallback = IFlowClientFallback.class
)
public interface IFlowClient {
String API_PREFIX = "/client";
String START_PROCESS_INSTANCE_BY_ID = API_PREFIX + "/start-process-instance-by-id";
String START_PROCESS_INSTANCE_BY_KEY = API_PREFIX + "/start-process-instance-by-key";
String COMPLETE_TASK = API_PREFIX + "/complete-task";
String TASK_VARIABLE = API_PREFIX + "/task-variable";
String TASK_VARIABLES = API_PREFIX + "/task-variables";
String PROCESS_DEFINITION = API_PREFIX + "/get-latest-process-definition-by-key";
String DELETE_RPOC_INS = API_PREFIX + "/delete-process-instance";
/**
* 开启流程
*
* @param processDefinitionId 流程id
* @param businessKey 业务key
* @param variables 参数
* @return BladeFlow
*/
@PostMapping(START_PROCESS_INSTANCE_BY_ID)
R<BladeFlow> startProcessInstanceById(@RequestParam("processDefinitionId") String processDefinitionId, @RequestParam("businessKey") String businessKey, @RequestBody Map<String, Object> variables);
/**
* 开启流程
*
* @param processDefinitionKey 流程标识
* @param businessKey 业务key
* @param variables 参数
* @return BladeFlow
*/
@PostMapping(START_PROCESS_INSTANCE_BY_KEY)
R<BladeFlow> startProcessInstanceByKey(@RequestParam("processDefinitionKey") String processDefinitionKey, @RequestParam("businessKey") String businessKey, @RequestBody Map<String, Object> variables);
/**
* 完成任务
*
* @param taskId 任务id
* @param processInstanceId 流程实例id
* @param comment 评论
* @param variables 参数
* @return R
*/
@PostMapping(COMPLETE_TASK)
R completeTask(@RequestParam("taskId") String taskId, @RequestParam("processInstanceId") String processInstanceId, @RequestParam("comment") String comment, @RequestBody Map<String, Object> variables);
/**
* 获取流程变量
*
* @param taskId 任务id
* @param variableName 变量名
* @return R
*/
@GetMapping(TASK_VARIABLE)
R<Object> taskVariable(@RequestParam("taskId") String taskId, @RequestParam("variableName") String variableName);
/**
* 获取流程变量集合
*
* @param taskId 任务id
* @return R
*/
@GetMapping(TASK_VARIABLES)
R<Map<String, Object>> taskVariables(@RequestParam("taskId") String taskId);
/**
* 根据流程标识获取最新的流程定义
*
* @param processDefinitionKey 流程标识
* @return ProcessDefinition
*/
@GetMapping(PROCESS_DEFINITION)
R<BladeFlowDef> getLatestProcessDefinitionByKey(@RequestParam("processDefinitionKey") String processDefinitionKey);
/**
* 根据流程实例id删除流程及其历史信息
*
* @param processInstanceIds 流程标识
* @return
*/
@GetMapping(DELETE_RPOC_INS)
R deleteProcessInstanceByInstanceId(@RequestParam("processInstanceIds") String processInstanceIds);
}

View File

@ -0,0 +1,68 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.flow.core.feign;
import org.security.flow.core.entity.BladeFlowDef;
import org.springblade.core.tool.api.R;
import org.security.flow.core.entity.BladeFlow;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* 流程远程调用失败处理类
*
* @author Chill
*/
@Component
public class IFlowClientFallback implements IFlowClient {
@Override
public R<BladeFlow> startProcessInstanceById(String processDefinitionId, String businessKey, Map<String, Object> variables) {
return R.fail("远程调用失败");
}
@Override
public R<BladeFlow> startProcessInstanceByKey(String processDefinitionKey, String businessKey, Map<String, Object> variables) {
return R.fail("远程调用失败");
}
@Override
public R completeTask(String taskId, String processInstanceId, String comment, Map<String, Object> variables) {
return R.fail("远程调用失败");
}
@Override
public R<Object> taskVariable(String taskId, String variableName) {
return R.fail("远程调用失败");
}
@Override
public R<Map<String, Object>> taskVariables(String taskId) {
return R.fail("远程调用失败");
}
@Override
public R<BladeFlowDef> getLatestProcessDefinitionByKey(String processDefinitionKey) {
return R.fail("远程调用失败");
}
@Override
public R deleteProcessInstanceByInstanceId(String processInstanceIds) {
return R.fail("远程调用失败");
}
}

View File

@ -0,0 +1,76 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.flow.core.utils;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import org.security.flow.core.constant.ProcessConstant;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.StringUtil;
import java.util.HashMap;
import java.util.Map;
/**
* 工作流工具类
*
* @author Chill
*/
public class FlowUtil {
/**
* 定义流程key对应的表名
*/
private final static Map<String, String> BUSINESS_TABLE = new HashMap<>();
static {
BUSINESS_TABLE.put(ProcessConstant.LEAVE_KEY, "blade_process_leave");
}
/**
* 通过流程key获取业务表名
*
* @param key 流程key
*/
public static String getBusinessTable(String key) {
String businessTable = BUSINESS_TABLE.get(key);
if (Func.isEmpty(businessTable)) {
throw new RuntimeException("流程启动失败,未找到相关业务表");
}
return businessTable;
}
/**
* 获取业务标识
*
* @param businessTable 业务表
* @param businessId 业务表主键
* @return businessKey
*/
public static String getBusinessKey(String businessTable, String businessId) {
return StringUtil.format("{}:{}", businessTable, businessId);
}
/**
* 通过流程对象获取业务表名
*
* @param clazz 流程对象类
*/
public static String getBusinessTable(Class clazz) {
String tableName = SqlHelper.table(clazz).getTableName();
return tableName;
}
}

View File

@ -0,0 +1,71 @@
/*
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Chill 庄骞 (smallchill@163.com)
*/
package org.security.flow.core.utils;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.StringUtil;
import static org.springblade.core.launch.constant.FlowConstant.TASK_USR_PREFIX;
/**
* 工作流任务工具类
*
* @author Chill
*/
public class TaskUtil {
/**
* 获取任务用户格式
*
* @return taskUser
*/
public static String getTaskUser() {
return StringUtil.format("{}{}", TASK_USR_PREFIX, AuthUtil.getUserId());
}
/**
* 获取任务用户格式
*
* @param userId 用户id
* @return taskUser
*/
public static String getTaskUser(String userId) {
return StringUtil.format("{}{}", TASK_USR_PREFIX, userId);
}
/**
* 获取用户主键
*
* @param taskUser 任务用户
* @return userId
*/
public static Long getUserId(String taskUser) {
return Func.toLong(StringUtil.removePrefix(taskUser, TASK_USR_PREFIX));
}
/**
* 获取用户组格式
*
* @return candidateGroup
*/
public static String getCandidateGroup() {
return AuthUtil.getUserRole();
}
}

Some files were not shown because too many files have changed in this diff Show More