Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
120eb1ee69 | ||
|
|
252c538571 | ||
|
|
1eec373b7e | ||
|
|
aa9b96a9c1 | ||
|
|
059fb30aa4 | ||
|
|
2afaf7ad34 | ||
|
|
076a0956c5 | ||
|
|
4a91ec47bf | ||
|
|
1066b43b3d | ||
|
|
3f78125c43 | ||
|
|
144a44673b | ||
|
|
777f7b3758 | ||
|
|
947fa0fea3 | ||
|
|
7109e89fb4 | ||
|
|
70e7b1d544 | ||
|
|
613f86155c | ||
|
|
8d0b58e48f | ||
|
|
8cea9dc977 | ||
|
|
471acfdf00 | ||
|
|
8ed42131d0 | ||
|
|
18c605354a | ||
|
|
8c04411458 | ||
|
|
9a8d1d05cd | ||
|
|
1cbaf9c424 | ||
|
|
537c2fc108 | ||
|
|
122b568cf5 | ||
|
|
8b97c02d15 | ||
|
|
dcfb016ce5 | ||
|
|
c842de9e23 |
15
.env.example
Normal file
15
.env.example
Normal file
@@ -0,0 +1,15 @@
|
||||
SERVICE_PORT=1081
|
||||
VOLUME_BASE=/data/orion-visor-space/docker-volumes
|
||||
|
||||
MYSQL_HOST=mysql
|
||||
MYSQL_PORT=3306
|
||||
MYSQL_DATABASE=orion_visor
|
||||
MYSQL_USER=orion
|
||||
MYSQL_PASSWORD=Data@123456
|
||||
MYSQL_ROOT_PASSWORD=Data@123456
|
||||
|
||||
REDIS_HOST=redis
|
||||
REDIS_PASSWORD=Data@123456
|
||||
|
||||
SECRET_KEY=uQeacXV8b3isvKLK
|
||||
DEMO_MODE=false
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -33,3 +33,4 @@ build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
.env
|
||||
|
||||
@@ -19,12 +19,12 @@
|
||||
<a target="_blank"
|
||||
style="text-decoration: none !important;"
|
||||
href="https://gitee.com/dromara/orion-visor/stargazers">
|
||||
<img src="https://gitee.com/dromara/orion-visor/badge/star.svg?theme=dark" alt="star" />
|
||||
<img src="https://gitee.com/dromara/orion-visor/badge/star.svg?theme=gvp" alt="star" />
|
||||
</a>
|
||||
<a target="_blank"
|
||||
style="text-decoration: none !important;"
|
||||
href="https://gitee.com/dromara/orion-visor/members">
|
||||
<img src="https://gitee.com/dromara/orion-visor/badge/fork.svg?theme=dark" alt="fork" />
|
||||
<img src="https://gitee.com/dromara/orion-visor/badge/fork.svg?theme=gvp" alt="fork" />
|
||||
</a>
|
||||
<a target="_blank"
|
||||
style="text-decoration: none !important;"
|
||||
@@ -63,7 +63,7 @@
|
||||
|
||||
```bash
|
||||
# clone
|
||||
git clone https://github.com/dromara/orion-visor
|
||||
git clone --depth=1 https://github.com/dromara/orion-visor
|
||||
cd orion-visor
|
||||
# 启动
|
||||
docker compose up -d
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
version: '3.3'
|
||||
services:
|
||||
orion-visor-service:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-service:2.1.1
|
||||
service:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-service:2.1.4
|
||||
privileged: true
|
||||
ports:
|
||||
- 1081:80
|
||||
environment:
|
||||
- MYSQL_HOST=orion-visor-mysql
|
||||
- MYSQL_HOST=mysql
|
||||
- MYSQL_PORT=3306
|
||||
- MYSQL_DATABASE=orion_visor
|
||||
- MYSQL_USER=root
|
||||
- MYSQL_PASSWORD=Data@123456
|
||||
- REDIS_HOST=orion-visor-redis
|
||||
- REDIS_HOST=redis
|
||||
- REDIS_PASSWORD=Data@123456
|
||||
- SECRET_KEY=uQeacXV8b3isvKLK
|
||||
- DEMO_MODE=false
|
||||
@@ -24,15 +24,15 @@ services:
|
||||
retries: 200
|
||||
start_period: 3s
|
||||
depends_on:
|
||||
orion-visor-mysql:
|
||||
mysql:
|
||||
condition: service_healthy
|
||||
orion-visor-redis:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
links:
|
||||
- orion-visor-mysql
|
||||
- orion-visor-redis
|
||||
orion-visor-mysql:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-mysql:2.1.1
|
||||
- mysql
|
||||
- redis
|
||||
mysql:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-mysql:2.1.4
|
||||
privileged: true
|
||||
ports:
|
||||
- 3307:3306
|
||||
@@ -51,8 +51,8 @@ services:
|
||||
timeout: 60s
|
||||
retries: 10
|
||||
start_period: 3s
|
||||
orion-visor-redis:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-redis:2.1.1
|
||||
redis:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-redis:2.1.4
|
||||
privileged: true
|
||||
ports:
|
||||
- 6380:6379
|
||||
@@ -71,9 +71,9 @@ services:
|
||||
build:
|
||||
context: ./docker/e2e
|
||||
environment:
|
||||
SERVER: http://orion-visor-service:80
|
||||
SERVER: http://service:80
|
||||
depends_on:
|
||||
orion-visor-service:
|
||||
service:
|
||||
condition: service_healthy
|
||||
links:
|
||||
- orion-visor-service
|
||||
- service
|
||||
|
||||
@@ -1,73 +1,78 @@
|
||||
version: '3.3'
|
||||
services:
|
||||
orion-visor-service:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-service:2.1.1
|
||||
service:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-service:2.1.4
|
||||
privileged: true
|
||||
ports:
|
||||
- 1081:80
|
||||
- ${SERVICE_PORT:-1081}:80
|
||||
environment:
|
||||
- MYSQL_HOST=orion-visor-mysql
|
||||
- MYSQL_PORT=3306
|
||||
- MYSQL_DATABASE=orion_visor
|
||||
- MYSQL_USER=root
|
||||
- MYSQL_PASSWORD=Data@123456
|
||||
- REDIS_HOST=orion-visor-redis
|
||||
- REDIS_PASSWORD=Data@123456
|
||||
- SECRET_KEY=uQeacXV8b3isvKLK
|
||||
- DEMO_MODE=false
|
||||
- MYSQL_HOST=${MYSQL_HOST:-mysql}
|
||||
- MYSQL_PORT=${MYSQL_PORT:-3306}
|
||||
- MYSQL_DATABASE=${MYSQL_DATABASE:-orion_visor}
|
||||
- MYSQL_USER=${MYSQL_USER:-root}
|
||||
- MYSQL_PASSWORD=${MYSQL_PASSWORD:-Data@123456}
|
||||
- REDIS_HOST=${REDIS_HOST:-redis}
|
||||
- REDIS_PASSWORD=${REDIS_PASSWORD:-Data@123456}
|
||||
- SECRET_KEY=${SECRET_KEY:-uQeacXV8b3isvKLK}
|
||||
- DEMO_MODE=${DEMO_MODE:-false}
|
||||
volumes:
|
||||
- /data/orion-visor-space/docker-volumes/service/root-orion:/root/orion
|
||||
- ${VOLUME_BASE:-/data/orion-visor-space/docker-volumes}/service/root-orion:/root/orion
|
||||
healthcheck:
|
||||
test: [ "CMD", "curl", "http://127.0.0.1:9200/orion-visor/api/server/bootstrap/health" ]
|
||||
interval: 3s
|
||||
interval: 15s
|
||||
timeout: 300s
|
||||
retries: 200
|
||||
retries: 15
|
||||
start_period: 3s
|
||||
depends_on:
|
||||
orion-visor-mysql:
|
||||
mysql:
|
||||
condition: service_healthy
|
||||
orion-visor-redis:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
links:
|
||||
- orion-visor-mysql
|
||||
- orion-visor-redis
|
||||
orion-visor-mysql:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-mysql:2.1.1
|
||||
- mysql
|
||||
- redis
|
||||
mysql:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-mysql:2.1.4
|
||||
privileged: true
|
||||
ports:
|
||||
- 3307:3306
|
||||
environment:
|
||||
- MYSQL_DATABASE=orion_visor
|
||||
- MYSQL_USER=orion
|
||||
- MYSQL_PASSWORD=Data@123456
|
||||
- MYSQL_ROOT_PASSWORD=Data@123456
|
||||
- MYSQL_DATABASE=${MYSQL_DATABASE:-orion_visor}
|
||||
- MYSQL_USER=${MYSQL_USER:-orion}
|
||||
- MYSQL_PASSWORD=${MYSQL_PASSWORD:-Data@123456}
|
||||
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-Data@123456}
|
||||
volumes:
|
||||
- /data/orion-visor-space/docker-volumes/mysql/var-lib-mysql:/var/lib/mysql
|
||||
- /data/orion-visor-space/docker-volumes/mysql/var-lib-mysql-files:/var/lib/mysql-files
|
||||
- /data/orion-visor-space/docker-volumes/mysql/etc-mysql:/etc/mysql
|
||||
- ${VOLUME_BASE:-/data/orion-visor-space/docker-volumes}/mysql/var-lib-mysql:/var/lib/mysql
|
||||
- ${VOLUME_BASE:-/data/orion-visor-space/docker-volumes}/mysql/var-lib-mysql-files:/var/lib/mysql-files
|
||||
- ${VOLUME_BASE:-/data/orion-visor-space/docker-volumes}/mysql/etc-mysql:/etc/mysql
|
||||
healthcheck:
|
||||
test: [ "CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/3306" ]
|
||||
interval: 3s
|
||||
interval: 15s
|
||||
timeout: 60s
|
||||
retries: 10
|
||||
retries: 15
|
||||
start_period: 3s
|
||||
orion-visor-redis:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-redis:2.1.1
|
||||
redis:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-redis:2.1.4
|
||||
privileged: true
|
||||
ports:
|
||||
- 6380:6379
|
||||
environment:
|
||||
- REDIS_PASSWORD=Data@123456
|
||||
- REDIS_PASSWORD=${REDIS_PASSWORD:-Data@123456}
|
||||
volumes:
|
||||
- /data/orion-visor-space/docker-volumes/redis/data:/data
|
||||
- ${VOLUME_BASE:-/data/orion-visor-space/docker-volumes}/redis/data:/data
|
||||
command: sh -c "redis-server /usr/local/redis.conf --requirepass $${REDIS_PASSWORD}"
|
||||
healthcheck:
|
||||
test: [ "CMD", "redis-cli", "--raw", "incr", "ping" ]
|
||||
interval: 3s
|
||||
interval: 15s
|
||||
timeout: 60s
|
||||
retries: 10
|
||||
retries: 15
|
||||
start_period: 3s
|
||||
orion-visor-adminer:
|
||||
image: adminer
|
||||
adminer:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-adminer:2.1.4
|
||||
ports:
|
||||
- 8081:8080
|
||||
depends_on:
|
||||
mysql:
|
||||
condition: service_healthy
|
||||
links:
|
||||
- mysql
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
docker compose down
|
||||
# demo 启动
|
||||
if [ "$1" == "demo" ]; then
|
||||
sed -i 's/DEMO_MODE=false/DEMO_MODE=true/g' docker-compose.yml
|
||||
sed -i 's/\${DEMO_MODE:-false}/true/g' docker-compose.yml
|
||||
fi
|
||||
docker compose up -d --remove-orphans
|
||||
|
||||
1
docker/adminer/Dockerfile
Normal file
1
docker/adminer/Dockerfile
Normal file
@@ -0,0 +1 @@
|
||||
FROM adminer:latest
|
||||
4
docker/adminer/build.sh
Normal file
4
docker/adminer/build.sh
Normal file
@@ -0,0 +1,4 @@
|
||||
#/bin/bash
|
||||
version=2.1.4
|
||||
docker build -t orion-visor-adminer:${version} .
|
||||
docker tag orion-visor-adminer:${version} registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-adminer:${version}
|
||||
@@ -1,7 +1,6 @@
|
||||
#/bin/bash
|
||||
version=2.1.1
|
||||
version=2.1.4
|
||||
cp -r ../../sql ./sql
|
||||
docker build -t orion-visor-mysql:${version} .
|
||||
rm -rf ./sql
|
||||
docker tag orion-visor-mysql:${version} registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-mysql:${version}
|
||||
docker push registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-mysql:${version}
|
||||
|
||||
6
docker/push.sh
Normal file
6
docker/push.sh
Normal file
@@ -0,0 +1,6 @@
|
||||
#/bin/bash
|
||||
version=2.1.4
|
||||
docker push registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-adminer:${version}
|
||||
docker push registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-mysql:${version}
|
||||
docker push registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-redis:${version}
|
||||
docker push registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-service:${version}
|
||||
@@ -1,5 +1,4 @@
|
||||
#/bin/bash
|
||||
version=2.1.1
|
||||
version=2.1.4
|
||||
docker build -t orion-visor-redis:${version} .
|
||||
docker tag orion-visor-redis:${version} registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-redis:${version}
|
||||
docker push registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-redis:${version}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
#/bin/bash
|
||||
version=2.1.1
|
||||
version=2.1.4
|
||||
mv ../../orion-visor-launch/target/orion-visor-launch.jar ./orion-visor-launch.jar
|
||||
mv ../../orion-visor-ui/dist ./dist
|
||||
docker build -t orion-visor-service:${version} .
|
||||
rm -rf ./orion-visor-launch.jar
|
||||
rm -rf ./dist
|
||||
docker tag orion-visor-service:${version} registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-service:${version}
|
||||
docker push registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-service:${version}
|
||||
|
||||
@@ -29,7 +29,7 @@ server {
|
||||
location /orion-visor/api {
|
||||
proxy_pass http://localhost:9200/orion-visor/api;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
@@ -19,12 +19,12 @@
|
||||
<a target="_blank"
|
||||
style="text-decoration: none !important;"
|
||||
href="https://gitee.com/lijiahangmax/orion-visor/stargazers">
|
||||
<img src="https://gitee.com/lijiahangmax/orion-visor/badge/star.svg?theme=dark" alt="star" />
|
||||
<img src="https://gitee.com/lijiahangmax/orion-visor/badge/star.svg?theme=gvp" alt="star" />
|
||||
</a>
|
||||
<a target="_blank"
|
||||
style="text-decoration: none !important;"
|
||||
href="https://gitee.com/lijiahangmax/orion-visor/members">
|
||||
<img src="https://gitee.com/lijiahangmax/orion-visor/badge/fork.svg?theme=dark" alt="fork" />
|
||||
<img src="https://gitee.com/lijiahangmax/orion-visor/badge/fork.svg?theme=gvp" alt="fork" />
|
||||
</a>
|
||||
<a target="_blank"
|
||||
style="text-decoration: none !important;"
|
||||
|
||||
@@ -21,22 +21,35 @@ Dashboard 修改)
|
||||
|
||||
```shell
|
||||
# github
|
||||
git clone https://github.com/lijiahangmax/orion-visor
|
||||
git clone --depth=1 https://github.com/lijiahangmax/orion-visor
|
||||
# gitee
|
||||
git clone https://gitee.com/lijiahangmax/orion-visor
|
||||
git clone --depth=1 https://gitee.com/lijiahangmax/orion-visor
|
||||
```
|
||||
|
||||
### 构建镜像
|
||||
### 拉取镜像
|
||||
|
||||
```
|
||||
# 进入仓库目录
|
||||
cd orion-visor
|
||||
# 修改 docker-compose.yml (建议修改)
|
||||
# MYSQL_USER mysql 用户名
|
||||
# MYSQL_PASSWORD mysql 用户密码
|
||||
# MYSQL_ROOT_PASSWORD mysql root 密码
|
||||
# REDIS_PASSWORD redis 密码
|
||||
# SECRET_KEY 加密密钥
|
||||
# 创建名为 .env 的 .env.example 副本
|
||||
cp .env.example .env
|
||||
# 将其中的值删除以保持默认或将其修改为你喜欢的值
|
||||
# SERVICE_PORT 你希望服务监听的端口
|
||||
# VOLUME_BASE 你希望数据持久化保存的目录, 如果不提前创建将以 docker 进程宿主身份创建(通常是 root)
|
||||
|
||||
# MYSQL_HOST mysql 服务所在的主机, 如果你没有现有的 MySQL 请保持值为 mysql, 如果你有自部署的请在 docker-compose.yml 中移除 services.mysql 以节约性能
|
||||
# MYSQL_PORT mysql 监听的端口
|
||||
# MYSQL_DATABASE mysql 数据库
|
||||
# MYSQL_USER mysql 用户名
|
||||
# MYSQL_PASSWORD mysql 用户密码
|
||||
# MYSQL_ROOT_PASSWORD mysql root 密码
|
||||
|
||||
# REDIS_HOST redis 服务所在的主机, 如果你没有现有的 Redis 请保持值为 redis, 如果你有自部署的请在 docker-compose.yml 中移除 services.redis 以节约性能
|
||||
# REDIS_PASSWORD redis 密码
|
||||
|
||||
# SECRET_KEY 加密密钥
|
||||
# 拉取远程镜像
|
||||
docker compose pull
|
||||
```
|
||||
|
||||
### 启动
|
||||
@@ -45,7 +58,7 @@ cd orion-visor
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### 修改加密方式
|
||||
### 修改 MySQL 账户的加密方式
|
||||
|
||||
```
|
||||
访问 adminer: http://localhost:8081
|
||||
|
||||
@@ -14,11 +14,11 @@
|
||||
<url>https://github.com/dromara/orion-visor</url>
|
||||
|
||||
<properties>
|
||||
<revision>2.1.1</revision>
|
||||
<revision>2.1.4</revision>
|
||||
<spring.boot.version>2.7.17</spring.boot.version>
|
||||
<spring.boot.admin.version>2.7.15</spring.boot.admin.version>
|
||||
<flatten.maven.plugin.version>1.5.0</flatten.maven.plugin.version>
|
||||
<orion.kit.version>1.0.7</orion.kit.version>
|
||||
<orion.kit.version>1.0.8</orion.kit.version>
|
||||
<aspectj.version>1.9.7</aspectj.version>
|
||||
<lombok.version>1.18.26</lombok.version>
|
||||
<springdoc.version>1.6.15</springdoc.version>
|
||||
|
||||
@@ -14,7 +14,7 @@ public interface AppConst extends OrionConst {
|
||||
/**
|
||||
* 同 ${orion.version} 迭代时候需要手动更改
|
||||
*/
|
||||
String VERSION = "2.1.1";
|
||||
String VERSION = "2.1.4";
|
||||
|
||||
/**
|
||||
* 同 ${spring.application.name}
|
||||
|
||||
@@ -109,4 +109,6 @@ public interface ErrorMessage {
|
||||
|
||||
String CLIENT_ABORT = "手动中断";
|
||||
|
||||
String UNABLE_DOWNLOAD_FOLDER = "无法下载文件夹";
|
||||
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ public class PageRequest implements IPageRequest {
|
||||
@Schema(description = "页码")
|
||||
private int page;
|
||||
|
||||
@Range(min = 1, max = 100, groups = Page.class)
|
||||
@Range(min = 1, max = 200, groups = Page.class)
|
||||
@Schema(description = "大小")
|
||||
private int limit;
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.orion.visor.framework.common.meta;
|
||||
|
||||
import com.alibaba.ttl.TransmittableThreadLocal;
|
||||
import com.orion.lang.id.UUIds;
|
||||
import org.slf4j.MDC;
|
||||
|
||||
/**
|
||||
* traceId 持有者
|
||||
@@ -23,16 +25,74 @@ public class TraceIdHolder {
|
||||
*/
|
||||
private static final ThreadLocal<String> HOLDER = new TransmittableThreadLocal<>();
|
||||
|
||||
/**
|
||||
* 获取 traceId
|
||||
*
|
||||
* @return traceId
|
||||
*/
|
||||
public static String get() {
|
||||
return HOLDER.get();
|
||||
}
|
||||
|
||||
public static void set(String traceId) {
|
||||
HOLDER.set(traceId);
|
||||
/**
|
||||
* 设置 traceId
|
||||
*/
|
||||
public static void set() {
|
||||
set(createTraceId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 traceId
|
||||
*
|
||||
* @param traceId traceId
|
||||
*/
|
||||
public static void set(String traceId) {
|
||||
// 设置应用上下文
|
||||
HOLDER.set(traceId);
|
||||
// 设置日志上下文
|
||||
setMdc(traceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除 traceId
|
||||
*/
|
||||
public static void remove() {
|
||||
// 移除应用上下文
|
||||
HOLDER.remove();
|
||||
// 移除日志上下文
|
||||
removeMdc();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从应用上下文 设置到日志上下文
|
||||
*/
|
||||
public static void setMdc() {
|
||||
setMdc(HOLDER.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置到日志上下文
|
||||
*
|
||||
* @param traceId traceId
|
||||
*/
|
||||
public static void setMdc(String traceId) {
|
||||
MDC.put(TRACE_ID_MDC, traceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除日志上下文
|
||||
*/
|
||||
public static void removeMdc() {
|
||||
MDC.remove(TRACE_ID_MDC);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 traceId
|
||||
*
|
||||
* @return traceId
|
||||
*/
|
||||
public static String createTraceId() {
|
||||
return UUIds.random32();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.orion.visor.framework.mybatis.core.generator.core;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.baomidou.mybatisplus.generator.config.po.TableField;
|
||||
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
|
||||
import com.orion.lang.utils.Exceptions;
|
||||
import com.orion.lang.utils.Strings;
|
||||
import com.orion.visor.framework.common.constant.Const;
|
||||
import com.orion.visor.framework.common.constant.FieldConst;
|
||||
@@ -47,7 +48,7 @@ public class DictParser {
|
||||
.stream()
|
||||
.filter(s -> variable.equals(s.getName()) || variable.equals(s.getPropertyName()))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new RuntimeException("未查询到字典映射字段 " + variable));
|
||||
.orElseThrow(() -> Exceptions.runtime("未查询到字典映射字段 " + variable));
|
||||
// 设置字段名称
|
||||
if (meta.getField() == null) {
|
||||
meta.setField(Strings.firstUpper(tableField.getPropertyName()));
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
<template #extra="{ record }">
|
||||
<a-space>
|
||||
<!-- 更多操作 -->
|
||||
<a-dropdown trigger="hover">
|
||||
<a-dropdown trigger="hover" :popup-max-height="false">
|
||||
<icon-more class="card-extra-icon" />
|
||||
<template #content>
|
||||
<!-- 修改 -->
|
||||
@@ -118,7 +118,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { ${vue.featureEntity}QueryRequest, ${vue.featureEntity}QueryResponse } from '@/api/${vue.module}/${vue.feature}';
|
||||
import { usePagination, useColLayout } from '@/types/card';
|
||||
import { useCardPagination, useCardColLayout } from '@/hooks/card';
|
||||
import { computed, reactive, ref, onMounted } from 'vue';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import { objectTruthKeyCount, resetObject } from '@/utils';
|
||||
@@ -136,8 +136,8 @@
|
||||
|
||||
const emits = defineEmits(['openAdd', 'openUpdate']);
|
||||
|
||||
const cardColLayout = useColLayout();
|
||||
const pagination = usePagination();
|
||||
const cardColLayout = useCardColLayout();
|
||||
const pagination = useCardPagination();
|
||||
const { loading, setLoading } = useLoading();
|
||||
#if($dictMap.entrySet().size() > 0)
|
||||
const { toOptions, getDictValue } = useDictStore();
|
||||
|
||||
@@ -151,9 +151,9 @@
|
||||
import {} from '../types/const';
|
||||
#end
|
||||
#if($vue.enableRowSelection)
|
||||
import { usePagination, useRowSelection } from '@/types/table';
|
||||
import { useTablePagination, useRowSelection } from '@/hooks/table';
|
||||
#else
|
||||
import { usePagination } from '@/types/table';
|
||||
import { useTablePagination } from '@/hooks/table';
|
||||
#end
|
||||
#if($dictMap.entrySet().size() > 0)
|
||||
import { useDictStore } from '@/store';
|
||||
@@ -161,7 +161,7 @@
|
||||
|
||||
const emits = defineEmits(['openAdd', 'openUpdate']);
|
||||
|
||||
const pagination = usePagination();
|
||||
const pagination = useTablePagination();
|
||||
#if($vue.enableRowSelection)
|
||||
const rowSelection = useRowSelection();
|
||||
#end
|
||||
|
||||
@@ -98,6 +98,13 @@ public class SecurityUtils {
|
||||
return loginUser != null ? loginUser.getTimestamp() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空用户上下文
|
||||
*/
|
||||
public static void clearAuthentication() {
|
||||
SecurityContextHolder.getContext().setAuthentication(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前用户
|
||||
*
|
||||
@@ -107,7 +114,9 @@ public class SecurityUtils {
|
||||
public static void setLoginUser(LoginUser loginUser, HttpServletRequest request) {
|
||||
// 创建 authentication
|
||||
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(loginUser, null, Collections.emptyList());
|
||||
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||
if (request != null) {
|
||||
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||
}
|
||||
// 设置上下文
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package com.orion.visor.framework.web.core.filter;
|
||||
|
||||
import com.orion.lang.id.UUIds;
|
||||
import com.orion.visor.framework.common.meta.TraceIdHolder;
|
||||
import org.slf4j.MDC;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
@@ -23,21 +21,17 @@ public class TraceIdFilter extends OncePerRequestFilter {
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
|
||||
try {
|
||||
// 获 traceId
|
||||
String traceId = UUIds.random32();
|
||||
// 设置应用上下文
|
||||
// 获取 traceId
|
||||
String traceId = TraceIdHolder.createTraceId();
|
||||
// 设置 traceId 上下文
|
||||
TraceIdHolder.set(traceId);
|
||||
// 设置日志上下文
|
||||
MDC.put(TraceIdHolder.TRACE_ID_MDC, traceId);
|
||||
// 设置响应头
|
||||
response.setHeader(TraceIdHolder.TRACE_ID_HEADER, traceId);
|
||||
// 执行请求
|
||||
filterChain.doFilter(request, response);
|
||||
} finally {
|
||||
// 清理应用上下文
|
||||
// 清空 traceId 上下文
|
||||
TraceIdHolder.remove();
|
||||
// 清理日志上下文
|
||||
MDC.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -167,6 +167,8 @@ app:
|
||||
allow-refresh: true
|
||||
# 凭证续签最大次数
|
||||
max-refresh-count: 3
|
||||
# 登录失败发送站内信阈值
|
||||
login-failed-send-threshold: 3
|
||||
# 登录失败锁定次数
|
||||
login-failed-lock-count: 5
|
||||
# 登录失败锁定时间 (分)
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.orion.visor.module.asset.handler.host.terminal.TerminalMessageDispatc
|
||||
import com.orion.visor.module.asset.handler.host.transfer.TransferMessageDispatcher;
|
||||
import com.orion.visor.module.asset.interceptor.ExecLogTailInterceptor;
|
||||
import com.orion.visor.module.asset.interceptor.TerminalAccessInterceptor;
|
||||
import com.orion.visor.module.asset.interceptor.TerminalTransferInterceptor;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
|
||||
@@ -28,6 +29,9 @@ public class AssetWebSocketConfiguration implements WebSocketConfigurer {
|
||||
@Resource
|
||||
private TerminalAccessInterceptor terminalAccessInterceptor;
|
||||
|
||||
@Resource
|
||||
private TerminalTransferInterceptor terminalTransferInterceptor;
|
||||
|
||||
@Resource
|
||||
private ExecLogTailInterceptor execLogTailInterceptor;
|
||||
|
||||
@@ -42,13 +46,13 @@ public class AssetWebSocketConfiguration implements WebSocketConfigurer {
|
||||
|
||||
@Override
|
||||
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
|
||||
// 终端
|
||||
// 终端会话
|
||||
registry.addHandler(terminalMessageDispatcher, prefix + "/host/terminal/{accessToken}")
|
||||
.addInterceptors(terminalAccessInterceptor)
|
||||
.setAllowedOrigins("*");
|
||||
// 文件传输
|
||||
registry.addHandler(transferMessageDispatcher, prefix + "/host/transfer/{accessToken}")
|
||||
.addInterceptors(terminalAccessInterceptor)
|
||||
registry.addHandler(transferMessageDispatcher, prefix + "/host/transfer/{transferToken}")
|
||||
.addInterceptors(terminalTransferInterceptor)
|
||||
.setAllowedOrigins("*");
|
||||
// 执行日志
|
||||
registry.addHandler(execLogTailHandler, prefix + "/exec/log/{token}")
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.orion.visor.framework.biz.operator.log.core.annotation.OperatorLog;
|
||||
import com.orion.visor.framework.common.validator.group.Page;
|
||||
import com.orion.visor.framework.log.core.annotation.IgnoreLog;
|
||||
import com.orion.visor.framework.log.core.enums.IgnoreLogMode;
|
||||
import com.orion.visor.framework.web.core.annotation.DemoDisableApi;
|
||||
import com.orion.visor.framework.web.core.annotation.RestWrapper;
|
||||
import com.orion.visor.module.asset.define.operator.ExecJobOperatorType;
|
||||
import com.orion.visor.module.asset.entity.request.exec.*;
|
||||
@@ -39,6 +40,7 @@ public class ExecJobController {
|
||||
@Resource
|
||||
private ExecJobService execJobService;
|
||||
|
||||
@DemoDisableApi
|
||||
@OperatorLog(ExecJobOperatorType.CREATE)
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建计划任务")
|
||||
@@ -47,6 +49,7 @@ public class ExecJobController {
|
||||
return execJobService.createExecJob(request);
|
||||
}
|
||||
|
||||
@DemoDisableApi
|
||||
@OperatorLog(ExecJobOperatorType.UPDATE)
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新计划任务")
|
||||
@@ -55,6 +58,7 @@ public class ExecJobController {
|
||||
return execJobService.updateExecJobById(request);
|
||||
}
|
||||
|
||||
@DemoDisableApi
|
||||
@OperatorLog(ExecJobOperatorType.UPDATE_STATUS)
|
||||
@PutMapping("/update-status")
|
||||
@Operation(summary = "更新计划任务状态")
|
||||
@@ -88,6 +92,7 @@ public class ExecJobController {
|
||||
return execJobService.getExecJobPage(request);
|
||||
}
|
||||
|
||||
@DemoDisableApi
|
||||
@OperatorLog(ExecJobOperatorType.DELETE)
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除计划任务")
|
||||
@@ -97,6 +102,7 @@ public class ExecJobController {
|
||||
return execJobService.deleteExecJobById(id);
|
||||
}
|
||||
|
||||
@DemoDisableApi
|
||||
@OperatorLog(ExecJobOperatorType.DELETE)
|
||||
@DeleteMapping("/batch-delete")
|
||||
@Operation(summary = "批量删除计划任务")
|
||||
|
||||
@@ -49,5 +49,12 @@ public class HostTerminalController {
|
||||
return hostTerminalService.getTerminalAccessToken();
|
||||
}
|
||||
|
||||
@GetMapping("/transfer")
|
||||
@Operation(summary = "获取主机终端 transferToken")
|
||||
@PreAuthorize("@ss.hasPermission('asset:host-terminal:access')")
|
||||
public String getTerminalTransferToken() {
|
||||
return hostTerminalService.getTerminalTransferToken();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.orion.lang.define.cache.key.CacheKeyBuilder;
|
||||
import com.orion.lang.define.cache.key.CacheKeyDefine;
|
||||
import com.orion.lang.define.cache.key.struct.RedisCacheStruct;
|
||||
import com.orion.visor.module.asset.entity.dto.HostTerminalAccessDTO;
|
||||
import com.orion.visor.module.asset.entity.dto.HostTerminalTransferDTO;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@@ -24,4 +25,12 @@ public interface HostTerminalCacheKeyDefine {
|
||||
.timeout(3, TimeUnit.MINUTES)
|
||||
.build();
|
||||
|
||||
CacheKeyDefine HOST_TERMINAL_TRANSFER = new CacheKeyBuilder()
|
||||
.key("host:terminal:transfer:{}")
|
||||
.desc("主机终端传输token ${token}")
|
||||
.type(HostTerminalTransferDTO.class)
|
||||
.struct(RedisCacheStruct.STRING)
|
||||
.timeout(3, TimeUnit.MINUTES)
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
@@ -42,10 +42,6 @@ public class CommandSnippetDO extends BaseDO {
|
||||
@TableField("name")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "触发前缀")
|
||||
@TableField("prefix")
|
||||
private String prefix;
|
||||
|
||||
@Schema(description = "代码片段")
|
||||
@TableField("command")
|
||||
private String command;
|
||||
|
||||
@@ -34,9 +34,6 @@ public class CommandSnippetCacheDTO implements LongCacheIdModel, Serializable {
|
||||
@Schema(description = "名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "触发前缀")
|
||||
private String prefix;
|
||||
|
||||
@Schema(description = "代码片段")
|
||||
private String command;
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.orion.visor.module.asset.entity.dto;
|
||||
|
||||
import com.orion.visor.framework.desensitize.core.annotation.DesensitizeObject;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 主机终端传输参数
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/12/26 15:47
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@DesensitizeObject
|
||||
@Schema(name = "HostTerminalTransferDTO", description = "主机终端传输参数")
|
||||
public class HostTerminalTransferDTO {
|
||||
|
||||
@Schema(description = "userId")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "username")
|
||||
private String username;
|
||||
|
||||
}
|
||||
@@ -33,9 +33,6 @@ public class CommandSnippetVO implements Serializable {
|
||||
@Schema(description = "名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "触发前缀")
|
||||
private String prefix;
|
||||
|
||||
@Schema(description = "代码片段")
|
||||
private String command;
|
||||
|
||||
|
||||
@@ -5,7 +5,9 @@ import com.orion.lang.exception.argument.InvalidArgumentException;
|
||||
import com.orion.lang.utils.Exceptions;
|
||||
import com.orion.lang.utils.Strings;
|
||||
import com.orion.net.host.SessionHolder;
|
||||
import com.orion.net.host.SessionLogger;
|
||||
import com.orion.net.host.SessionStore;
|
||||
import com.orion.visor.framework.common.constant.AppConst;
|
||||
import com.orion.visor.framework.common.constant.Const;
|
||||
import com.orion.visor.framework.common.utils.CryptoUtils;
|
||||
import com.orion.visor.module.asset.entity.dto.HostTerminalConnectDTO;
|
||||
@@ -40,7 +42,10 @@ public class SessionStores {
|
||||
CURRENT_ADDRESS.set(address);
|
||||
// 创建会话
|
||||
SessionHolder sessionHolder = SessionHolder.create();
|
||||
sessionHolder.setLogger(SessionLogger.INFO);
|
||||
SessionStore session = createSessionStore(conn, sessionHolder);
|
||||
// 设置版本
|
||||
session.getSession().setClientVersion("SSH-2.0-ORION_VISOR_V" + AppConst.VERSION);
|
||||
// 连接
|
||||
session.connect();
|
||||
log.info("SessionStores-open-success hostId: {}, address: {}, username: {}", hostId, address, username);
|
||||
|
||||
@@ -131,9 +131,9 @@ public enum InputTypeEnum {
|
||||
* SFTP 修改文件权限
|
||||
*/
|
||||
SFTP_CHMOD("cm",
|
||||
SftpChangeModHandler.class,
|
||||
SftpChangeModeHandler.class,
|
||||
new String[]{"type", "sessionId", "path", "mod"},
|
||||
SftpChangeModRequest.class,
|
||||
SftpChangeModeRequest.class,
|
||||
true),
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,7 +5,7 @@ import com.orion.visor.framework.biz.operator.log.core.utils.OperatorLogs;
|
||||
import com.orion.visor.framework.common.enums.BooleanBit;
|
||||
import com.orion.visor.module.asset.define.operator.HostTerminalOperatorType;
|
||||
import com.orion.visor.module.asset.handler.host.terminal.enums.OutputTypeEnum;
|
||||
import com.orion.visor.module.asset.handler.host.terminal.model.request.SftpChangeModRequest;
|
||||
import com.orion.visor.module.asset.handler.host.terminal.model.request.SftpChangeModeRequest;
|
||||
import com.orion.visor.module.asset.handler.host.terminal.model.response.SftpBaseResponse;
|
||||
import com.orion.visor.module.asset.handler.host.terminal.session.ISftpSession;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -23,24 +23,24 @@ import java.util.Map;
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class SftpChangeModHandler extends AbstractTerminalHandler<SftpChangeModRequest> {
|
||||
public class SftpChangeModeHandler extends AbstractTerminalHandler<SftpChangeModeRequest> {
|
||||
|
||||
@Override
|
||||
public void handle(WebSocketSession channel, SftpChangeModRequest payload) {
|
||||
public void handle(WebSocketSession channel, SftpChangeModeRequest payload) {
|
||||
long startTime = System.currentTimeMillis();
|
||||
// 获取会话
|
||||
String sessionId = payload.getSessionId();
|
||||
ISftpSession session = hostTerminalManager.getSession(channel.getId(), sessionId);
|
||||
String path = payload.getPath();
|
||||
Integer mod = payload.getMod();
|
||||
log.info("SftpChangeModHandler-handle start sessionId: {}, path: {}, mod: {}", sessionId, path, mod);
|
||||
log.info("SftpChangeModeHandler-handle start sessionId: {}, path: {}, mod: {}", sessionId, path, mod);
|
||||
Exception ex = null;
|
||||
// 修改权限
|
||||
try {
|
||||
session.chmod(path, mod);
|
||||
log.info("SftpChangeModHandler-handle success sessionId: {}, path: {}, mod: {}", sessionId, path, mod);
|
||||
log.info("SftpChangeModeHandler-handle success sessionId: {}, path: {}, mod: {}", sessionId, path, mod);
|
||||
} catch (Exception e) {
|
||||
log.error("SftpChangeModHandler-handle error sessionId: {}", sessionId, e);
|
||||
log.error("SftpChangeModeHandler-handle error sessionId: {}", sessionId, e);
|
||||
ex = e;
|
||||
}
|
||||
// 返回
|
||||
@@ -65,13 +65,15 @@ public class TerminalConnectHandler extends AbstractTerminalHandler<TerminalConn
|
||||
// 移除会话连接信息
|
||||
channel.getAttributes().remove(sessionId);
|
||||
Exception ex = null;
|
||||
ITerminalSession session = null;
|
||||
try {
|
||||
// 连接主机
|
||||
ITerminalSession session = this.connect(sessionId, connect, channel, payload);
|
||||
session = this.connect(sessionId, connect, channel, payload);
|
||||
// 添加会话到 manager
|
||||
hostTerminalManager.addSession(session);
|
||||
} catch (Exception e) {
|
||||
ex = e;
|
||||
Streams.close(session);
|
||||
// 修改连接状态为失败
|
||||
Map<String, Object> extra = Maps.newMap(4);
|
||||
extra.put(ExtraFieldConst.ERROR_MESSAGE, this.getConnectErrorMessage(e));
|
||||
|
||||
@@ -20,7 +20,7 @@ import lombok.experimental.SuperBuilder;
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class SftpChangeModRequest extends SftpBaseRequest {
|
||||
public class SftpChangeModeRequest extends SftpBaseRequest {
|
||||
|
||||
/**
|
||||
* 10进制的8进制 权限
|
||||
@@ -12,7 +12,6 @@ import com.orion.visor.framework.common.constant.Const;
|
||||
import com.orion.visor.framework.common.utils.Valid;
|
||||
import com.orion.visor.module.asset.handler.host.terminal.model.TerminalConfig;
|
||||
import com.orion.visor.module.asset.handler.host.terminal.model.response.SftpFileVO;
|
||||
import com.orion.visor.module.asset.utils.SftpUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
|
||||
@@ -84,9 +83,7 @@ public class SftpSession extends TerminalSession implements ISftpSession {
|
||||
public void move(String source, String target) {
|
||||
source = Valid.checkNormalize(source);
|
||||
// 移动
|
||||
SftpUtils.move(executor, source, target);
|
||||
// FIXME kit
|
||||
// executor.move(source, target);
|
||||
executor.move(source, target);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -37,8 +37,11 @@ public class DownloadSession extends TransferSession implements StreamingRespons
|
||||
|
||||
protected InputStream inputStream;
|
||||
|
||||
private Long fileSize;
|
||||
|
||||
public DownloadSession(HostTerminalConnectDTO connectInfo, SessionStore sessionStore, WebSocketSession channel) {
|
||||
super(connectInfo, sessionStore, channel);
|
||||
this.fileSize = 0L;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -53,7 +56,9 @@ public class DownloadSession extends TransferSession implements StreamingRespons
|
||||
// 检查文件是否存在
|
||||
SftpFile file = executor.getFile(path);
|
||||
Valid.notNull(file, ErrorMessage.FILE_ABSENT);
|
||||
if (file.getSize() == 0L) {
|
||||
// 验证非文件夹
|
||||
Valid.isTrue(!file.isDirectory(), ErrorMessage.UNABLE_DOWNLOAD_FOLDER);
|
||||
if ((this.fileSize = file.getSize()) == 0L) {
|
||||
// 文件为空
|
||||
log.info("DownloadSession.startDownload file empty channelId: {}, path: {}", channelId, path);
|
||||
TransferUtils.sendMessage(channel, TransferReceiver.FINISH, null);
|
||||
@@ -101,14 +106,14 @@ public class DownloadSession extends TransferSession implements StreamingRespons
|
||||
// 首次触发
|
||||
if (i == 0) {
|
||||
outputStream.flush();
|
||||
this.sendProgress(size, null);
|
||||
this.sendProgress(size, fileSize);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
// 最后一次也要 flush
|
||||
if (i != 0) {
|
||||
outputStream.flush();
|
||||
this.sendProgress(size, null);
|
||||
this.sendProgress(size, fileSize);
|
||||
}
|
||||
log.info("DownloadSession.download finish channelId: {}, path: {}", channelId, path);
|
||||
} catch (Exception e) {
|
||||
|
||||
@@ -17,7 +17,7 @@ import javax.annotation.Resource;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 终端拦截器
|
||||
* 终端访问拦截器
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
@@ -34,11 +34,11 @@ public class TerminalAccessInterceptor implements HandshakeInterceptor {
|
||||
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
|
||||
// 获取 accessToken
|
||||
String accessToken = Urls.getUrlSource(request.getURI().getPath());
|
||||
log.info("TerminalInterceptor-beforeHandshake start accessToken: {}", accessToken);
|
||||
log.info("TerminalAccessInterceptor-beforeHandshake start accessToken: {}", accessToken);
|
||||
// 获取连接数据
|
||||
HostTerminalAccessDTO access = hostTerminalService.getAccessInfoByToken(accessToken);
|
||||
if (access == null) {
|
||||
log.error("TerminalInterceptor-beforeHandshake absent accessToken: {}", accessToken);
|
||||
log.error("TerminalAccessInterceptor-beforeHandshake absent accessToken: {}", accessToken);
|
||||
return false;
|
||||
}
|
||||
// 设置参数
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.orion.visor.module.asset.interceptor;
|
||||
|
||||
import com.orion.lang.utils.Urls;
|
||||
import com.orion.visor.framework.common.constant.ExtraFieldConst;
|
||||
import com.orion.visor.framework.common.meta.TraceIdHolder;
|
||||
import com.orion.visor.framework.common.utils.Requests;
|
||||
import com.orion.visor.module.asset.entity.dto.HostTerminalTransferDTO;
|
||||
import com.orion.visor.module.asset.service.HostTerminalService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.socket.WebSocketHandler;
|
||||
import org.springframework.web.socket.server.HandshakeInterceptor;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 终端传输拦截器
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/12/27 23:53
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class TerminalTransferInterceptor implements HandshakeInterceptor {
|
||||
|
||||
@Resource
|
||||
private HostTerminalService hostTerminalService;
|
||||
|
||||
@Override
|
||||
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
|
||||
// 获取 transferToken
|
||||
String transferToken = Urls.getUrlSource(request.getURI().getPath());
|
||||
log.info("TerminalTransferInterceptor-beforeHandshake start transferToken: {}", transferToken);
|
||||
// 获取连接数据
|
||||
HostTerminalTransferDTO transfer = hostTerminalService.getTransferInfoByToken(transferToken);
|
||||
if (transfer == null) {
|
||||
log.error("TerminalTransferInterceptor-beforeHandshake absent transferToken: {}", transferToken);
|
||||
return false;
|
||||
}
|
||||
// 设置参数
|
||||
attributes.put(ExtraFieldConst.USER_ID, transfer.getUserId());
|
||||
attributes.put(ExtraFieldConst.USERNAME, transfer.getUsername());
|
||||
attributes.put(ExtraFieldConst.TRACE_ID, TraceIdHolder.get());
|
||||
attributes.put(ExtraFieldConst.IDENTITY, Requests.getIdentity());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package com.orion.visor.module.asset.service;
|
||||
import com.orion.visor.module.asset.entity.domain.HostDO;
|
||||
import com.orion.visor.module.asset.entity.dto.HostTerminalAccessDTO;
|
||||
import com.orion.visor.module.asset.entity.dto.HostTerminalConnectDTO;
|
||||
import com.orion.visor.module.asset.entity.dto.HostTerminalTransferDTO;
|
||||
import com.orion.visor.module.asset.entity.vo.HostTerminalThemeVO;
|
||||
|
||||
import java.util.List;
|
||||
@@ -30,6 +31,13 @@ public interface HostTerminalService {
|
||||
*/
|
||||
String getTerminalAccessToken();
|
||||
|
||||
/**
|
||||
* 获取主机终端传输 transferToken
|
||||
*
|
||||
* @return transferToken
|
||||
*/
|
||||
String getTerminalTransferToken();
|
||||
|
||||
/**
|
||||
* 通过 accessToken 获取主机终端访问信息
|
||||
*
|
||||
@@ -38,6 +46,14 @@ public interface HostTerminalService {
|
||||
*/
|
||||
HostTerminalAccessDTO getAccessInfoByToken(String token);
|
||||
|
||||
/**
|
||||
* 通过 transferToken 获取主机终端传输信息
|
||||
*
|
||||
* @param token token
|
||||
* @return config
|
||||
*/
|
||||
HostTerminalTransferDTO getTransferInfoByToken(String token);
|
||||
|
||||
/**
|
||||
* 获取连接信息
|
||||
*
|
||||
|
||||
@@ -17,6 +17,7 @@ import com.orion.visor.module.asset.entity.domain.HostIdentityDO;
|
||||
import com.orion.visor.module.asset.entity.domain.HostKeyDO;
|
||||
import com.orion.visor.module.asset.entity.dto.HostTerminalAccessDTO;
|
||||
import com.orion.visor.module.asset.entity.dto.HostTerminalConnectDTO;
|
||||
import com.orion.visor.module.asset.entity.dto.HostTerminalTransferDTO;
|
||||
import com.orion.visor.module.asset.entity.vo.HostTerminalThemeVO;
|
||||
import com.orion.visor.module.asset.enums.*;
|
||||
import com.orion.visor.module.asset.handler.host.config.model.HostSshConfigModel;
|
||||
@@ -74,6 +75,10 @@ public class HostTerminalServiceImpl implements HostTerminalService {
|
||||
|
||||
@Override
|
||||
public List<HostTerminalThemeVO> getTerminalThemes() {
|
||||
// if (true) {
|
||||
// String arr = "";
|
||||
// return JSON.parseArray(arr, HostTerminalThemeVO.class);
|
||||
// }
|
||||
List<JSONObject> themes = dictValueApi.getDictValue(THEME_DICT_KEY);
|
||||
return themes.stream()
|
||||
.map(s -> HostTerminalThemeVO.builder()
|
||||
@@ -99,6 +104,21 @@ public class HostTerminalServiceImpl implements HostTerminalService {
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTerminalTransferToken() {
|
||||
LoginUser user = Valid.notNull(SecurityUtils.getLoginUser());
|
||||
log.info("HostConnectService.getTerminalTransferToken userId: {}", user.getId());
|
||||
String transferToken = UUIds.random19();
|
||||
HostTerminalTransferDTO transfer = HostTerminalTransferDTO.builder()
|
||||
.userId(user.getId())
|
||||
.username(user.getUsername())
|
||||
.build();
|
||||
// 设置 transfer 缓存
|
||||
String key = HostTerminalCacheKeyDefine.HOST_TERMINAL_TRANSFER.format(transferToken);
|
||||
RedisStrings.setJson(key, HostTerminalCacheKeyDefine.HOST_TERMINAL_TRANSFER, transfer);
|
||||
return transferToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HostTerminalAccessDTO getAccessInfoByToken(String token) {
|
||||
// 获取缓存
|
||||
@@ -111,6 +131,18 @@ public class HostTerminalServiceImpl implements HostTerminalService {
|
||||
return access;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HostTerminalTransferDTO getTransferInfoByToken(String token) {
|
||||
// 获取缓存
|
||||
String key = HostTerminalCacheKeyDefine.HOST_TERMINAL_TRANSFER.format(token);
|
||||
HostTerminalTransferDTO transfer = RedisStrings.getJson(key, HostTerminalCacheKeyDefine.HOST_TERMINAL_TRANSFER);
|
||||
// 删除缓存
|
||||
if (transfer != null) {
|
||||
RedisStrings.delete(key);
|
||||
}
|
||||
return transfer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HostTerminalConnectDTO getTerminalConnectInfo(Long hostId) {
|
||||
log.info("HostConnectService.getTerminalConnectInfo-withHost hostId: {}", hostId);
|
||||
|
||||
@@ -2,9 +2,7 @@ package com.orion.visor.module.asset.utils;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.orion.lang.utils.Booleans;
|
||||
import com.orion.lang.utils.Exceptions;
|
||||
import com.orion.lang.utils.Strings;
|
||||
import com.orion.lang.utils.io.Files1;
|
||||
import com.orion.net.host.sftp.SftpExecutor;
|
||||
import com.orion.net.host.sftp.SftpFile;
|
||||
import com.orion.visor.module.asset.define.config.AppSftpConfig;
|
||||
@@ -41,49 +39,8 @@ public class SftpUtils {
|
||||
SftpFileBackupParams backupParams = new SftpFileBackupParams(file.getName(), System.currentTimeMillis());
|
||||
String target = Strings.format(config.getBackupFileName(), JSON.parseObject(JSON.toJSONString(backupParams)));
|
||||
// 移动
|
||||
// FIXME kit
|
||||
move(executor, path, target);
|
||||
// executor.move(path, target);
|
||||
executor.move(path, target);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移动文件
|
||||
* FIXME kit DELETE
|
||||
*
|
||||
* @param executor executor
|
||||
* @param source source
|
||||
* @param target target
|
||||
*/
|
||||
public static void move(SftpExecutor executor, String source, String target) {
|
||||
try {
|
||||
source = Files1.getPath(source);
|
||||
target = Files1.getPath(target);
|
||||
if (target.charAt(0) == '/') {
|
||||
// 检查是否需要创建目标文件目录
|
||||
if (!isSameParentPath(source, target)) {
|
||||
executor.makeDirectories(Files1.getParentPath(target));
|
||||
}
|
||||
// 绝对路径
|
||||
executor.getChannel().rename(source, Files1.getPath(Files1.normalize(target)));
|
||||
} else {
|
||||
// 相对路径
|
||||
executor.getChannel().rename(source, Files1.getPath(Files1.normalize(Files1.getPath(source + "/../" + target))));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw Exceptions.sftp(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* FIXME kit DELETE
|
||||
*
|
||||
* @param source source
|
||||
* @param target target
|
||||
* @return res
|
||||
*/
|
||||
private static boolean isSameParentPath(String source, String target) {
|
||||
return Files1.getParentPath(source).equals(Files1.getParentPath(target));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
<result column="user_id" property="userId"/>
|
||||
<result column="group_id" property="groupId"/>
|
||||
<result column="name" property="name"/>
|
||||
<result column="prefix" property="prefix"/>
|
||||
<result column="command" property="command"/>
|
||||
<result column="create_time" property="createTime"/>
|
||||
<result column="update_time" property="updateTime"/>
|
||||
@@ -19,7 +18,7 @@
|
||||
|
||||
<!-- 通用查询结果列 -->
|
||||
<sql id="Base_Column_List">
|
||||
id, user_id, group_id, name, prefix, command, create_time, update_time, creator, updater, deleted
|
||||
id, user_id, group_id, name, command, create_time, update_time, creator, updater, deleted
|
||||
</sql>
|
||||
|
||||
</mapper>
|
||||
|
||||
@@ -28,13 +28,15 @@ public class TerminalThemeGenerator {
|
||||
List<File> files = Files1.listFiles("D:\\idea-project\\iTerm2-Color-Schemes\\vhs");
|
||||
// 过滤的 theme
|
||||
List<String> schemaFilter = Lists.of(
|
||||
"Dracula", "Atom",
|
||||
"catppuccin-mocha", "MaterialDesignColors",
|
||||
"catppuccin-macchiato", "OneHalfDark",
|
||||
"Apple System Colors", "Builtin Tango Light",
|
||||
"Duotone Dark", "BlulocoLight",
|
||||
"Chester", "CLRS",
|
||||
"Calamity", "Tomorrow"
|
||||
"Dracula", "Builtin Tango Light",
|
||||
"Atom", "AtomOneLight",
|
||||
"OneHalfDark", "OneHalfLight",
|
||||
"Apple System Colors", "Tomorrow",
|
||||
"catppuccin-mocha", "catppuccin-latte",
|
||||
"catppuccin-macchiato", "BlulocoLight",
|
||||
"catppuccin-frappe", "MaterialDesignColors",
|
||||
"GitHub Dark", "Github",
|
||||
"DimmedMonokai", "Duotone Dark"
|
||||
);
|
||||
// 颜色大写
|
||||
ValueFilter colorFilter = (Object object, String name, Object value) -> {
|
||||
@@ -60,7 +62,7 @@ public class TerminalThemeGenerator {
|
||||
theme.setDark(Colors.isDarkColor(background));
|
||||
theme.setSchema(JSON.parseObject(JSON.toJSONString(schema), TerminalThemeSchema.class));
|
||||
return theme;
|
||||
}).collect(Collectors.toList());
|
||||
}).skip(0).limit(50).collect(Collectors.toList());
|
||||
// 排序
|
||||
if (!Lists.isEmpty(schemaFilter)) {
|
||||
arr.sort(Comparator.comparing(s -> schemaFilter.indexOf(s.getName())));
|
||||
@@ -70,11 +72,12 @@ public class TerminalThemeGenerator {
|
||||
for (TerminalTheme theme : arr) {
|
||||
System.out.println("name: " + theme.name);
|
||||
System.out.println("dark: " + theme.dark);
|
||||
System.out.println("value: \n" + JSON.toJSONString(theme.schema, colorFilter));
|
||||
System.out.println("value: " + JSON.toJSONString(theme.schema, colorFilter));
|
||||
System.out.println("json: " + JSON.toJSONString(theme, colorFilter));
|
||||
System.out.println();
|
||||
}
|
||||
// String json = JSON.toJSONString(arr, colorFilter);
|
||||
// System.out.println("\n" + json);
|
||||
String json = JSON.toJSONString(arr, colorFilter);
|
||||
System.out.println("\n" + json);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.orion.visor.module.infra.api;
|
||||
|
||||
import com.orion.visor.module.infra.entity.dto.user.SystemUserAuthDTO;
|
||||
|
||||
/**
|
||||
* 认证服务实现
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/8/14 21:37
|
||||
*/
|
||||
public interface AuthenticationApi {
|
||||
|
||||
/**
|
||||
* 通过密码认证
|
||||
*
|
||||
* @param username username
|
||||
* @param password password
|
||||
* @param addFailedCount addFailedCount
|
||||
* @return result
|
||||
*/
|
||||
SystemUserAuthDTO authByPassword(String username, String password, boolean addFailedCount);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.orion.visor.module.infra.api;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 权限 对外服务类
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/8/19 15:22
|
||||
*/
|
||||
public interface PermissionApi {
|
||||
|
||||
/**
|
||||
* 用户是否为管理员用户
|
||||
*
|
||||
* @param id id
|
||||
* @return isAdmin
|
||||
*/
|
||||
boolean isAdminUser(Long id);
|
||||
|
||||
/**
|
||||
* 检查当前用户是否含有此角色
|
||||
*
|
||||
* @param userId userId
|
||||
* @param role role
|
||||
* @return 是否包含
|
||||
*/
|
||||
boolean hasRole(Long userId, String role);
|
||||
|
||||
/**
|
||||
* 检查当前用户是否含有任意角色
|
||||
*
|
||||
* @param userId userId
|
||||
* @param roles roles
|
||||
* @return 是否包含
|
||||
*/
|
||||
boolean hasAnyRole(Long userId, List<String> roles);
|
||||
|
||||
/**
|
||||
* 检查当前用户是否含有此权限
|
||||
*
|
||||
* @param userId userId
|
||||
* @param permission permission
|
||||
* @return 是否包含
|
||||
*/
|
||||
boolean hasPermission(Long userId, String permission);
|
||||
|
||||
/**
|
||||
* 检查当前用户是否含任意权限
|
||||
*
|
||||
* @param userId userId
|
||||
* @param permissions permissions
|
||||
* @return 是否包含
|
||||
*/
|
||||
boolean hasAnyPermission(Long userId, List<String> permissions);
|
||||
|
||||
}
|
||||
@@ -11,6 +11,22 @@ import com.orion.visor.module.infra.entity.dto.user.SystemUserDTO;
|
||||
*/
|
||||
public interface SystemUserApi {
|
||||
|
||||
/**
|
||||
* 通过 id 查询用户名
|
||||
*
|
||||
* @param id id
|
||||
* @return username
|
||||
*/
|
||||
String getUsernameById(Long id);
|
||||
|
||||
/**
|
||||
* 通过 id 查询花名
|
||||
*
|
||||
* @param id id
|
||||
* @return nickname
|
||||
*/
|
||||
String getNicknameById(Long id);
|
||||
|
||||
/**
|
||||
* 通过 id 查询用户
|
||||
*
|
||||
@@ -19,12 +35,4 @@ public interface SystemUserApi {
|
||||
*/
|
||||
SystemUserDTO getUserById(Long id);
|
||||
|
||||
/**
|
||||
* 用户是否为管理员用户
|
||||
*
|
||||
* @param id id
|
||||
* @return isAdmin
|
||||
*/
|
||||
boolean isAdminUser(Long id);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.orion.visor.module.infra.entity.dto.user;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 用户状态检查 业务对象
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/8/14 21:52
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(name = "SystemUserAuthDTO", description = "用户认证 业务对象")
|
||||
public class SystemUserAuthDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "id")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "用户名")
|
||||
private String username;
|
||||
|
||||
@Schema(description = "花名")
|
||||
private String nickname;
|
||||
|
||||
@Schema(description = "密码是否正确")
|
||||
private Boolean passRight;
|
||||
|
||||
@Schema(description = "认证是否通过")
|
||||
private Boolean authed;
|
||||
|
||||
@Schema(description = "错误信息")
|
||||
private String errorMessage;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.orion.visor.module.infra.api.impl;
|
||||
|
||||
import com.orion.visor.framework.common.constant.ErrorMessage;
|
||||
import com.orion.visor.framework.common.utils.Valid;
|
||||
import com.orion.visor.module.infra.api.AuthenticationApi;
|
||||
import com.orion.visor.module.infra.entity.domain.SystemUserDO;
|
||||
import com.orion.visor.module.infra.entity.dto.user.SystemUserAuthDTO;
|
||||
import com.orion.visor.module.infra.service.AuthenticationService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 认证服务实现
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/8/14 21:37
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class AuthenticationApiImpl implements AuthenticationApi {
|
||||
|
||||
@Resource
|
||||
private AuthenticationService authenticationService;
|
||||
|
||||
@Override
|
||||
public SystemUserAuthDTO authByPassword(String username, String password, boolean addFailedCount) {
|
||||
SystemUserAuthDTO result = new SystemUserAuthDTO();
|
||||
try {
|
||||
// 登录预检
|
||||
SystemUserDO user = authenticationService.preCheckLogin(username, password);
|
||||
result.setId(user.getId());
|
||||
result.setUsername(user.getUsername());
|
||||
result.setNickname(user.getNickname());
|
||||
// 检查用户密码
|
||||
boolean passRight = authenticationService.checkUserPassword(user, password, addFailedCount);
|
||||
result.setPassRight(passRight);
|
||||
Valid.isTrue(passRight, ErrorMessage.USERNAME_PASSWORD_ERROR);
|
||||
// 检查用户状态
|
||||
authenticationService.checkUserStatus(user);
|
||||
result.setAuthed(true);
|
||||
} catch (Exception e) {
|
||||
result.setAuthed(false);
|
||||
result.setErrorMessage(e.getMessage());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.orion.visor.module.infra.api.impl;
|
||||
|
||||
import com.orion.visor.module.infra.api.PermissionApi;
|
||||
import com.orion.visor.module.infra.service.PermissionService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 权限 对外服务类实现
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/8/19 15:25
|
||||
*/
|
||||
@Service
|
||||
public class PermissionApiImpl implements PermissionApi {
|
||||
|
||||
@Resource
|
||||
private PermissionService permissionService;
|
||||
|
||||
@Override
|
||||
public boolean isAdminUser(Long id) {
|
||||
return permissionService.isAdminUser(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasRole(Long userId, String role) {
|
||||
return permissionService.hasRole(userId, role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAnyRole(Long userId, List<String> roles) {
|
||||
return permissionService.hasAnyRole(userId, roles);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(Long userId, String permission) {
|
||||
return permissionService.hasPermission(userId, permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAnyPermission(Long userId, List<String> permissions) {
|
||||
return permissionService.hasAnyPermission(userId, permissions);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import com.orion.visor.module.infra.convert.SystemUserProviderConvert;
|
||||
import com.orion.visor.module.infra.dao.SystemUserDAO;
|
||||
import com.orion.visor.module.infra.entity.domain.SystemUserDO;
|
||||
import com.orion.visor.module.infra.entity.dto.user.SystemUserDTO;
|
||||
import com.orion.visor.module.infra.service.SystemUserService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
@@ -23,8 +22,25 @@ public class SystemUserApiImpl implements SystemUserApi {
|
||||
@Resource
|
||||
private SystemUserDAO systemUserDAO;
|
||||
|
||||
@Resource
|
||||
private SystemUserService systemUserService;
|
||||
@Override
|
||||
public String getUsernameById(Long id) {
|
||||
return systemUserDAO.of()
|
||||
.createWrapper()
|
||||
.select(SystemUserDO::getUsername)
|
||||
.eq(SystemUserDO::getId, id)
|
||||
.then()
|
||||
.getOne(SystemUserDO::getUsername);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNicknameById(Long id) {
|
||||
return systemUserDAO.of()
|
||||
.createWrapper()
|
||||
.select(SystemUserDO::getNickname)
|
||||
.eq(SystemUserDO::getId, id)
|
||||
.then()
|
||||
.getOne(SystemUserDO::getNickname);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SystemUserDTO getUserById(Long id) {
|
||||
@@ -35,9 +51,4 @@ public class SystemUserApiImpl implements SystemUserApi {
|
||||
return SystemUserProviderConvert.MAPPER.to(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAdminUser(Long id) {
|
||||
return systemUserService.isAdminUser(id);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.orion.visor.module.infra.controller;
|
||||
|
||||
import com.orion.visor.framework.common.validator.group.Page;
|
||||
import com.orion.visor.framework.log.core.annotation.IgnoreLog;
|
||||
import com.orion.visor.framework.log.core.enums.IgnoreLogMode;
|
||||
import com.orion.visor.framework.web.core.annotation.RestWrapper;
|
||||
@@ -38,7 +39,7 @@ public class SystemMessageController {
|
||||
@IgnoreLog(IgnoreLogMode.ALL)
|
||||
@PostMapping("/list")
|
||||
@Operation(summary = "查询系统消息列表")
|
||||
public List<SystemMessageVO> getSystemMessageList(@RequestBody SystemMessageQueryRequest request) {
|
||||
public List<SystemMessageVO> getSystemMessageList(@Validated(Page.class) @RequestBody SystemMessageQueryRequest request) {
|
||||
return systemMessageService.getSystemMessageList(request);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import com.orion.visor.framework.log.core.enums.IgnoreLogMode;
|
||||
import com.orion.visor.framework.web.core.annotation.RestWrapper;
|
||||
import com.orion.visor.module.infra.entity.vo.SystemMenuVO;
|
||||
import com.orion.visor.module.infra.entity.vo.UserPermissionVO;
|
||||
import com.orion.visor.module.infra.service.PermissionService;
|
||||
import com.orion.visor.module.infra.service.UserPermissionService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -26,22 +26,23 @@ import java.util.List;
|
||||
* @version 1.0.0
|
||||
* @since 2023/7/14 11:20
|
||||
*/
|
||||
@Tag(name = "infra - 权限服务")
|
||||
@Tag(name = "infra - 用户权限服务")
|
||||
@Slf4j
|
||||
@Validated
|
||||
@RestWrapper
|
||||
@RestController
|
||||
@RequestMapping("/infra/permission")
|
||||
public class PermissionController {
|
||||
@RequestMapping("/infra/user-permission")
|
||||
@SuppressWarnings({"ELValidationInJSP", "SpringElInspection"})
|
||||
public class UserPermissionController {
|
||||
|
||||
@Resource
|
||||
private PermissionService permissionService;
|
||||
private UserPermissionService userPermissionService;
|
||||
|
||||
@PutMapping("/refresh-cache")
|
||||
@Operation(summary = "刷新角色权限缓存")
|
||||
@PreAuthorize("@ss.hasPermission('infra:system-menu:management:refresh-cache')")
|
||||
public Boolean refreshCache() {
|
||||
permissionService.initPermissionCache();
|
||||
userPermissionService.initPermissionCache();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -49,14 +50,14 @@ public class PermissionController {
|
||||
@GetMapping("/menu")
|
||||
@Operation(summary = "获取用户菜单")
|
||||
public List<SystemMenuVO> getUserMenuList() {
|
||||
return permissionService.getUserMenuList();
|
||||
return userPermissionService.getUserMenuList();
|
||||
}
|
||||
|
||||
@IgnoreLog(IgnoreLogMode.RET)
|
||||
@GetMapping("/user")
|
||||
@Operation(summary = "获取用户权限聚合信息")
|
||||
public UserPermissionVO getUserPermission() {
|
||||
return permissionService.getUserPermission();
|
||||
return userPermissionService.getUserPermission();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -34,11 +34,22 @@ public interface SystemRoleDAO extends IMapper<SystemRoleDO> {
|
||||
/**
|
||||
* 通过 userId 和 roleCode 查询 roleId (检查用户是否包含某个角色)
|
||||
*
|
||||
* @param userId userId
|
||||
* @param code code
|
||||
* @param userId userId
|
||||
* @param codeList codeList
|
||||
* @return roleId
|
||||
*/
|
||||
Long getRoleIdByUserIdAndRoleCode(@Param("userId") Long userId, @Param("code") String code);
|
||||
List<Long> getRoleIdByUserIdAndRoleCode(@Param("userId") Long userId,
|
||||
@Param("codeList") List<String> codeList);
|
||||
|
||||
/**
|
||||
* 通过 roleId 和 permission 查询 permission (检查角色是否包含某个权限)
|
||||
*
|
||||
* @param roleIdList roleIdList
|
||||
* @param permissionList permissionList
|
||||
* @return permission
|
||||
*/
|
||||
List<String> getPermissionByRoleIdAndPermission(@Param("roleIdList") List<Long> roleIdList,
|
||||
@Param("permissionList") List<String> permissionList);
|
||||
|
||||
/**
|
||||
* 查询用户角色
|
||||
|
||||
@@ -31,6 +31,11 @@ public class AppAuthenticationConfig {
|
||||
*/
|
||||
private Integer maxRefreshCount;
|
||||
|
||||
/**
|
||||
* 登录失败发送站内信阈值
|
||||
*/
|
||||
private Integer loginFailedSendThreshold;
|
||||
|
||||
/**
|
||||
* 登录失败锁定次数
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.orion.visor.module.infra.define.message;
|
||||
|
||||
import com.orion.visor.module.infra.define.SystemMessageDefine;
|
||||
import com.orion.visor.module.infra.enums.MessageClassifyEnum;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 用户 系统消息定义
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/5/14 17:23
|
||||
*/
|
||||
@Getter
|
||||
public enum SystemUserMessageDefine implements SystemMessageDefine {
|
||||
|
||||
/**
|
||||
* 登录失败
|
||||
*/
|
||||
LOGIN_FAILED(MessageClassifyEnum.NOTICE,
|
||||
"登录失败",
|
||||
"您的账号在 <sb>${time}</sb> 登录系统时身份认证失败, 您的密码可能已经泄漏。如非本人操作请尽快修改密码。(<sb>${address} - ${location}</sb>)"),
|
||||
|
||||
;
|
||||
|
||||
SystemUserMessageDefine(MessageClassifyEnum classify, String title, String content) {
|
||||
this.classify = classify;
|
||||
this.type = this.name();
|
||||
this.title = title;
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息分类
|
||||
*/
|
||||
private final MessageClassifyEnum classify;
|
||||
|
||||
/**
|
||||
* 消息类型
|
||||
*/
|
||||
private final String type;
|
||||
|
||||
/**
|
||||
* 标题
|
||||
*/
|
||||
private final String title;
|
||||
|
||||
/**
|
||||
* 内容
|
||||
*/
|
||||
private final String content;
|
||||
|
||||
}
|
||||
@@ -1,10 +1,8 @@
|
||||
package com.orion.visor.module.infra.entity.request.message;
|
||||
|
||||
import com.orion.visor.framework.common.entity.PageRequest;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* 系统消息 查询请求对象
|
||||
@@ -17,11 +15,9 @@ import lombok.NoArgsConstructor;
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(name = "SystemMessageQueryRequest", description = "系统消息 查询请求对象")
|
||||
public class SystemMessageQueryRequest {
|
||||
|
||||
@Schema(description = "大小")
|
||||
private Integer limit;
|
||||
public class SystemMessageQueryRequest extends PageRequest {
|
||||
|
||||
@Schema(description = "maxId")
|
||||
private Long maxId;
|
||||
|
||||
@@ -8,7 +8,7 @@ import com.orion.visor.module.infra.entity.dto.LoginTokenDTO;
|
||||
import com.orion.visor.module.infra.enums.LoginTokenStatusEnum;
|
||||
import com.orion.visor.module.infra.enums.UserStatusEnum;
|
||||
import com.orion.visor.module.infra.service.AuthenticationService;
|
||||
import com.orion.visor.module.infra.service.PermissionService;
|
||||
import com.orion.visor.module.infra.service.UserPermissionService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
@@ -27,30 +27,30 @@ public class SecurityFrameworkServiceImpl implements SecurityFrameworkService {
|
||||
private AuthenticationService authenticationService;
|
||||
|
||||
@Resource
|
||||
private PermissionService permissionService;
|
||||
private UserPermissionService userPermissionService;
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
// 检查是否有权限
|
||||
return permissionService.hasPermission(permission);
|
||||
return userPermissionService.hasPermission(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAnyPermission(String... permissions) {
|
||||
// 检查是否有权限
|
||||
return permissionService.hasAnyPermission(permissions);
|
||||
return userPermissionService.hasAnyPermission(permissions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasRole(String role) {
|
||||
// 检查是否有角色
|
||||
return permissionService.hasRole(role);
|
||||
return userPermissionService.hasRole(role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAnyRole(String... roles) {
|
||||
// 检查是否有角色
|
||||
return permissionService.hasAnyRole(roles);
|
||||
return userPermissionService.hasAnyRole(roles);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -314,6 +314,11 @@ public class TerminalPreferenceModel implements GenericsDataModel {
|
||||
*/
|
||||
private Boolean openSftp;
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
*/
|
||||
private Boolean uploadFile;
|
||||
|
||||
/**
|
||||
* 清空
|
||||
*/
|
||||
|
||||
@@ -91,6 +91,7 @@ public class TerminalPreferenceStrategy extends AbstractGenericsDataStrategy<Ter
|
||||
new TerminalPreferenceModel.ShortcutKeysModel("toBottom", true, true, false, "ArrowDown", true),
|
||||
new TerminalPreferenceModel.ShortcutKeysModel("selectAll", true, true, false, "KeyA", true),
|
||||
new TerminalPreferenceModel.ShortcutKeysModel("search", true, true, false, "KeyF", true),
|
||||
new TerminalPreferenceModel.ShortcutKeysModel("uploadFile", true, true, false, "KeyU", true),
|
||||
new TerminalPreferenceModel.ShortcutKeysModel("commandEditor", true, false, true, "KeyE", true),
|
||||
new TerminalPreferenceModel.ShortcutKeysModel("fontSizePlus", true, false, true, "Equal", true),
|
||||
new TerminalPreferenceModel.ShortcutKeysModel("fontSizeSubtract", true, false, true, "Minus", true)
|
||||
@@ -101,8 +102,8 @@ public class TerminalPreferenceStrategy extends AbstractGenericsDataStrategy<Ter
|
||||
String actionBarSetting = TerminalPreferenceModel.ActionBarSettingModel.builder()
|
||||
.commandInput(false)
|
||||
.connectStatus(true)
|
||||
.toTop(true)
|
||||
.toBottom(true)
|
||||
.toTop(false)
|
||||
.toBottom(false)
|
||||
.selectAll(false)
|
||||
.search(true)
|
||||
.copy(true)
|
||||
@@ -112,7 +113,10 @@ public class TerminalPreferenceStrategy extends AbstractGenericsDataStrategy<Ter
|
||||
.fontSizePlus(false)
|
||||
.fontSizeSubtract(false)
|
||||
.commandEditor(true)
|
||||
.fontSizePlus(false)
|
||||
.fontSizeSubtract(false)
|
||||
.openSftp(true)
|
||||
.uploadFile(true)
|
||||
.clear(true)
|
||||
.disconnect(false)
|
||||
.build()
|
||||
@@ -123,7 +127,7 @@ public class TerminalPreferenceStrategy extends AbstractGenericsDataStrategy<Ter
|
||||
.theme(new JSONObject())
|
||||
.displaySetting(JSONObject.parseObject(defaultDisplaySetting))
|
||||
.actionBarSetting(JSONObject.parseObject(actionBarSetting))
|
||||
.rightMenuSetting(Lists.of("selectAll", "copy", "paste", "fontSizePlus", "fontSizeSubtract", "search", "clear"))
|
||||
.rightMenuSetting(Lists.of("selectAll", "copy", "paste", "search", "clear"))
|
||||
.interactSetting(JSONObject.parseObject(defaultInteractSetting))
|
||||
.pluginsSetting(JSONObject.parseObject(defaultPluginsSetting))
|
||||
.sessionSetting(JSONObject.parseObject(defaultSessionSetting))
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.orion.visor.module.infra.service;
|
||||
|
||||
import com.orion.visor.framework.common.security.LoginUser;
|
||||
import com.orion.visor.module.infra.entity.domain.SystemUserDO;
|
||||
import com.orion.visor.module.infra.entity.dto.LoginTokenDTO;
|
||||
import com.orion.visor.module.infra.entity.request.user.UserLoginRequest;
|
||||
import com.orion.visor.module.infra.entity.vo.UserLoginVO;
|
||||
@@ -48,4 +49,30 @@ public interface AuthenticationService {
|
||||
*/
|
||||
LoginTokenDTO getLoginTokenInfo(String loginToken);
|
||||
|
||||
/**
|
||||
* 登录预检查
|
||||
*
|
||||
* @param username username
|
||||
* @param password password
|
||||
* @return user
|
||||
*/
|
||||
SystemUserDO preCheckLogin(String username, String password);
|
||||
|
||||
/**
|
||||
* 检查用户密码
|
||||
*
|
||||
* @param user user
|
||||
* @param password password
|
||||
* @param addFailedCount addFailedCount
|
||||
* @return passRight
|
||||
*/
|
||||
boolean checkUserPassword(SystemUserDO user, String password, boolean addFailedCount);
|
||||
|
||||
/**
|
||||
* 检查用户状态
|
||||
*
|
||||
* @param user user
|
||||
*/
|
||||
void checkUserStatus(SystemUserDO user);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,92 +1,58 @@
|
||||
package com.orion.visor.module.infra.service;
|
||||
|
||||
import com.orion.visor.module.infra.entity.domain.SystemRoleDO;
|
||||
import com.orion.visor.module.infra.entity.dto.SystemMenuCacheDTO;
|
||||
import com.orion.visor.module.infra.entity.vo.SystemMenuVO;
|
||||
import com.orion.visor.module.infra.entity.vo.UserPermissionVO;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 权限服务
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/7/16 1:03
|
||||
* @since 2024/8/19 15:29
|
||||
*/
|
||||
public interface PermissionService {
|
||||
|
||||
/**
|
||||
* 获取 角色缓存
|
||||
* 检测用户是否是为管理员
|
||||
*
|
||||
* @return cache
|
||||
* @param userId userId
|
||||
* @return 是否为管理员
|
||||
*/
|
||||
Map<Long, SystemRoleDO> getRoleCache();
|
||||
boolean isAdminUser(Long userId);
|
||||
|
||||
/**
|
||||
* 获取 菜单缓存 以作角色权限直接引用
|
||||
* 检查当前用户是否含有此角色
|
||||
*
|
||||
* @return cache
|
||||
*/
|
||||
List<SystemMenuCacheDTO> getMenuCache();
|
||||
|
||||
/**
|
||||
* 获取 角色菜单关联
|
||||
*
|
||||
* @return cache
|
||||
*/
|
||||
Map<Long, List<SystemMenuCacheDTO>> getRoleMenuCache();
|
||||
|
||||
/**
|
||||
* 初始化权限缓存
|
||||
*/
|
||||
void initPermissionCache();
|
||||
|
||||
/**
|
||||
* 检查当前用户是否含有此角色 (有效性判断)
|
||||
*
|
||||
* @param role role
|
||||
* @param userId userId
|
||||
* @param role role
|
||||
* @return 是否包含
|
||||
*/
|
||||
boolean hasRole(String role);
|
||||
boolean hasRole(Long userId, String role);
|
||||
|
||||
/**
|
||||
* 检查当前用户是否含有任意角色 (有效性判断)
|
||||
* 检查当前用户是否含有任意角色
|
||||
*
|
||||
* @param roles roles
|
||||
* @param userId userId
|
||||
* @param roles roles
|
||||
* @return 是否包含
|
||||
*/
|
||||
boolean hasAnyRole(String... roles);
|
||||
boolean hasAnyRole(Long userId, List<String> roles);
|
||||
|
||||
/**
|
||||
* 检查当前用户是否含有此权限 (有效性判断)
|
||||
* 检查当前用户是否含有此权限
|
||||
*
|
||||
* @param userId userId
|
||||
* @param permission permission
|
||||
* @return 是否包含
|
||||
*/
|
||||
boolean hasPermission(String permission);
|
||||
boolean hasPermission(Long userId, String permission);
|
||||
|
||||
/**
|
||||
* 检查当前用户是否含任意权限 (有效性判断)
|
||||
* 检查当前用户是否含任意权限
|
||||
*
|
||||
* @param userId userId
|
||||
* @param permissions permissions
|
||||
* @return 是否包含
|
||||
*/
|
||||
boolean hasAnyPermission(String... permissions);
|
||||
|
||||
/**
|
||||
* 获取用户菜单
|
||||
*
|
||||
* @return 菜单
|
||||
*/
|
||||
List<SystemMenuVO> getUserMenuList();
|
||||
|
||||
/**
|
||||
* 获取用户权限
|
||||
*
|
||||
* @return 权限信息
|
||||
*/
|
||||
UserPermissionVO getUserPermission();
|
||||
boolean hasAnyPermission(Long userId, List<String> permissions);
|
||||
|
||||
}
|
||||
|
||||
@@ -92,12 +92,4 @@ public interface SystemUserService {
|
||||
*/
|
||||
void resetPassword(UserResetPasswordRequest request);
|
||||
|
||||
/**
|
||||
* 检测用户是否是为管理员
|
||||
*
|
||||
* @param userId userId
|
||||
* @return 是否为管理员
|
||||
*/
|
||||
boolean isAdminUser(Long userId);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.orion.visor.module.infra.service;
|
||||
|
||||
import com.orion.visor.module.infra.entity.domain.SystemRoleDO;
|
||||
import com.orion.visor.module.infra.entity.dto.SystemMenuCacheDTO;
|
||||
import com.orion.visor.module.infra.entity.vo.SystemMenuVO;
|
||||
import com.orion.visor.module.infra.entity.vo.UserPermissionVO;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 用户权限服务
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/7/16 1:03
|
||||
*/
|
||||
public interface UserPermissionService {
|
||||
|
||||
/**
|
||||
* 获取 角色缓存
|
||||
*
|
||||
* @return cache
|
||||
*/
|
||||
Map<Long, SystemRoleDO> getRoleCache();
|
||||
|
||||
/**
|
||||
* 获取 菜单缓存 以作角色权限直接引用
|
||||
*
|
||||
* @return cache
|
||||
*/
|
||||
List<SystemMenuCacheDTO> getMenuCache();
|
||||
|
||||
/**
|
||||
* 获取 角色菜单关联
|
||||
*
|
||||
* @return cache
|
||||
*/
|
||||
Map<Long, List<SystemMenuCacheDTO>> getRoleMenuCache();
|
||||
|
||||
/**
|
||||
* 初始化权限缓存
|
||||
*/
|
||||
void initPermissionCache();
|
||||
|
||||
/**
|
||||
* 检查当前用户是否含有此角色 (有效性判断)
|
||||
*
|
||||
* @param role role
|
||||
* @return 是否包含
|
||||
*/
|
||||
boolean hasRole(String role);
|
||||
|
||||
/**
|
||||
* 检查当前用户是否含有任意角色 (有效性判断)
|
||||
*
|
||||
* @param roles roles
|
||||
* @return 是否包含
|
||||
*/
|
||||
boolean hasAnyRole(String... roles);
|
||||
|
||||
/**
|
||||
* 检查当前用户是否含有此权限 (有效性判断)
|
||||
*
|
||||
* @param permission permission
|
||||
* @return 是否包含
|
||||
*/
|
||||
boolean hasPermission(String permission);
|
||||
|
||||
/**
|
||||
* 检查当前用户是否含任意权限 (有效性判断)
|
||||
*
|
||||
* @param permissions permissions
|
||||
* @return 是否包含
|
||||
*/
|
||||
boolean hasAnyPermission(String... permissions);
|
||||
|
||||
/**
|
||||
* 获取用户菜单
|
||||
*
|
||||
* @return 菜单
|
||||
*/
|
||||
List<SystemMenuVO> getUserMenuList();
|
||||
|
||||
/**
|
||||
* 获取用户权限
|
||||
*
|
||||
* @return 权限信息
|
||||
*/
|
||||
UserPermissionVO getUserPermission();
|
||||
|
||||
}
|
||||
@@ -1,14 +1,17 @@
|
||||
package com.orion.visor.module.infra.service.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.orion.lang.annotation.Keep;
|
||||
import com.orion.lang.define.wrapper.Pair;
|
||||
import com.orion.lang.utils.Exceptions;
|
||||
import com.orion.lang.utils.Strings;
|
||||
import com.orion.lang.utils.collect.Lists;
|
||||
import com.orion.lang.utils.crypto.Signatures;
|
||||
import com.orion.lang.utils.time.Dates;
|
||||
import com.orion.visor.framework.biz.operator.log.core.utils.OperatorLogs;
|
||||
import com.orion.visor.framework.common.annotation.Keep;
|
||||
import com.orion.visor.framework.common.constant.Const;
|
||||
import com.orion.visor.framework.common.constant.ErrorMessage;
|
||||
import com.orion.visor.framework.common.constant.ExtraFieldConst;
|
||||
import com.orion.visor.framework.common.security.LoginUser;
|
||||
import com.orion.visor.framework.common.security.UserRole;
|
||||
import com.orion.visor.framework.common.utils.CryptoUtils;
|
||||
@@ -17,30 +20,30 @@ import com.orion.visor.framework.common.utils.Valid;
|
||||
import com.orion.visor.framework.redis.core.utils.RedisStrings;
|
||||
import com.orion.visor.framework.redis.core.utils.RedisUtils;
|
||||
import com.orion.visor.framework.security.core.utils.SecurityUtils;
|
||||
import com.orion.visor.module.infra.api.SystemMessageApi;
|
||||
import com.orion.visor.module.infra.convert.SystemUserConvert;
|
||||
import com.orion.visor.module.infra.dao.SystemUserDAO;
|
||||
import com.orion.visor.module.infra.dao.SystemUserRoleDAO;
|
||||
import com.orion.visor.module.infra.define.cache.UserCacheKeyDefine;
|
||||
import com.orion.visor.module.infra.define.config.AppAuthenticationConfig;
|
||||
import com.orion.visor.module.infra.define.message.SystemUserMessageDefine;
|
||||
import com.orion.visor.module.infra.entity.domain.SystemUserDO;
|
||||
import com.orion.visor.module.infra.entity.dto.LoginTokenDTO;
|
||||
import com.orion.visor.module.infra.entity.dto.LoginTokenIdentityDTO;
|
||||
import com.orion.visor.module.infra.entity.dto.message.SystemMessageDTO;
|
||||
import com.orion.visor.module.infra.entity.request.user.UserLoginRequest;
|
||||
import com.orion.visor.module.infra.entity.vo.UserLoginVO;
|
||||
import com.orion.visor.module.infra.enums.LoginTokenStatusEnum;
|
||||
import com.orion.visor.module.infra.enums.UserStatusEnum;
|
||||
import com.orion.visor.module.infra.service.AuthenticationService;
|
||||
import com.orion.visor.module.infra.service.PermissionService;
|
||||
import com.orion.visor.module.infra.service.UserPermissionService;
|
||||
import com.orion.web.servlet.web.Servlets;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -64,7 +67,10 @@ public class AuthenticationServiceImpl implements AuthenticationService {
|
||||
private SystemUserRoleDAO systemUserRoleDAO;
|
||||
|
||||
@Resource
|
||||
private PermissionService permissionService;
|
||||
private UserPermissionService userPermissionService;
|
||||
|
||||
@Resource
|
||||
private SystemMessageApi systemMessageApi;
|
||||
|
||||
@Keep
|
||||
@Resource
|
||||
@@ -72,39 +78,35 @@ public class AuthenticationServiceImpl implements AuthenticationService {
|
||||
|
||||
@Override
|
||||
public UserLoginVO login(UserLoginRequest request, HttpServletRequest servletRequest) {
|
||||
// 设置日志上下文的用户 否则登录失败不会记录日志
|
||||
OperatorLogs.setUser(SystemUserConvert.MAPPER.toLoginUser(request));
|
||||
// 登录前检查
|
||||
this.preCheckLogin(request);
|
||||
// 获取登录用户
|
||||
SystemUserDO user = systemUserDAO.of()
|
||||
.createWrapper()
|
||||
.eq(SystemUserDO::getUsername, request.getUsername())
|
||||
.then()
|
||||
.getOne();
|
||||
Valid.notNull(user, ErrorMessage.USERNAME_PASSWORD_ERROR);
|
||||
// 重新设置日志上下文
|
||||
OperatorLogs.setUser(SystemUserConvert.MAPPER.toLoginUser(user));
|
||||
// 检查密码
|
||||
boolean passwordCorrect = this.checkPassword(request, user);
|
||||
Valid.isTrue(passwordCorrect, ErrorMessage.USERNAME_PASSWORD_ERROR);
|
||||
// 检查用户状态
|
||||
UserStatusEnum.checkUserStatus(user.getStatus());
|
||||
// 设置上次登录时间
|
||||
this.setLastLoginTime(user.getId());
|
||||
// 删除用户缓存
|
||||
this.deleteUserCache(user);
|
||||
// 重设用户缓存
|
||||
this.setUserCache(user);
|
||||
// 获取登录信息
|
||||
String remoteAddr = IpUtils.getRemoteAddr(servletRequest);
|
||||
String location = IpUtils.getLocation(remoteAddr);
|
||||
String userAgent = Servlets.getUserAgent(servletRequest);
|
||||
// 设置日志上下文的用户 否则登录失败不会记录日志
|
||||
OperatorLogs.setUser(SystemUserConvert.MAPPER.toLoginUser(request));
|
||||
// 登录前检查
|
||||
SystemUserDO user = this.preCheckLogin(request.getUsername(), request.getPassword());
|
||||
// 重新设置日志上下文
|
||||
OperatorLogs.setUser(SystemUserConvert.MAPPER.toLoginUser(user));
|
||||
// 用户密码校验
|
||||
boolean passRight = this.checkUserPassword(user, request.getPassword(), true);
|
||||
// 发送站内信
|
||||
this.sendLoginFailedErrorMessage(passRight, user, remoteAddr, location);
|
||||
Valid.isTrue(passRight, ErrorMessage.USERNAME_PASSWORD_ERROR);
|
||||
// 用户状态校验
|
||||
this.checkUserStatus(user);
|
||||
Long id = user.getId();
|
||||
// 设置上次登录时间
|
||||
this.setLastLoginTime(id);
|
||||
// 删除用户缓存
|
||||
this.deleteUserCache(user);
|
||||
// 重设用户缓存
|
||||
this.setUserCache(user);
|
||||
long current = System.currentTimeMillis();
|
||||
// 不允许多端登录
|
||||
if (!appAuthenticationConfig.getAllowMultiDevice()) {
|
||||
// 无效化其他缓存
|
||||
this.invalidOtherDeviceToken(user.getId(), current, remoteAddr, location, userAgent);
|
||||
this.invalidOtherDeviceToken(id, current, remoteAddr, location, userAgent);
|
||||
}
|
||||
// 生成 loginToken
|
||||
String token = this.generatorLoginToken(user, current, remoteAddr, location, userAgent);
|
||||
@@ -189,62 +191,83 @@ public class AuthenticationServiceImpl implements AuthenticationService {
|
||||
return refresh;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 token pair
|
||||
*
|
||||
* @param loginToken loginToken
|
||||
* @return pair
|
||||
*/
|
||||
private Pair<Long, Long> getLoginTokenPair(String loginToken) {
|
||||
if (loginToken == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
String value = CryptoUtils.decryptBase62(loginToken);
|
||||
String[] pair = value.split(":");
|
||||
return Pair.of(Long.valueOf(pair[0]), Long.valueOf(pair[1]));
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录预检查
|
||||
*
|
||||
* @param request request
|
||||
*/
|
||||
private void preCheckLogin(UserLoginRequest request) {
|
||||
@Override
|
||||
public SystemUserDO preCheckLogin(String username, String password) {
|
||||
// 检查密码长度是否正确 MD5 长度为 32
|
||||
if (request.getPassword().length() != Const.MD5_LEN) {
|
||||
if (password.length() != Const.MD5_LEN) {
|
||||
throw Exceptions.argument(ErrorMessage.USERNAME_PASSWORD_ERROR);
|
||||
}
|
||||
// 检查登录失败次数
|
||||
String failedCountKey = UserCacheKeyDefine.LOGIN_FAILED_COUNT.format(request.getUsername());
|
||||
String failedCountKey = UserCacheKeyDefine.LOGIN_FAILED_COUNT.format(username);
|
||||
String failedCount = redisTemplate.opsForValue().get(failedCountKey);
|
||||
if (failedCount != null
|
||||
&& Integer.parseInt(failedCount) >= appAuthenticationConfig.getLoginFailedLockCount()) {
|
||||
throw Exceptions.argument(ErrorMessage.MAX_LOGIN_FAILED);
|
||||
}
|
||||
// 获取登录用户
|
||||
SystemUserDO user = systemUserDAO.of()
|
||||
.createWrapper()
|
||||
.eq(SystemUserDO::getUsername, username)
|
||||
.then()
|
||||
.getOne();
|
||||
Valid.notNull(user, ErrorMessage.USERNAME_PASSWORD_ERROR);
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkUserPassword(SystemUserDO user, String password, boolean addFailedCount) {
|
||||
// 检查密码
|
||||
boolean passRight = user.getPassword().equals(Signatures.md5(password));
|
||||
if (!passRight && addFailedCount) {
|
||||
// 刷新登录失败缓存
|
||||
String failedCountKey = UserCacheKeyDefine.LOGIN_FAILED_COUNT.format(user.getUsername());
|
||||
redisTemplate.opsForValue().increment(failedCountKey);
|
||||
RedisUtils.setExpire(failedCountKey, appAuthenticationConfig.getLoginFailedLockTime(), TimeUnit.MINUTES);
|
||||
}
|
||||
return passRight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkUserStatus(SystemUserDO user) {
|
||||
// 检查用户状态
|
||||
UserStatusEnum.checkUserStatus(user.getStatus());
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查密码
|
||||
* 发送登录失败错误消息
|
||||
*
|
||||
* @param request request
|
||||
* @param user user
|
||||
* @return 是否正确
|
||||
* @param passRight passRight
|
||||
* @param user user
|
||||
* @param remoteAddr remoteAddr
|
||||
* @param location location
|
||||
*/
|
||||
@SuppressWarnings("ALL")
|
||||
private boolean checkPassword(UserLoginRequest request, SystemUserDO user) {
|
||||
// 密码正确
|
||||
if (user.getPassword().equals(Signatures.md5(request.getPassword()))) {
|
||||
return true;
|
||||
private void sendLoginFailedErrorMessage(boolean passRight, SystemUserDO user,
|
||||
String remoteAddr, String location) {
|
||||
if (passRight) {
|
||||
return;
|
||||
}
|
||||
// 刷新登录失败缓存
|
||||
String failedCountKey = UserCacheKeyDefine.LOGIN_FAILED_COUNT.format(request.getUsername());
|
||||
redisTemplate.opsForValue().increment(failedCountKey);
|
||||
RedisUtils.setExpire(failedCountKey, appAuthenticationConfig.getLoginFailedLockTime(), TimeUnit.MINUTES);
|
||||
return false;
|
||||
String failedCountKey = UserCacheKeyDefine.LOGIN_FAILED_COUNT.format(user.getUsername());
|
||||
String failedCountStr = redisTemplate.opsForValue().get(failedCountKey);
|
||||
if (failedCountStr == null || !Strings.isInteger(failedCountStr)) {
|
||||
return;
|
||||
}
|
||||
// 直接用相等 因为只触发一次
|
||||
if (!appAuthenticationConfig.getLoginFailedSendThreshold().equals(Integer.valueOf(failedCountStr))) {
|
||||
return;
|
||||
}
|
||||
// 发送站内信
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put(ExtraFieldConst.ADDRESS, remoteAddr);
|
||||
params.put(ExtraFieldConst.LOCATION, location);
|
||||
params.put(ExtraFieldConst.TIME, Dates.current());
|
||||
SystemMessageDTO message = SystemMessageDTO.builder()
|
||||
.receiverId(user.getId())
|
||||
.receiverUsername(user.getUsername())
|
||||
.relKey(user.getUsername())
|
||||
.params(params)
|
||||
.build();
|
||||
// 发送
|
||||
systemMessageApi.create(SystemUserMessageDefine.LOGIN_FAILED, message);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -273,6 +296,25 @@ public class AuthenticationServiceImpl implements AuthenticationService {
|
||||
redisTemplate.delete(Lists.of(userInfoKey, loginFailedCountKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 token pair
|
||||
*
|
||||
* @param loginToken loginToken
|
||||
* @return pair
|
||||
*/
|
||||
private Pair<Long, Long> getLoginTokenPair(String loginToken) {
|
||||
if (loginToken == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
String value = CryptoUtils.decryptBase62(loginToken);
|
||||
String[] pair = value.split(":");
|
||||
return Pair.of(Long.valueOf(pair[0]), Long.valueOf(pair[1]));
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置用户缓存
|
||||
*
|
||||
@@ -283,7 +325,7 @@ public class AuthenticationServiceImpl implements AuthenticationService {
|
||||
Long id = user.getId();
|
||||
// 查询用户角色
|
||||
List<Long> roleIds = systemUserRoleDAO.selectRoleIdByUserId(id);
|
||||
List<UserRole> roleList = permissionService.getRoleCache()
|
||||
List<UserRole> roleList = userPermissionService.getRoleCache()
|
||||
.values()
|
||||
.stream()
|
||||
.filter(s -> roleIds.contains(s.getId()))
|
||||
|
||||
@@ -85,6 +85,8 @@ public class DataPermissionServiceImpl implements DataPermissionService {
|
||||
.eq(DataPermissionDO::getRoleId, roleId)
|
||||
.eq(DataPermissionDO::getType, type);
|
||||
dataPermissionDAO.delete(wrapper);
|
||||
// 删除缓存
|
||||
this.deleteCache(type, userId, roleId);
|
||||
if (Lists.isEmpty(request.getRelIdList())) {
|
||||
return;
|
||||
}
|
||||
@@ -100,8 +102,6 @@ public class DataPermissionServiceImpl implements DataPermissionService {
|
||||
.build())
|
||||
.collect(Collectors.toList());
|
||||
dataPermissionDAO.insertBatch(records);
|
||||
// 删除缓存
|
||||
this.deleteCache(type, userId, roleId);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,305 +1,67 @@
|
||||
package com.orion.visor.module.infra.service.impl;
|
||||
|
||||
import com.orion.lang.utils.Arrays1;
|
||||
import com.orion.lang.utils.collect.Lists;
|
||||
import com.orion.lang.utils.collect.Maps;
|
||||
import com.orion.visor.framework.common.constant.Const;
|
||||
import com.orion.visor.framework.common.security.LoginUser;
|
||||
import com.orion.visor.framework.common.security.UserRole;
|
||||
import com.orion.visor.framework.security.core.utils.SecurityUtils;
|
||||
import com.orion.visor.module.infra.convert.SystemMenuConvert;
|
||||
import com.orion.visor.module.infra.convert.SystemUserConvert;
|
||||
import com.orion.visor.module.infra.dao.SystemMenuDAO;
|
||||
import com.orion.visor.module.infra.dao.SystemRoleDAO;
|
||||
import com.orion.visor.module.infra.dao.SystemRoleMenuDAO;
|
||||
import com.orion.visor.module.infra.define.RoleDefine;
|
||||
import com.orion.visor.module.infra.entity.domain.SystemMenuDO;
|
||||
import com.orion.visor.module.infra.entity.domain.SystemRoleDO;
|
||||
import com.orion.visor.module.infra.entity.domain.SystemRoleMenuDO;
|
||||
import com.orion.visor.module.infra.entity.dto.SystemMenuCacheDTO;
|
||||
import com.orion.visor.module.infra.entity.vo.SystemMenuVO;
|
||||
import com.orion.visor.module.infra.entity.vo.UserCollectInfoVO;
|
||||
import com.orion.visor.module.infra.entity.vo.UserPermissionVO;
|
||||
import com.orion.visor.module.infra.enums.MenuStatusEnum;
|
||||
import com.orion.visor.module.infra.enums.MenuTypeEnum;
|
||||
import com.orion.visor.module.infra.enums.PreferenceTypeEnum;
|
||||
import com.orion.visor.module.infra.enums.RoleStatusEnum;
|
||||
import com.orion.visor.module.infra.service.PermissionService;
|
||||
import com.orion.visor.module.infra.service.PreferenceService;
|
||||
import com.orion.visor.module.infra.service.SystemMenuService;
|
||||
import com.orion.visor.module.infra.service.TipsService;
|
||||
import lombok.Getter;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.Resource;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.function.Function;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 权限服务
|
||||
* 权限 服务实现类
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/7/16 1:05
|
||||
* @since 2024/8/19 15:29
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class PermissionServiceImpl implements PermissionService {
|
||||
|
||||
@Getter
|
||||
private final Map<Long, SystemRoleDO> roleCache = new HashMap<>();
|
||||
|
||||
@Getter
|
||||
private final List<SystemMenuCacheDTO> menuCache = new ArrayList<>();
|
||||
|
||||
@Getter
|
||||
private final Map<Long, List<SystemMenuCacheDTO>> roleMenuCache = new HashMap<>();
|
||||
|
||||
@Resource
|
||||
private SystemRoleDAO systemRoleDAO;
|
||||
|
||||
@Resource
|
||||
private SystemMenuDAO systemMenuDAO;
|
||||
|
||||
@Resource
|
||||
private SystemRoleMenuDAO systemRoleMenuDAO;
|
||||
|
||||
@Resource
|
||||
private SystemMenuService systemMenuService;
|
||||
|
||||
@Resource
|
||||
private PreferenceService preferenceService;
|
||||
|
||||
@Resource
|
||||
private TipsService tipsService;
|
||||
|
||||
@PostConstruct
|
||||
@Override
|
||||
public void initPermissionCache() {
|
||||
long start = System.currentTimeMillis();
|
||||
log.info("initPermissionCache-start");
|
||||
roleCache.clear();
|
||||
menuCache.clear();
|
||||
roleMenuCache.clear();
|
||||
// 加载所有角色
|
||||
List<SystemRoleDO> roles = systemRoleDAO.selectList(null);
|
||||
for (SystemRoleDO role : roles) {
|
||||
roleCache.put(role.getId(), role);
|
||||
}
|
||||
// 加载所有菜单信息
|
||||
List<SystemMenuDO> menuList = systemMenuDAO.selectList(null);
|
||||
List<SystemMenuCacheDTO> menus = SystemMenuConvert.MAPPER.toCache(menuList);
|
||||
Map<Long, SystemMenuCacheDTO> menuMapping = menus.stream()
|
||||
.collect(Collectors.toMap(SystemMenuCacheDTO::getId, Function.identity()));
|
||||
menuCache.addAll(menus);
|
||||
// 查询所有角色菜单
|
||||
systemRoleMenuDAO.selectList(null)
|
||||
.stream()
|
||||
.collect(Collectors.groupingBy(SystemRoleMenuDO::getRoleId,
|
||||
Collectors.mapping(SystemRoleMenuDO::getMenuId, Collectors.toList())))
|
||||
.forEach((roleId, menuIdList) -> {
|
||||
// 获取菜单引用
|
||||
List<SystemMenuCacheDTO> roleMenus = menuIdList.stream()
|
||||
.map(menuMapping::get)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
// 获取角色引用
|
||||
roleMenuCache.put(roleId, roleMenus);
|
||||
});
|
||||
log.info("initPermissionCache-end used: {}ms", System.currentTimeMillis() - start);
|
||||
public boolean isAdminUser(Long userId) {
|
||||
return this.hasAnyRole(userId, Lists.of(RoleDefine.ADMIN_CODE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasRole(String role) {
|
||||
// 获取用户角色
|
||||
Map<Long, String> roles = this.getUserEnabledRoles();
|
||||
public boolean hasRole(Long userId, String role) {
|
||||
return this.hasAnyRole(userId, Lists.of(role));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAnyRole(Long userId, List<String> roles) {
|
||||
return !systemRoleDAO.getRoleIdByUserIdAndRoleCode(userId, roles).isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(Long userId, String permission) {
|
||||
return this.hasAnyPermission(userId, Lists.singleton(permission));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAnyPermission(Long userId, List<String> permissions) {
|
||||
// 查询用户角色
|
||||
List<SystemRoleDO> roles = systemRoleDAO.selectRoleByUserId(userId);
|
||||
roles.removeIf(s -> !RoleStatusEnum.ENABLED.getStatus().equals(s.getStatus()));
|
||||
if (roles.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
// 检查是否为超级管理员或包含此角色
|
||||
return RoleDefine.containsAdmin(roles.values()) || roles.containsValue(role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAnyRole(String... roles) {
|
||||
if (Arrays1.isEmpty(roles)) {
|
||||
// 判断是否为 admin
|
||||
boolean isAdmin = roles.stream().anyMatch(s -> s.getCode().equals(RoleDefine.ADMIN_CODE));
|
||||
if (isAdmin) {
|
||||
return true;
|
||||
}
|
||||
// 获取用户角色
|
||||
Map<Long, String> enableRoles = this.getUserEnabledRoles();
|
||||
if (enableRoles.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
// 检查是否为超级管理员 || 有此角色
|
||||
return RoleDefine.containsAdmin(enableRoles.values())
|
||||
|| Arrays.stream(roles).anyMatch(enableRoles::containsValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
// 获取用户角色
|
||||
Map<Long, String> roles = this.getUserEnabledRoles();
|
||||
if (roles.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
// 检查是否为超级管理员
|
||||
if (RoleDefine.containsAdmin(roles.values())) {
|
||||
return true;
|
||||
}
|
||||
// 检查普通角色是否有此权限
|
||||
return roles.keySet()
|
||||
.stream()
|
||||
.anyMatch(s -> this.checkRoleHasPermission(s, permission));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAnyPermission(String... permissions) {
|
||||
if (Arrays1.isEmpty(permissions)) {
|
||||
return true;
|
||||
}
|
||||
// 获取用户角色
|
||||
Map<Long, String> roles = this.getUserEnabledRoles();
|
||||
if (roles.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
// 检查是否为超级管理员
|
||||
if (RoleDefine.containsAdmin(roles.values())) {
|
||||
return true;
|
||||
}
|
||||
// 检查用户角色是否包含权限
|
||||
return Arrays.stream(permissions)
|
||||
.anyMatch(perm -> roles.keySet()
|
||||
.stream()
|
||||
.anyMatch(s -> this.checkRoleHasPermission(s, perm)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SystemMenuVO> getUserMenuList() {
|
||||
// 获取用户角色
|
||||
Map<Long, String> roles = this.getUserEnabledRoles();
|
||||
if (roles.isEmpty()) {
|
||||
return Lists.empty();
|
||||
}
|
||||
// 查询角色菜单
|
||||
Stream<SystemMenuCacheDTO> mergeStream;
|
||||
if (RoleDefine.containsAdmin(roles.values())) {
|
||||
// 管理员拥有全部菜单
|
||||
mergeStream = menuCache.stream();
|
||||
} else {
|
||||
// 当前用户所适配的角色菜单
|
||||
mergeStream = roles.keySet()
|
||||
.stream()
|
||||
.map(roleMenuCache::get)
|
||||
.filter(Objects::nonNull)
|
||||
.flatMap(Collection::stream)
|
||||
.distinct();
|
||||
}
|
||||
// 状态过滤
|
||||
List<SystemMenuVO> menus = mergeStream
|
||||
.filter(s -> MenuStatusEnum.ENABLED.getStatus().equals(s.getStatus()))
|
||||
.filter(s -> !MenuTypeEnum.FUNCTION.getType().equals(s.getType()))
|
||||
.map(SystemMenuConvert.MAPPER::to)
|
||||
List<Long> roleIdList = roles.stream()
|
||||
.map(SystemRoleDO::getId)
|
||||
.collect(Collectors.toList());
|
||||
// 构建菜单树
|
||||
return systemMenuService.buildSystemMenuTree(menus);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public UserPermissionVO getUserPermission() {
|
||||
// 获取用户信息
|
||||
UserCollectInfoVO user = SystemUserConvert.MAPPER.toCollectInfo(SecurityUtils.getLoginUser());
|
||||
Long id = user.getId();
|
||||
// 获取用户系统偏好
|
||||
Future<Map<String, Object>> systemPreference = preferenceService.getPreferenceAsync(id, PreferenceTypeEnum.SYSTEM);
|
||||
// 获取用户角色
|
||||
Map<Long, String> roles = this.getUserEnabledRoles();
|
||||
// 获取用户权限
|
||||
List<String> permissions;
|
||||
if (roles.isEmpty()) {
|
||||
permissions = Lists.empty();
|
||||
} else {
|
||||
if (RoleDefine.containsAdmin(roles.values())) {
|
||||
// 管理员拥有全部权限
|
||||
permissions = Lists.of(Const.ASTERISK);
|
||||
} else {
|
||||
// 当前用户所适配的角色的权限
|
||||
permissions = roles.keySet()
|
||||
.stream()
|
||||
.map(roleMenuCache::get)
|
||||
.filter(Objects::nonNull)
|
||||
.flatMap(Collection::stream)
|
||||
.filter(s -> MenuStatusEnum.ENABLED.getStatus().equals(s.getStatus()))
|
||||
.map(SystemMenuCacheDTO::getPermission)
|
||||
.filter(Objects::nonNull)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
// 设置已提示的 key
|
||||
user.setTippedKeys(tipsService.getTippedKeys());
|
||||
// 获取异步结果
|
||||
user.setSystemPreference(systemPreference.get());
|
||||
// 组装数据
|
||||
return UserPermissionVO.builder()
|
||||
.user(user)
|
||||
.roles(roles.values())
|
||||
.permissions(permissions)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查角色是否有权限
|
||||
*
|
||||
* @param roleId roleId
|
||||
* @param permission permission
|
||||
* @return 是否有权限
|
||||
*/
|
||||
private boolean checkRoleHasPermission(Long roleId, String permission) {
|
||||
// 获取角色权限列表
|
||||
List<SystemMenuCacheDTO> menus = roleMenuCache.get(roleId);
|
||||
if (Lists.isEmpty(menus)) {
|
||||
return false;
|
||||
}
|
||||
// 检查是否有此权限
|
||||
return menus.stream()
|
||||
.filter(s -> MenuStatusEnum.ENABLED.getStatus().equals(s.getStatus()))
|
||||
.map(SystemMenuCacheDTO::getPermission)
|
||||
.filter(Objects::nonNull)
|
||||
.anyMatch(permission::equals);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户启用的角色
|
||||
*
|
||||
* @return roles
|
||||
*/
|
||||
private Map<Long, String> getUserEnabledRoles() {
|
||||
// 获取当前用户角色
|
||||
List<UserRole> userRoles = Optional.ofNullable(SecurityUtils.getLoginUser())
|
||||
.map(LoginUser::getRoles)
|
||||
.orElse(Lists.empty());
|
||||
if (Lists.isEmpty(userRoles)) {
|
||||
return Maps.empty();
|
||||
}
|
||||
// 获取角色编码
|
||||
Map<Long, String> roles = userRoles.stream()
|
||||
.map(UserRole::getId)
|
||||
.map(roleCache::get)
|
||||
.filter(Objects::nonNull)
|
||||
// 过滤未启用的角色
|
||||
.filter(r -> RoleStatusEnum.ENABLED.getStatus().equals(r.getStatus()))
|
||||
.collect(Collectors.toMap(SystemRoleDO::getId, SystemRoleDO::getCode));
|
||||
if (Maps.isEmpty(roles)) {
|
||||
return Maps.empty();
|
||||
}
|
||||
return roles;
|
||||
return !systemRoleDAO.getPermissionByRoleIdAndPermission(roleIdList, permissions).isEmpty();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import com.orion.visor.module.infra.entity.vo.SystemMenuVO;
|
||||
import com.orion.visor.module.infra.enums.MenuStatusEnum;
|
||||
import com.orion.visor.module.infra.enums.MenuTypeEnum;
|
||||
import com.orion.visor.module.infra.enums.MenuVisibleEnum;
|
||||
import com.orion.visor.module.infra.service.PermissionService;
|
||||
import com.orion.visor.module.infra.service.UserPermissionService;
|
||||
import com.orion.visor.module.infra.service.SystemMenuService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
@@ -51,7 +51,7 @@ public class SystemMenuServiceImpl implements SystemMenuService {
|
||||
private SystemRoleMenuDAO systemRoleMenuDAO;
|
||||
|
||||
@Resource
|
||||
private PermissionService permissionService;
|
||||
private UserPermissionService userPermissionService;
|
||||
|
||||
@Override
|
||||
public Long createSystemMenu(SystemMenuCreateRequest request) {
|
||||
@@ -68,7 +68,7 @@ public class SystemMenuServiceImpl implements SystemMenuService {
|
||||
int effect = systemMenuDAO.insert(record);
|
||||
log.info("SystemMenuService-createSystemMenu effect: {}, record: {}", effect, JSON.toJSONString(record));
|
||||
// 保存至缓存
|
||||
List<SystemMenuCacheDTO> menuCache = permissionService.getMenuCache();
|
||||
List<SystemMenuCacheDTO> menuCache = userPermissionService.getMenuCache();
|
||||
menuCache.add(SystemMenuConvert.MAPPER.toCache(record));
|
||||
return record.getId();
|
||||
}
|
||||
@@ -89,7 +89,7 @@ public class SystemMenuServiceImpl implements SystemMenuService {
|
||||
// 重新查询转换为缓存
|
||||
SystemMenuCacheDTO cache = SystemMenuConvert.MAPPER.toCache(systemMenuDAO.selectById(id));
|
||||
// 获取原始缓存
|
||||
permissionService.getMenuCache()
|
||||
userPermissionService.getMenuCache()
|
||||
.stream()
|
||||
.filter(s -> s.getId().equals(id))
|
||||
.findFirst()
|
||||
@@ -115,7 +115,7 @@ public class SystemMenuServiceImpl implements SystemMenuService {
|
||||
Integer type = request.getType();
|
||||
Integer status = request.getStatus();
|
||||
// 从缓存中查询
|
||||
List<SystemMenuVO> menus = permissionService.getMenuCache()
|
||||
List<SystemMenuVO> menus = userPermissionService.getMenuCache()
|
||||
.stream()
|
||||
.filter(s -> Strings.isBlank(name) || s.getName().contains(name))
|
||||
.filter(s -> type == null || s.getType().equals(type))
|
||||
@@ -197,7 +197,7 @@ public class SystemMenuServiceImpl implements SystemMenuService {
|
||||
// 添加日志参数
|
||||
OperatorLogs.add(OperatorLogs.NAME, record.getName());
|
||||
// 从缓存中查询
|
||||
List<SystemMenuCacheDTO> cache = permissionService.getMenuCache();
|
||||
List<SystemMenuCacheDTO> cache = userPermissionService.getMenuCache();
|
||||
// 获取要更新的id
|
||||
List<Long> updateIdList = this.getChildrenIdList(id, cache, record.getType());
|
||||
// 修改状态
|
||||
@@ -229,7 +229,7 @@ public class SystemMenuServiceImpl implements SystemMenuService {
|
||||
// 添加日志参数
|
||||
OperatorLogs.add(OperatorLogs.NAME, record.getName());
|
||||
// 从缓存中查询
|
||||
List<SystemMenuCacheDTO> cache = permissionService.getMenuCache();
|
||||
List<SystemMenuCacheDTO> cache = userPermissionService.getMenuCache();
|
||||
// 获取要删除的id
|
||||
List<Long> deletedIdList = this.getChildrenIdList(id, cache, record.getType());
|
||||
// 删除菜单
|
||||
@@ -239,7 +239,7 @@ public class SystemMenuServiceImpl implements SystemMenuService {
|
||||
// 删除菜单缓存
|
||||
cache.removeIf(s -> deletedIdList.contains(s.getId()));
|
||||
// 删除引用缓存
|
||||
permissionService.getRoleMenuCache()
|
||||
userPermissionService.getRoleMenuCache()
|
||||
.values()
|
||||
.forEach(roleMenus -> roleMenus.removeIf(s -> deletedIdList.contains(s.getId())));
|
||||
log.info("SystemMenuService-deleteSystemMenu deletedIdList: {}, effect: {}", deletedIdList, effect);
|
||||
|
||||
@@ -74,9 +74,9 @@ public class SystemMessageServiceImpl implements SystemMessageService {
|
||||
.eq(SystemMessageDO::getClassify, request.getClassify())
|
||||
.lt(SystemMessageDO::getId, request.getMaxId())
|
||||
.eq(SystemMessageDO::getStatus, status)
|
||||
.last(Const.LIMIT + Const.SPACE + request.getLimit())
|
||||
.orderByDesc(SystemMessageDO::getId)
|
||||
.then()
|
||||
.limit(request.getLimit())
|
||||
.list(SystemMessageConvert.MAPPER::to);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import com.orion.visor.module.infra.entity.domain.SystemRoleDO;
|
||||
import com.orion.visor.module.infra.entity.domain.SystemRoleMenuDO;
|
||||
import com.orion.visor.module.infra.entity.dto.SystemMenuCacheDTO;
|
||||
import com.orion.visor.module.infra.entity.request.menu.SystemRoleGrantMenuRequest;
|
||||
import com.orion.visor.module.infra.service.PermissionService;
|
||||
import com.orion.visor.module.infra.service.UserPermissionService;
|
||||
import com.orion.visor.module.infra.service.SystemRoleMenuService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -49,7 +49,7 @@ public class SystemRoleMenuServiceImpl implements SystemRoleMenuService {
|
||||
private SystemRoleMenuDAO systemRoleMenuDAO;
|
||||
|
||||
@Resource
|
||||
private PermissionService permissionService;
|
||||
private UserPermissionService userPermissionService;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@@ -104,7 +104,7 @@ public class SystemRoleMenuServiceImpl implements SystemRoleMenuService {
|
||||
effect += insertMenuIdList.size();
|
||||
}
|
||||
// 更新缓存
|
||||
Map<Long, List<SystemMenuCacheDTO>> cache = permissionService.getRoleMenuCache();
|
||||
Map<Long, List<SystemMenuCacheDTO>> cache = userPermissionService.getRoleMenuCache();
|
||||
List<SystemMenuCacheDTO> roleCache = cache.computeIfAbsent(roleId, s -> new ArrayList<>());
|
||||
roleCache.clear();
|
||||
roleCache.addAll(SystemMenuConvert.MAPPER.toCache(menuList));
|
||||
|
||||
@@ -20,7 +20,7 @@ import com.orion.visor.module.infra.entity.request.role.SystemRoleUpdateRequest;
|
||||
import com.orion.visor.module.infra.entity.vo.SystemRoleVO;
|
||||
import com.orion.visor.module.infra.enums.RoleStatusEnum;
|
||||
import com.orion.visor.module.infra.service.DataPermissionService;
|
||||
import com.orion.visor.module.infra.service.PermissionService;
|
||||
import com.orion.visor.module.infra.service.UserPermissionService;
|
||||
import com.orion.visor.module.infra.service.SystemRoleService;
|
||||
import com.orion.visor.module.infra.service.SystemUserRoleService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -51,7 +51,7 @@ public class SystemRoleServiceImpl implements SystemRoleService {
|
||||
private SystemRoleMenuDAO systemRoleMenuDAO;
|
||||
|
||||
@Resource
|
||||
private PermissionService permissionService;
|
||||
private UserPermissionService userPermissionService;
|
||||
|
||||
@Resource
|
||||
private SystemUserRoleService systemUserRoleService;
|
||||
@@ -72,7 +72,7 @@ public class SystemRoleServiceImpl implements SystemRoleService {
|
||||
int effect = systemRoleDAO.insert(record);
|
||||
log.info("SystemRoleService-createSystemRole effect: {}, domain: {}", effect, JSON.toJSONString(record));
|
||||
// 设置到缓存
|
||||
permissionService.getRoleCache().put(record.getId(), record);
|
||||
userPermissionService.getRoleCache().put(record.getId(), record);
|
||||
return record.getId();
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ public class SystemRoleServiceImpl implements SystemRoleService {
|
||||
int effect = systemRoleDAO.updateById(updateRecord);
|
||||
log.info("SystemRoleService-updateSystemRoleById effect: {}, updateRecord: {}", effect, JSON.toJSONString(updateRecord));
|
||||
// 设置到缓存
|
||||
SystemRoleDO roleCache = permissionService.getRoleCache().get(id);
|
||||
SystemRoleDO roleCache = userPermissionService.getRoleCache().get(id);
|
||||
roleCache.setName(updateRecord.getName());
|
||||
return effect;
|
||||
}
|
||||
@@ -117,7 +117,7 @@ public class SystemRoleServiceImpl implements SystemRoleService {
|
||||
int effect = systemRoleDAO.updateById(updateRecord);
|
||||
log.info("SystemRoleService-updateRoleStatus effect: {}, updateRecord: {}", effect, JSON.toJSONString(updateRecord));
|
||||
// 修改本地缓存状态
|
||||
SystemRoleDO roleCache = permissionService.getRoleCache().get(id);
|
||||
SystemRoleDO roleCache = userPermissionService.getRoleCache().get(id);
|
||||
roleCache.setStatus(status);
|
||||
// 删除数据权限缓存
|
||||
dataPermissionService.clearRoleCache(id);
|
||||
@@ -180,9 +180,9 @@ public class SystemRoleServiceImpl implements SystemRoleService {
|
||||
// 删除角色菜单关联
|
||||
effect += systemRoleMenuDAO.deleteByRoleId(id);
|
||||
// 删除角色缓存
|
||||
permissionService.getRoleCache().remove(id);
|
||||
userPermissionService.getRoleCache().remove(id);
|
||||
// 删除菜单缓存
|
||||
permissionService.getRoleMenuCache().remove(id);
|
||||
userPermissionService.getRoleMenuCache().remove(id);
|
||||
// 删除用户缓存中的角色
|
||||
systemUserRoleService.deleteUserCacheRoleAsync(id, userIdList);
|
||||
// 删除数据权限缓存
|
||||
|
||||
@@ -25,7 +25,6 @@ import com.orion.visor.module.infra.dao.OperatorLogDAO;
|
||||
import com.orion.visor.module.infra.dao.SystemRoleDAO;
|
||||
import com.orion.visor.module.infra.dao.SystemUserDAO;
|
||||
import com.orion.visor.module.infra.dao.SystemUserRoleDAO;
|
||||
import com.orion.visor.module.infra.define.RoleDefine;
|
||||
import com.orion.visor.module.infra.define.cache.TipsCacheKeyDefine;
|
||||
import com.orion.visor.module.infra.define.cache.UserCacheKeyDefine;
|
||||
import com.orion.visor.module.infra.define.config.AppAuthenticationConfig;
|
||||
@@ -302,11 +301,6 @@ public class SystemUserServiceImpl implements SystemUserService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAdminUser(Long userId) {
|
||||
return systemRoleDAO.getRoleIdByUserIdAndRoleCode(userId, RoleDefine.ADMIN_CODE) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查用户名否存在
|
||||
*
|
||||
|
||||
@@ -0,0 +1,305 @@
|
||||
package com.orion.visor.module.infra.service.impl;
|
||||
|
||||
import com.orion.lang.utils.Arrays1;
|
||||
import com.orion.lang.utils.collect.Lists;
|
||||
import com.orion.lang.utils.collect.Maps;
|
||||
import com.orion.visor.framework.common.constant.Const;
|
||||
import com.orion.visor.framework.common.security.LoginUser;
|
||||
import com.orion.visor.framework.common.security.UserRole;
|
||||
import com.orion.visor.framework.security.core.utils.SecurityUtils;
|
||||
import com.orion.visor.module.infra.convert.SystemMenuConvert;
|
||||
import com.orion.visor.module.infra.convert.SystemUserConvert;
|
||||
import com.orion.visor.module.infra.dao.SystemMenuDAO;
|
||||
import com.orion.visor.module.infra.dao.SystemRoleDAO;
|
||||
import com.orion.visor.module.infra.dao.SystemRoleMenuDAO;
|
||||
import com.orion.visor.module.infra.define.RoleDefine;
|
||||
import com.orion.visor.module.infra.entity.domain.SystemMenuDO;
|
||||
import com.orion.visor.module.infra.entity.domain.SystemRoleDO;
|
||||
import com.orion.visor.module.infra.entity.domain.SystemRoleMenuDO;
|
||||
import com.orion.visor.module.infra.entity.dto.SystemMenuCacheDTO;
|
||||
import com.orion.visor.module.infra.entity.vo.SystemMenuVO;
|
||||
import com.orion.visor.module.infra.entity.vo.UserCollectInfoVO;
|
||||
import com.orion.visor.module.infra.entity.vo.UserPermissionVO;
|
||||
import com.orion.visor.module.infra.enums.MenuStatusEnum;
|
||||
import com.orion.visor.module.infra.enums.MenuTypeEnum;
|
||||
import com.orion.visor.module.infra.enums.PreferenceTypeEnum;
|
||||
import com.orion.visor.module.infra.enums.RoleStatusEnum;
|
||||
import com.orion.visor.module.infra.service.PreferenceService;
|
||||
import com.orion.visor.module.infra.service.SystemMenuService;
|
||||
import com.orion.visor.module.infra.service.TipsService;
|
||||
import com.orion.visor.module.infra.service.UserPermissionService;
|
||||
import lombok.Getter;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.Resource;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 用户权限服务
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/7/16 1:05
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class UserPermissionServiceImpl implements UserPermissionService {
|
||||
|
||||
@Getter
|
||||
private final Map<Long, SystemRoleDO> roleCache = new HashMap<>();
|
||||
|
||||
@Getter
|
||||
private final List<SystemMenuCacheDTO> menuCache = new ArrayList<>();
|
||||
|
||||
@Getter
|
||||
private final Map<Long, List<SystemMenuCacheDTO>> roleMenuCache = new HashMap<>();
|
||||
|
||||
@Resource
|
||||
private SystemRoleDAO systemRoleDAO;
|
||||
|
||||
@Resource
|
||||
private SystemMenuDAO systemMenuDAO;
|
||||
|
||||
@Resource
|
||||
private SystemRoleMenuDAO systemRoleMenuDAO;
|
||||
|
||||
@Resource
|
||||
private SystemMenuService systemMenuService;
|
||||
|
||||
@Resource
|
||||
private PreferenceService preferenceService;
|
||||
|
||||
@Resource
|
||||
private TipsService tipsService;
|
||||
|
||||
@PostConstruct
|
||||
@Override
|
||||
public void initPermissionCache() {
|
||||
long start = System.currentTimeMillis();
|
||||
log.info("initPermissionCache-start");
|
||||
roleCache.clear();
|
||||
menuCache.clear();
|
||||
roleMenuCache.clear();
|
||||
// 加载所有角色
|
||||
List<SystemRoleDO> roles = systemRoleDAO.selectList(null);
|
||||
for (SystemRoleDO role : roles) {
|
||||
roleCache.put(role.getId(), role);
|
||||
}
|
||||
// 加载所有菜单信息
|
||||
List<SystemMenuDO> menuList = systemMenuDAO.selectList(null);
|
||||
List<SystemMenuCacheDTO> menus = SystemMenuConvert.MAPPER.toCache(menuList);
|
||||
Map<Long, SystemMenuCacheDTO> menuMapping = menus.stream()
|
||||
.collect(Collectors.toMap(SystemMenuCacheDTO::getId, Function.identity()));
|
||||
menuCache.addAll(menus);
|
||||
// 查询所有角色菜单
|
||||
systemRoleMenuDAO.selectList(null)
|
||||
.stream()
|
||||
.collect(Collectors.groupingBy(SystemRoleMenuDO::getRoleId,
|
||||
Collectors.mapping(SystemRoleMenuDO::getMenuId, Collectors.toList())))
|
||||
.forEach((roleId, menuIdList) -> {
|
||||
// 获取菜单引用
|
||||
List<SystemMenuCacheDTO> roleMenus = menuIdList.stream()
|
||||
.map(menuMapping::get)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
// 获取角色引用
|
||||
roleMenuCache.put(roleId, roleMenus);
|
||||
});
|
||||
log.info("initPermissionCache-end used: {}ms", System.currentTimeMillis() - start);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasRole(String role) {
|
||||
// 获取用户角色
|
||||
Map<Long, String> roles = this.getUserEnabledRoles();
|
||||
if (roles.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
// 检查是否为超级管理员或包含此角色
|
||||
return RoleDefine.containsAdmin(roles.values()) || roles.containsValue(role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAnyRole(String... roles) {
|
||||
if (Arrays1.isEmpty(roles)) {
|
||||
return true;
|
||||
}
|
||||
// 获取用户角色
|
||||
Map<Long, String> enableRoles = this.getUserEnabledRoles();
|
||||
if (enableRoles.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
// 检查是否为超级管理员 || 有此角色
|
||||
return RoleDefine.containsAdmin(enableRoles.values())
|
||||
|| Arrays.stream(roles).anyMatch(enableRoles::containsValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
// 获取用户角色
|
||||
Map<Long, String> roles = this.getUserEnabledRoles();
|
||||
if (roles.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
// 检查是否为超级管理员
|
||||
if (RoleDefine.containsAdmin(roles.values())) {
|
||||
return true;
|
||||
}
|
||||
// 检查普通角色是否有此权限
|
||||
return roles.keySet()
|
||||
.stream()
|
||||
.anyMatch(s -> this.checkRoleHasPermission(s, permission));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAnyPermission(String... permissions) {
|
||||
if (Arrays1.isEmpty(permissions)) {
|
||||
return true;
|
||||
}
|
||||
// 获取用户角色
|
||||
Map<Long, String> roles = this.getUserEnabledRoles();
|
||||
if (roles.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
// 检查是否为超级管理员
|
||||
if (RoleDefine.containsAdmin(roles.values())) {
|
||||
return true;
|
||||
}
|
||||
// 检查用户角色是否包含权限
|
||||
return Arrays.stream(permissions)
|
||||
.anyMatch(perm -> roles.keySet()
|
||||
.stream()
|
||||
.anyMatch(s -> this.checkRoleHasPermission(s, perm)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SystemMenuVO> getUserMenuList() {
|
||||
// 获取用户角色
|
||||
Map<Long, String> roles = this.getUserEnabledRoles();
|
||||
if (roles.isEmpty()) {
|
||||
return Lists.empty();
|
||||
}
|
||||
// 查询角色菜单
|
||||
Stream<SystemMenuCacheDTO> mergeStream;
|
||||
if (RoleDefine.containsAdmin(roles.values())) {
|
||||
// 管理员拥有全部菜单
|
||||
mergeStream = menuCache.stream();
|
||||
} else {
|
||||
// 当前用户所适配的角色菜单
|
||||
mergeStream = roles.keySet()
|
||||
.stream()
|
||||
.map(roleMenuCache::get)
|
||||
.filter(Objects::nonNull)
|
||||
.flatMap(Collection::stream)
|
||||
.distinct();
|
||||
}
|
||||
// 状态过滤
|
||||
List<SystemMenuVO> menus = mergeStream
|
||||
.filter(s -> MenuStatusEnum.ENABLED.getStatus().equals(s.getStatus()))
|
||||
.filter(s -> !MenuTypeEnum.FUNCTION.getType().equals(s.getType()))
|
||||
.map(SystemMenuConvert.MAPPER::to)
|
||||
.collect(Collectors.toList());
|
||||
// 构建菜单树
|
||||
return systemMenuService.buildSystemMenuTree(menus);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public UserPermissionVO getUserPermission() {
|
||||
// 获取用户信息
|
||||
UserCollectInfoVO user = SystemUserConvert.MAPPER.toCollectInfo(SecurityUtils.getLoginUser());
|
||||
Long id = user.getId();
|
||||
// 获取用户系统偏好
|
||||
Future<Map<String, Object>> systemPreference = preferenceService.getPreferenceAsync(id, PreferenceTypeEnum.SYSTEM);
|
||||
// 获取用户角色
|
||||
Map<Long, String> roles = this.getUserEnabledRoles();
|
||||
// 获取用户权限
|
||||
List<String> permissions;
|
||||
if (roles.isEmpty()) {
|
||||
permissions = Lists.empty();
|
||||
} else {
|
||||
if (RoleDefine.containsAdmin(roles.values())) {
|
||||
// 管理员拥有全部权限
|
||||
permissions = Lists.of(Const.ASTERISK);
|
||||
} else {
|
||||
// 当前用户所适配的角色的权限
|
||||
permissions = roles.keySet()
|
||||
.stream()
|
||||
.map(roleMenuCache::get)
|
||||
.filter(Objects::nonNull)
|
||||
.flatMap(Collection::stream)
|
||||
.filter(s -> MenuStatusEnum.ENABLED.getStatus().equals(s.getStatus()))
|
||||
.map(SystemMenuCacheDTO::getPermission)
|
||||
.filter(Objects::nonNull)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
// 设置已提示的 key
|
||||
user.setTippedKeys(tipsService.getTippedKeys());
|
||||
// 获取异步结果
|
||||
user.setSystemPreference(systemPreference.get());
|
||||
// 组装数据
|
||||
return UserPermissionVO.builder()
|
||||
.user(user)
|
||||
.roles(roles.values())
|
||||
.permissions(permissions)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查角色是否有权限
|
||||
*
|
||||
* @param roleId roleId
|
||||
* @param permission permission
|
||||
* @return 是否有权限
|
||||
*/
|
||||
private boolean checkRoleHasPermission(Long roleId, String permission) {
|
||||
// 获取角色权限列表
|
||||
List<SystemMenuCacheDTO> menus = roleMenuCache.get(roleId);
|
||||
if (Lists.isEmpty(menus)) {
|
||||
return false;
|
||||
}
|
||||
// 检查是否有此权限
|
||||
return menus.stream()
|
||||
.filter(s -> MenuStatusEnum.ENABLED.getStatus().equals(s.getStatus()))
|
||||
.map(SystemMenuCacheDTO::getPermission)
|
||||
.filter(Objects::nonNull)
|
||||
.anyMatch(permission::equals);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户启用的角色
|
||||
*
|
||||
* @return roles
|
||||
*/
|
||||
private Map<Long, String> getUserEnabledRoles() {
|
||||
// 获取当前用户角色
|
||||
List<UserRole> userRoles = Optional.ofNullable(SecurityUtils.getLoginUser())
|
||||
.map(LoginUser::getRoles)
|
||||
.orElse(Lists.empty());
|
||||
if (Lists.isEmpty(userRoles)) {
|
||||
return Maps.empty();
|
||||
}
|
||||
// 获取角色编码
|
||||
Map<Long, String> roles = userRoles.stream()
|
||||
.map(UserRole::getId)
|
||||
.map(roleCache::get)
|
||||
.filter(Objects::nonNull)
|
||||
// 过滤未启用的角色
|
||||
.filter(r -> RoleStatusEnum.ENABLED.getStatus().equals(r.getStatus()))
|
||||
.collect(Collectors.toMap(SystemRoleDO::getId, SystemRoleDO::getCode));
|
||||
if (Maps.isEmpty(roles)) {
|
||||
return Maps.empty();
|
||||
}
|
||||
return roles;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,6 +22,11 @@
|
||||
"type": "java.lang.Integer",
|
||||
"description": "凭证续签最大次数."
|
||||
},
|
||||
{
|
||||
"name": "app.authentication.loginFailedSendThreshold",
|
||||
"type": "java.lang.Integer",
|
||||
"description": "登录失败发送站内信阈值."
|
||||
},
|
||||
{
|
||||
"name": "app.authentication.loginFailedLockCount",
|
||||
"type": "java.lang.Integer",
|
||||
|
||||
@@ -24,12 +24,42 @@
|
||||
SELECT role_id
|
||||
FROM system_user_role
|
||||
WHERE user_id = #{userId}
|
||||
AND deleted = 0
|
||||
AND role_id IN (SELECT id FROM system_role WHERE CODE = #{code} AND deleted = 0) LIMIT 1
|
||||
AND deleted = 0
|
||||
AND role_id IN
|
||||
(SELECT id
|
||||
FROM system_role
|
||||
WHERE deleted = 0
|
||||
AND status = 1
|
||||
AND code IN
|
||||
<foreach collection="codeList" item="item" open="(" close=")" separator=",">
|
||||
#{item}
|
||||
</foreach>
|
||||
)
|
||||
</select>
|
||||
|
||||
<select id="getPermissionByRoleIdAndPermission" resultType="java.lang.String">
|
||||
SELECT m.permission
|
||||
FROM system_menu m
|
||||
LEFT JOIN system_role_menu rm ON rm.menu_id = m.id
|
||||
WHERE rm.deleted = 0
|
||||
AND m.deleted = 0
|
||||
AND m.type = 3
|
||||
AND m.status = 1
|
||||
<if test="permissionList != null and permissionList.size() > 0">
|
||||
AND m.permission IN
|
||||
<foreach collection="permissionList" item="item" open="(" close=")" separator=",">
|
||||
#{item}
|
||||
</foreach>
|
||||
</if>
|
||||
AND rm.role_id IN
|
||||
<foreach collection="roleIdList" item="item" open="(" close=")" separator=",">
|
||||
#{item}
|
||||
</foreach>
|
||||
</select>
|
||||
|
||||
<select id="selectRoleByUserId" resultMap="BaseResultMap">
|
||||
SELECT <include refid="Base_Column_List"/>
|
||||
SELECT
|
||||
<include refid="Base_Column_List"/>
|
||||
FROM system_role
|
||||
WHERE deleted = 0
|
||||
AND id IN (SELECT role_id FROM system_user_role WHERE user_id = #{userId} AND deleted = 0)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
VITE_API_BASE_URL= 'http://127.0.0.1:9200/orion-visor/api'
|
||||
VITE_WS_BASE_URL= 'ws://127.0.0.1:9200/orion-visor/keep-alive'
|
||||
VITE_APP_VERSION= '2.1.1'
|
||||
VITE_APP_VERSION= '2.1.4'
|
||||
VITE_APP_RELEASE= 'community'
|
||||
VITE_SFTP_PREVIEW_MB= 2
|
||||
VITE_DEMO_MODE= false
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
VITE_API_BASE_URL= '/orion-visor/api'
|
||||
VITE_WS_BASE_URL= '/orion-visor/keep-alive'
|
||||
VITE_APP_VERSION= '2.1.1'
|
||||
VITE_APP_VERSION= '2.1.4'
|
||||
VITE_APP_RELEASE= 'community'
|
||||
VITE_SFTP_PREVIEW_MB= 2
|
||||
VITE_DEMO_MODE= false
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "orion-visor-ui",
|
||||
"description": "Orion Visor UI",
|
||||
"version": "2.1.1",
|
||||
"version": "2.1.4",
|
||||
"private": true,
|
||||
"author": "Jiahang Li",
|
||||
"license": "Apache 2.0",
|
||||
|
||||
@@ -29,7 +29,6 @@ export interface CommandSnippetQueryResponse extends CommandSnippetQueryResponse
|
||||
|
||||
export interface CommandSnippetQueryResponseExtra {
|
||||
visible: boolean;
|
||||
expand?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -51,6 +51,13 @@ export function getTerminalAccessToken() {
|
||||
return axios.get<string>('/asset/host-terminal/access');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取主机终端 transferToken
|
||||
*/
|
||||
export function getTerminalTransferToken() {
|
||||
return axios.get<string>('/asset/host-terminal/transfer');
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开主机终端 websocket
|
||||
*/
|
||||
|
||||
@@ -31,7 +31,6 @@ export interface PathBookmarkQueryResponse extends PathBookmarkQueryResponseExtr
|
||||
|
||||
export interface PathBookmarkQueryResponseExtra {
|
||||
visible: boolean;
|
||||
expand?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -93,5 +93,5 @@ export function deleteMenu(id: number) {
|
||||
* 刷新缓存
|
||||
*/
|
||||
export function refreshCache() {
|
||||
return axios.put('/infra/permission/refresh-cache');
|
||||
return axios.put('/infra/user-permission/refresh-cache');
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { Pagination } from '@/types/global';
|
||||
import axios from 'axios';
|
||||
|
||||
/**
|
||||
* 系统消息查询请求
|
||||
*/
|
||||
export interface MessageQueryRequest {
|
||||
limit?: number;
|
||||
export interface MessageQueryRequest extends Pagination {
|
||||
maxId?: number;
|
||||
classify?: string;
|
||||
queryUnread?: boolean;
|
||||
|
||||
@@ -50,12 +50,12 @@ export function logout() {
|
||||
* 获取用户信息
|
||||
*/
|
||||
export function getUserPermission() {
|
||||
return axios.get<UserPermissionResponse>('/infra/permission/user');
|
||||
return axios.get<UserPermissionResponse>('/infra/user-permission/user');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取菜单列表
|
||||
*/
|
||||
export function getMenuList() {
|
||||
return axios.get<Array<MenuQueryResponse>>('/infra/permission/menu');
|
||||
return axios.get<Array<MenuQueryResponse>>('/infra/user-permission/menu');
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ body {
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
border: 1px solid transparent;
|
||||
transition: background-color 0.1s cubic-bezier(0, 0, 1, 1);
|
||||
transition: background-color 0.15s cubic-bezier(0, 0, 1, 1);
|
||||
|
||||
&:hover {
|
||||
background: var(--color-fill-3);
|
||||
@@ -230,6 +230,26 @@ body {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.fs12 {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.fs13 {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.fs14 {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.fs15 {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.fs16 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.text-ellipsis {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
@@ -301,13 +321,13 @@ body {
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background-color: var(--color-fill-1);
|
||||
border-radius: 8px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
border: 1px solid transparent;
|
||||
background-clip: padding-box;
|
||||
border-radius: 8px;
|
||||
border-radius: 4px;
|
||||
background-color: var(--color-fill-4);
|
||||
|
||||
//&:hover {
|
||||
|
||||
@@ -15,11 +15,13 @@ body {
|
||||
--color-bg-panel-tabs: var(--color-bg-panel);
|
||||
--color-bg-panel-tabs-active: #F9F9F9;
|
||||
--color-bg-panel-icon-1: #F5F5F5;
|
||||
--color-bg-panel-bar: #F3F4F5;
|
||||
--color-bg-panel-bar: #F0F0F0;
|
||||
--color-panel-text-1: var(--color-content-text-1);
|
||||
--color-panel-text-2: var(--color-content-text-3);
|
||||
--color-panel-gradient-start: rgba(218, 218, 218, 1);
|
||||
--color-panel-gradient-end: rgba(218, 218, 218, 0);
|
||||
--color-button-bg: #E3E3E3;
|
||||
--color-button-bg-active: var(--color-sidebar-icon-checked);
|
||||
--search-bg-focus: rgba(234, 234, 234, .75);
|
||||
--search-bg: rgba(234, 234, 234, .95);
|
||||
--search-color-text: #0E0E0E;
|
||||
@@ -52,6 +54,8 @@ body[terminal-theme='dark'] {
|
||||
--color-panel-text-2: var(--color-content-text-3);
|
||||
--color-panel-gradient-start: rgba(38, 38, 38, 1);
|
||||
--color-panel-gradient-end: rgba(38, 38, 38, 0);
|
||||
--color-button-bg: var(--color-sidebar-icon-bg);
|
||||
--color-button-bg-active: #484848;
|
||||
--search-bg: rgba(12, 12, 12, .75);
|
||||
--search-bg-focus: rgba(12, 12, 12, .95);
|
||||
--search-color-text: #E0E0E0;
|
||||
@@ -508,7 +512,7 @@ body[terminal-theme='dark'] .arco-modal-container {
|
||||
color: var(--color-sidebar-icon);
|
||||
border-radius: 4px;
|
||||
border: 1px solid transparent;
|
||||
transition: 0.1s cubic-bezier(0, 0, 1, 1);
|
||||
transition: 0.15s cubic-bezier(0, 0, 1, 1);
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
import type { HostQueryResponse } from '@/api/asset/host';
|
||||
import { dataColor } from '@/utils';
|
||||
import { tagColor } from '@/views/asset/host-list/types/const';
|
||||
import { useRowSelection } from '@/types/table';
|
||||
import { useRowSelection } from '@/hooks/table';
|
||||
import columns from '../types/table.columns';
|
||||
import { computed } from 'vue';
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@ export default class LogAppender implements ILogAppender {
|
||||
|
||||
// 复制
|
||||
copy(): void {
|
||||
copyText(this.current.terminal.getSelection(), '已复制');
|
||||
copyText(this.current.terminal.getSelection(), true);
|
||||
this.focus();
|
||||
}
|
||||
|
||||
|
||||
@@ -175,7 +175,7 @@
|
||||
import { downloadExecCommandLogFile } from '@/api/exec/exec-command-log';
|
||||
import { downloadExecJobLogFile } from '@/api/job/exec-job-log';
|
||||
import { downloadFile } from '@/utils/file';
|
||||
import XtermSearchModal from '@/components/xtrem/search-modal/index.vue';
|
||||
import XtermSearchModal from '@/components/xterm/search-modal/index.vue';
|
||||
import '@xterm/xterm/css/xterm.css';
|
||||
|
||||
const props = defineProps<{
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
</template>
|
||||
<!-- 模板命令 -->
|
||||
<template #command="{ record }">
|
||||
<span class="copy-left" @click="copy(record.command, '已复制')">
|
||||
<span class="copy-left" @click="copy(record.command, true)">
|
||||
<icon-copy />
|
||||
</span>
|
||||
<span :title="record.command">{{ record.command }}</span>
|
||||
@@ -89,7 +89,7 @@
|
||||
<script lang="ts" setup>
|
||||
import type { ExecTemplateQueryRequest, ExecTemplateQueryResponse } from '@/api/exec/exec-template';
|
||||
import { reactive, ref } from 'vue';
|
||||
import { usePagination } from '@/types/table';
|
||||
import { useTablePagination } from '@/hooks/table';
|
||||
import useVisible from '@/hooks/visible';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import { copy } from '@/hooks/copy';
|
||||
@@ -100,7 +100,7 @@
|
||||
|
||||
const { visible, setVisible } = useVisible();
|
||||
const { loading, setLoading } = useLoading();
|
||||
const pagination = usePagination();
|
||||
const pagination = useTablePagination();
|
||||
|
||||
const tableRenderData = ref<ExecTemplateQueryResponse[]>([]);
|
||||
const formModel = reactive<ExecTemplateQueryRequest>({
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'pathBookmarkGroupSelect'
|
||||
name: 'pathBookmarkGroupSelector'
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'commandSnippetGroupSelect'
|
||||
name: 'commandSnippetGroupSelector'
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -104,7 +104,7 @@
|
||||
<template #append>
|
||||
<span class="allow-click span-blue"
|
||||
title="点击复制"
|
||||
@click="copy(inputValues.cron,'已复制')">
|
||||
@click="copy(inputValues.cron, true)">
|
||||
<icon-copy />
|
||||
</span>
|
||||
</template>
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
<template #beforeValue="{ record }">
|
||||
<span class="copy-left"
|
||||
title="复制"
|
||||
@click="copy(record.beforeValue, '已复制')">
|
||||
@click="copy(record.beforeValue, true)">
|
||||
<icon-copy />
|
||||
</span>
|
||||
<span>{{ record.beforeValue }}</span>
|
||||
@@ -49,7 +49,7 @@
|
||||
<template #afterValue="{ record }">
|
||||
<span class="copy-left"
|
||||
title="复制"
|
||||
@click="copy(record.afterValue, '已复制')">
|
||||
@click="copy(record.afterValue, true)">
|
||||
<icon-copy />
|
||||
</span>
|
||||
<span>{{ record.afterValue }}</span>
|
||||
@@ -85,7 +85,7 @@
|
||||
import useLoading from '@/hooks/loading';
|
||||
import useVisible from '@/hooks/visible';
|
||||
import { getHistoryValuePage } from '@/api/meta/history-value';
|
||||
import { usePagination } from '@/types/table';
|
||||
import { useTablePagination } from '@/hooks/table';
|
||||
import { copy } from '@/hooks/copy';
|
||||
import columns from './table.columns';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
@@ -96,7 +96,7 @@
|
||||
}>();
|
||||
const emits = defineEmits(['updated']);
|
||||
|
||||
const pagination = usePagination();
|
||||
const pagination = useTablePagination();
|
||||
const { visible, setVisible } = useVisible();
|
||||
const { loading, setLoading } = useLoading();
|
||||
|
||||
|
||||
@@ -129,6 +129,7 @@
|
||||
: undefined;
|
||||
// 查询数据
|
||||
const { data } = await getSystemMessageList({
|
||||
page: 1,
|
||||
limit: messageLimit,
|
||||
classify: currentClassify.value,
|
||||
queryUnread: queryUnread.value,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user