Compare commits
15 Commits
155376ca6e
...
2c37cd2c75
| Author | SHA1 | Date | |
|---|---|---|---|
| 2c37cd2c75 | |||
| aefb212285 | |||
| 2e18a8a419 | |||
| a6a5d42bea | |||
| eaf44e1b4f | |||
| 6da20bdac4 | |||
| 02010bd9ae | |||
| 660a8430f1 | |||
| da56185960 | |||
| 5d8816fb2f | |||
| b271f0aa87 | |||
| 50ac15a8f7 | |||
| 872be51082 | |||
| da78005ad7 | |||
| f6b3369dc0 |
@@ -0,0 +1,54 @@
|
|||||||
|
stages:
|
||||||
|
- sync
|
||||||
|
|
||||||
|
sync-to-github:
|
||||||
|
stage: sync
|
||||||
|
tags:
|
||||||
|
- sync
|
||||||
|
image: ruby:3.3
|
||||||
|
rules:
|
||||||
|
- if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_TAG == null'
|
||||||
|
when: on_success
|
||||||
|
- when: never
|
||||||
|
# 关闭浅克隆,拉取完整提交记录
|
||||||
|
variables:
|
||||||
|
GIT_DEPTH: 0
|
||||||
|
script:
|
||||||
|
# 1. 初始化Git配置,复用提交者信息
|
||||||
|
- echo "当前提交者信息:$CI_COMMIT_AUTHOR"
|
||||||
|
- GIT_AUTHOR_NAME=$(echo "$CI_COMMIT_AUTHOR" | cut -d'<' -f1 | xargs)
|
||||||
|
- GIT_AUTHOR_EMAIL=$(echo "$CI_COMMIT_AUTHOR" | cut -d'<' -f2 | cut -d'>' -f1 | xargs)
|
||||||
|
- git config --global user.name "$GIT_AUTHOR_NAME"
|
||||||
|
- git config --global user.email "$GIT_AUTHOR_EMAIL"
|
||||||
|
- git config --global http.sslVerify false
|
||||||
|
# 核心新增:禁用HTTP2,强制使用HTTP/1.1(解决HTTP2 framing layer帧层错误)
|
||||||
|
- git config --global http.version HTTP/1.1
|
||||||
|
- git config --global http.proxy $PROXY_ADDR
|
||||||
|
- git config --global https.proxy $PROXY_ADDR
|
||||||
|
|
||||||
|
|
||||||
|
# 2. 构造带PAT的GitHub推送地址
|
||||||
|
- GITHUB_PUSH_URL="https://$GITHUB_PAT@${GITHUB_REPO_URL#https://}"
|
||||||
|
- echo "同步目标:GitHub仓库 $GITHUB_REPO_URL"
|
||||||
|
|
||||||
|
# 3. 安全处理GitHub远程仓库
|
||||||
|
- git remote rm github || true
|
||||||
|
- git remote add github "$GITHUB_PUSH_URL"
|
||||||
|
|
||||||
|
# 4. 核心修复:基于分离HEAD创建本地同名分支(解决refspec不匹配问题)
|
||||||
|
- echo "基于分离HEAD创建本地分支 $CI_COMMIT_BRANCH"
|
||||||
|
- git checkout -b $CI_COMMIT_BRANCH
|
||||||
|
|
||||||
|
# 5. 拉取GitHub远程最新代码(避免冲突,新分支拉取失败不影响)
|
||||||
|
- git fetch github || true
|
||||||
|
|
||||||
|
# 6. 单分支精准推送(GitHub自动创建不存在的分支)
|
||||||
|
- echo "开始同步GitLab分支 $CI_COMMIT_BRANCH 到GitHub..."
|
||||||
|
- git push github $CI_COMMIT_BRANCH:$CI_COMMIT_BRANCH -f
|
||||||
|
|
||||||
|
retry:
|
||||||
|
max: 2
|
||||||
|
when:
|
||||||
|
- runner_system_failure
|
||||||
|
- stuck_or_timeout_failure
|
||||||
|
- api_failure
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
<modules>
|
<modules>
|
||||||
<module>xiaoyi-auth</module>
|
<module>xiaoyi-auth</module>
|
||||||
<module>xiaoyi-framework</module>
|
<module>xiaoyi-framework</module>
|
||||||
|
<module>xiaoyi-gateway</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
@@ -41,7 +42,7 @@
|
|||||||
<jackson.version>2.16.1</jackson.version>
|
<jackson.version>2.16.1</jackson.version>
|
||||||
<mysql-connector-java.version>8.0.29</mysql-connector-java.version>
|
<mysql-connector-java.version>8.0.29</mysql-connector-java.version>
|
||||||
<mybatis-plus.version>3.5.5</mybatis-plus.version>
|
<mybatis-plus.version>3.5.5</mybatis-plus.version>
|
||||||
<druid.version>1.2.21</druid.version>
|
<druid.version>1.2.27</druid.version>
|
||||||
<flatten-maven-plugin.version>1.5.0</flatten-maven-plugin.version>
|
<flatten-maven-plugin.version>1.5.0</flatten-maven-plugin.version>
|
||||||
<jansi.version>1.18</jansi.version>
|
<jansi.version>1.18</jansi.version>
|
||||||
<sa-token.version>1.38.0</sa-token.version>
|
<sa-token.version>1.38.0</sa-token.version>
|
||||||
@@ -50,6 +51,7 @@
|
|||||||
<commons-lang3.version>3.12.0</commons-lang3.version>
|
<commons-lang3.version>3.12.0</commons-lang3.version>
|
||||||
<dypnsapi.version>2.0.0</dypnsapi.version>
|
<dypnsapi.version>2.0.0</dypnsapi.version>
|
||||||
<jasypt-starter.version>3.0.5</jasypt-starter.version>
|
<jasypt-starter.version>3.0.5</jasypt-starter.version>
|
||||||
|
<nacos-config.version>0.3.0-RC</nacos-config.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<!-- 统一依赖管理 -->
|
<!-- 统一依赖管理 -->
|
||||||
@@ -144,6 +146,11 @@
|
|||||||
<groupId>cn.dev33</groupId>
|
<groupId>cn.dev33</groupId>
|
||||||
<artifactId>sa-token-spring-boot3-starter</artifactId>
|
<artifactId>sa-token-spring-boot3-starter</artifactId>
|
||||||
<version>${sa-token.version}</version>
|
<version>${sa-token.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.dev33</groupId>
|
||||||
|
<artifactId>sa-token-reactor-spring-boot3-starter</artifactId>
|
||||||
|
<version>${sa-token.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
|
<!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -190,6 +197,12 @@
|
|||||||
<artifactId>jasypt-spring-boot-starter</artifactId>
|
<artifactId>jasypt-spring-boot-starter</artifactId>
|
||||||
<version>${jasypt-starter.version}</version>
|
<version>${jasypt-starter.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- Nacos配置中心 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.boot</groupId>
|
||||||
|
<artifactId>nacos-config-spring-boot-starter</artifactId>
|
||||||
|
<version>${nacos-config.version}</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
|
|
||||||
|
|||||||
@@ -96,6 +96,26 @@
|
|||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Nacos 配置中心 -->
|
||||||
|
<!-- <dependency> -->
|
||||||
|
<!-- <groupId>com.alibaba.boot</groupId> -->
|
||||||
|
<!-- <artifactId>nacos-config-spring-boot-starter</artifactId> -->
|
||||||
|
<!-- </dependency> -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-bootstrap</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!-- 服务注册发现 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package top.crushtj.xiaoyishu.auth;
|
package top.crushtj.xiaoyi.auth;
|
||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package top.crushtj.xiaoyi.auth.alarm;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import top.crushtj.xiaoyi.auth.alarm.impl.MailAlarmHelper;
|
||||||
|
import top.crushtj.xiaoyi.auth.alarm.impl.SmsAlarmHelper;
|
||||||
|
import top.crushtj.xiaoyi.auth.constant.XiaoyiAuthConstants;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ayi
|
||||||
|
* @version V1.0
|
||||||
|
* @title AlarmConfig
|
||||||
|
* @date 2026/2/2 15:37
|
||||||
|
* @description 告警配置类
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@RefreshScope
|
||||||
|
public class AlarmConfig {
|
||||||
|
|
||||||
|
@Value("${alarm.type}")
|
||||||
|
private String alarmType;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@RefreshScope
|
||||||
|
public AlarmInterface alarmHelper() {
|
||||||
|
// 根据配置文件中的告警类型,初始化选择不同的告警实现类
|
||||||
|
if (StringUtils.equals(XiaoyiAuthConstants.ALARM_TYPE_SMS, alarmType)) {
|
||||||
|
return new SmsAlarmHelper();
|
||||||
|
} else if (StringUtils.equals(XiaoyiAuthConstants.ALARM_TYPE_EMAIL, alarmType)) {
|
||||||
|
return new MailAlarmHelper();
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("错误的告警类型...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package top.crushtj.xiaoyi.auth.alarm;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ayi
|
||||||
|
* @version V1.0
|
||||||
|
* @title AlarmInterface
|
||||||
|
* @date 2026/2/2 15:37
|
||||||
|
* @description 告警接口
|
||||||
|
*/
|
||||||
|
|
||||||
|
public interface AlarmInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送告警信息
|
||||||
|
*
|
||||||
|
* @param message 告警信息
|
||||||
|
* @return 发送结果
|
||||||
|
*/
|
||||||
|
boolean send(String message);
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package top.crushtj.xiaoyi.auth.alarm.impl;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import top.crushtj.xiaoyi.auth.alarm.AlarmInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ayi
|
||||||
|
* @version V1.0
|
||||||
|
* @title MailAlarmHelper
|
||||||
|
* @date 2026/2/2 15:38
|
||||||
|
* @description 邮件告警通知类
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class MailAlarmHelper implements AlarmInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送告警信息
|
||||||
|
*
|
||||||
|
* @param message 告警信息
|
||||||
|
* @return 发送结果
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean send(String message) {
|
||||||
|
log.info("==> 【邮件告警】:{}", message);
|
||||||
|
|
||||||
|
// 业务逻辑...
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package top.crushtj.xiaoyi.auth.alarm.impl;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import top.crushtj.xiaoyi.auth.alarm.AlarmInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ayi
|
||||||
|
* @version V1.0
|
||||||
|
* @title SmsAlarmHelper
|
||||||
|
* @date 2026/2/2 15:39
|
||||||
|
* @description 短信告警通知类
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class SmsAlarmHelper implements AlarmInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送告警信息
|
||||||
|
*
|
||||||
|
* @param message 告警信息
|
||||||
|
* @return 发送结果
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean send(String message) {
|
||||||
|
log.info("==> 【短信告警】:{}", message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package top.crushtj.xiaoyishu.auth.config;
|
package top.crushtj.xiaoyi.auth.config;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package top.crushtj.xiaoyishu.auth.config;
|
package top.crushtj.xiaoyi.auth.config;
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
+2
-2
@@ -1,4 +1,4 @@
|
|||||||
package top.crushtj.xiaoyishu.auth.config;
|
package top.crushtj.xiaoyi.auth.config;
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
@@ -7,7 +7,7 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
|||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
|
||||||
import static top.crushtj.xiaoyishu.auth.constant.ConfigConstants.*;
|
import static top.crushtj.xiaoyi.auth.constant.ConfigConstants.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ayi
|
* @author ayi
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package top.crushtj.xiaoyishu.auth.constant;
|
package top.crushtj.xiaoyi.auth.constant;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ayi
|
* @author ayi
|
||||||
+7
-7
@@ -1,4 +1,4 @@
|
|||||||
package top.crushtj.xiaoyishu.auth.constant;
|
package top.crushtj.xiaoyi.auth.constant;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -48,21 +48,21 @@ public class RedisKeyConstants {
|
|||||||
/**
|
/**
|
||||||
* 构建用户角色数据 KEY
|
* 构建用户角色数据 KEY
|
||||||
*
|
*
|
||||||
* @param phone 用户手机号
|
* @param userId 用户手机号
|
||||||
* @return 用户角色数据 KEY
|
* @return 用户角色数据 KEY
|
||||||
*/
|
*/
|
||||||
public static String buildUserRolesKey(String phone) {
|
public static String buildUserRolesKey(Long userId) {
|
||||||
return USER_ROLES_KEY_PREFIX + phone;
|
return USER_ROLES_KEY_PREFIX + userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建角色权限数据 KEY
|
* 构建角色权限数据 KEY
|
||||||
*
|
*
|
||||||
* @param roleId 角色 ID
|
* @param roleKey 角色 ID
|
||||||
* @return 角色权限数据 KEY
|
* @return 角色权限数据 KEY
|
||||||
*/
|
*/
|
||||||
public static String buildRolePermissionsKey(Long roleId) {
|
public static String buildRolePermissionsKey(String roleKey) {
|
||||||
return ROLE_PERMISSIONS_KEY_PREFIX + roleId;
|
return ROLE_PERMISSIONS_KEY_PREFIX + roleKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package top.crushtj.xiaoyishu.auth.constant;
|
package top.crushtj.xiaoyi.auth.constant;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ayi
|
* @author ayi
|
||||||
+4
-1
@@ -1,4 +1,4 @@
|
|||||||
package top.crushtj.xiaoyishu.auth.constant;
|
package top.crushtj.xiaoyi.auth.constant;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ayi
|
* @author ayi
|
||||||
@@ -11,4 +11,7 @@ package top.crushtj.xiaoyishu.auth.constant;
|
|||||||
public class XiaoyiAuthConstants {
|
public class XiaoyiAuthConstants {
|
||||||
public static final String NICK_NAME_PREFIX = "咿呀_";
|
public static final String NICK_NAME_PREFIX = "咿呀_";
|
||||||
public static final Long XIAOYI_ID_INITIAL_VALUE = 1000000L;
|
public static final Long XIAOYI_ID_INITIAL_VALUE = 1000000L;
|
||||||
|
|
||||||
|
public static final String ALARM_TYPE_SMS = "sms";
|
||||||
|
public static final String ALARM_TYPE_EMAIL = "email";
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package top.crushtj.xiaoyi.auth.controller;
|
||||||
|
|
||||||
|
import com.alibaba.nacos.api.config.annotation.NacosValue;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import top.crushtj.xiaoyi.auth.alarm.AlarmInterface;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@RestController
|
||||||
|
public class TestController {
|
||||||
|
|
||||||
|
@NacosValue(value = "${rate-limit.api.limit}",autoRefreshed = true)
|
||||||
|
private Integer limit;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private AlarmInterface alarm;
|
||||||
|
|
||||||
|
@GetMapping("/test")
|
||||||
|
public String test() {
|
||||||
|
return "当前限流阈值为: " + limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/alarm")
|
||||||
|
public String sendAlarm() {
|
||||||
|
alarm.send("系统出错啦,犬小哈这个月绩效没了,速度上线解决问题!");
|
||||||
|
return "alarm success";
|
||||||
|
}
|
||||||
|
}
|
||||||
+13
-9
@@ -1,24 +1,21 @@
|
|||||||
package top.crushtj.xiaoyishu.auth.controller;
|
package top.crushtj.xiaoyi.auth.controller;
|
||||||
|
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
import top.crushtj.framework.biz.operationlog.aspect.ApiOperationLog;
|
import top.crushtj.framework.biz.operationlog.aspect.ApiOperationLog;
|
||||||
import top.crushtj.framework.common.response.Response;
|
import top.crushtj.framework.common.response.Response;
|
||||||
import top.crushtj.xiaoyishu.auth.model.vo.user.UserLoginReqVO;
|
import top.crushtj.xiaoyi.auth.model.vo.user.UserLoginReqVO;
|
||||||
import top.crushtj.xiaoyishu.auth.service.UserService;
|
import top.crushtj.xiaoyi.auth.service.UserService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ayi
|
* @author ayi
|
||||||
* @version V1.0
|
* @version V1.0
|
||||||
* @title UserController
|
* @title UserController
|
||||||
* @date 2026-01-18 20:40:21
|
* @date 2026-01-18 20:40:21
|
||||||
* @description 用户表(User)表控制层
|
* @description 用户表(User)表控制层
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@RestController
|
@RestController
|
||||||
@@ -33,5 +30,12 @@ public class UserController {
|
|||||||
public Response<String> loginOrRegister(@RequestBody @Validated UserLoginReqVO userLoginReqVO) {
|
public Response<String> loginOrRegister(@RequestBody @Validated UserLoginReqVO userLoginReqVO) {
|
||||||
return userService.loginOrRegister(userLoginReqVO);
|
return userService.loginOrRegister(userLoginReqVO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/logout")
|
||||||
|
@ApiOperationLog(description = "用户登出")
|
||||||
|
public Response<Void> logout(@RequestHeader("userId") String userId) {
|
||||||
|
// todo 实现用户登出逻辑
|
||||||
|
return Response.success();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
+3
-3
@@ -1,9 +1,9 @@
|
|||||||
package top.crushtj.xiaoyishu.auth.controller;
|
package top.crushtj.xiaoyi.auth.controller;
|
||||||
|
|
||||||
import top.crushtj.framework.biz.operationlog.aspect.ApiOperationLog;
|
import top.crushtj.framework.biz.operationlog.aspect.ApiOperationLog;
|
||||||
import top.crushtj.framework.common.response.Response;
|
import top.crushtj.framework.common.response.Response;
|
||||||
import top.crushtj.xiaoyishu.auth.model.vo.verificationcode.SendVerificationCodeReqVO;
|
import top.crushtj.xiaoyi.auth.model.vo.verificationcode.SendVerificationCodeReqVO;
|
||||||
import top.crushtj.xiaoyishu.auth.service.VerificationCodeService;
|
import top.crushtj.xiaoyi.auth.service.VerificationCodeService;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package top.crushtj.xiaoyishu.auth.domain.entity;
|
package top.crushtj.xiaoyi.auth.domain.entity;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableField;
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package top.crushtj.xiaoyishu.auth.domain.entity;
|
package top.crushtj.xiaoyi.auth.domain.entity;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableField;
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package top.crushtj.xiaoyishu.auth.domain.entity;
|
package top.crushtj.xiaoyi.auth.domain.entity;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableField;
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package top.crushtj.xiaoyishu.auth.domain.entity;
|
package top.crushtj.xiaoyi.auth.domain.entity;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableField;
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package top.crushtj.xiaoyishu.auth.domain.entity;
|
package top.crushtj.xiaoyi.auth.domain.entity;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableField;
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
package top.crushtj.xiaoyishu.auth.domain.mappers;
|
package top.crushtj.xiaoyi.auth.domain.mappers;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
import top.crushtj.xiaoyishu.auth.domain.entity.PermissionEntity;
|
import top.crushtj.xiaoyi.auth.domain.entity.PermissionEntity;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
+4
-2
@@ -1,7 +1,7 @@
|
|||||||
package top.crushtj.xiaoyishu.auth.domain.mappers;
|
package top.crushtj.xiaoyi.auth.domain.mappers;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
import top.crushtj.xiaoyishu.auth.domain.entity.RoleEntity;
|
import top.crushtj.xiaoyi.auth.domain.entity.RoleEntity;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -21,5 +21,7 @@ public interface RoleMapper extends BaseMapper<RoleEntity> {
|
|||||||
*/
|
*/
|
||||||
List<RoleEntity> selectEnabledRoleList();
|
List<RoleEntity> selectEnabledRoleList();
|
||||||
|
|
||||||
|
List<String> selectRoleKeyByUserId(Long userId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
package top.crushtj.xiaoyishu.auth.domain.mappers;
|
package top.crushtj.xiaoyi.auth.domain.mappers;
|
||||||
|
|
||||||
import org.apache.ibatis.annotations.Param;
|
import org.apache.ibatis.annotations.Param;
|
||||||
import top.crushtj.xiaoyishu.auth.domain.entity.RolePermissionRelEntity;
|
import top.crushtj.xiaoyi.auth.domain.entity.RolePermissionRelEntity;
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
package top.crushtj.xiaoyishu.auth.domain.mappers;
|
package top.crushtj.xiaoyi.auth.domain.mappers;
|
||||||
|
|
||||||
import org.apache.ibatis.annotations.Param;
|
import org.apache.ibatis.annotations.Param;
|
||||||
import top.crushtj.xiaoyishu.auth.domain.entity.UserEntity;
|
import top.crushtj.xiaoyi.auth.domain.entity.UserEntity;
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
package top.crushtj.xiaoyishu.auth.domain.mappers;
|
package top.crushtj.xiaoyi.auth.domain.mappers;
|
||||||
|
|
||||||
import top.crushtj.xiaoyishu.auth.domain.entity.UserRoleRelEntity;
|
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import top.crushtj.xiaoyi.auth.domain.entity.UserRoleRelEntity;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ayi
|
* @author ayi
|
||||||
+3
-3
@@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
<mapper namespace="top.crushtj.xiaoyishu.auth.domain.mappers.PermissionMapper">
|
<mapper namespace="top.crushtj.xiaoyi.auth.domain.mappers.PermissionMapper">
|
||||||
|
|
||||||
<resultMap type="top.crushtj.xiaoyishu.auth.domain.entity.PermissionEntity" id="PermissionMap">
|
<resultMap type="top.crushtj.xiaoyi.auth.domain.entity.PermissionEntity" id="PermissionMap">
|
||||||
<result property="id" column="id" jdbcType="INTEGER"/>
|
<result property="id" column="id" jdbcType="INTEGER"/>
|
||||||
<result property="parentId" column="parent_id" jdbcType="INTEGER"/>
|
<result property="parentId" column="parent_id" jdbcType="INTEGER"/>
|
||||||
<result property="name" column="name" jdbcType="VARCHAR"/>
|
<result property="name" column="name" jdbcType="VARCHAR"/>
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
|
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
|
||||||
<result property="isDeleted" column="is_deleted" jdbcType="BOOLEAN"/>
|
<result property="isDeleted" column="is_deleted" jdbcType="BOOLEAN"/>
|
||||||
</resultMap>
|
</resultMap>
|
||||||
<select id="selectAppEnabledList" resultType="top.crushtj.xiaoyishu.auth.domain.entity.PermissionEntity">
|
<select id="selectAppEnabledList" resultType="top.crushtj.xiaoyi.auth.domain.entity.PermissionEntity">
|
||||||
SELECT id, name, permission_key
|
SELECT id, name, permission_key
|
||||||
FROM t_permission p
|
FROM t_permission p
|
||||||
WHERE p.status = 1
|
WHERE p.status = 1
|
||||||
+9
-3
@@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
<mapper namespace="top.crushtj.xiaoyishu.auth.domain.mappers.RoleMapper">
|
<mapper namespace="top.crushtj.xiaoyi.auth.domain.mappers.RoleMapper">
|
||||||
|
|
||||||
<resultMap type="top.crushtj.xiaoyishu.auth.domain.entity.RoleEntity" id="RoleMap">
|
<resultMap type="top.crushtj.xiaoyi.auth.domain.entity.RoleEntity" id="RoleMap">
|
||||||
<result property="id" column="id" jdbcType="INTEGER"/>
|
<result property="id" column="id" jdbcType="INTEGER"/>
|
||||||
<result property="roleName" column="role_name" jdbcType="VARCHAR"/>
|
<result property="roleName" column="role_name" jdbcType="VARCHAR"/>
|
||||||
<result property="roleKey" column="role_key" jdbcType="VARCHAR"/>
|
<result property="roleKey" column="role_key" jdbcType="VARCHAR"/>
|
||||||
@@ -14,10 +14,16 @@
|
|||||||
<result property="isDeleted" column="is_deleted" jdbcType="BOOLEAN"/>
|
<result property="isDeleted" column="is_deleted" jdbcType="BOOLEAN"/>
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<select id="selectEnabledRoleList" resultType="top.crushtj.xiaoyishu.auth.domain.entity.RoleEntity">
|
<select id="selectEnabledRoleList" resultType="top.crushtj.xiaoyi.auth.domain.entity.RoleEntity">
|
||||||
SELECT id, role_name, role_key
|
SELECT id, role_name, role_key
|
||||||
FROM t_role
|
FROM t_role
|
||||||
WHERE status = 1
|
WHERE status = 1
|
||||||
AND is_deleted = 0
|
AND is_deleted = 0
|
||||||
</select>
|
</select>
|
||||||
|
<select id="selectRoleKeyByUserId" resultType="java.lang.String">
|
||||||
|
SELECT r.role_key
|
||||||
|
FROM t_role r
|
||||||
|
LEFT JOIN t_user_role_rel urr ON r.id = urr.role_id
|
||||||
|
WHERE urr.user_id = #{userId}
|
||||||
|
</select>
|
||||||
</mapper>
|
</mapper>
|
||||||
+3
-3
@@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
<mapper namespace="top.crushtj.xiaoyishu.auth.domain.mappers.RolePermissionRelMapper">
|
<mapper namespace="top.crushtj.xiaoyi.auth.domain.mappers.RolePermissionRelMapper">
|
||||||
|
|
||||||
<resultMap type="top.crushtj.xiaoyishu.auth.domain.entity.RolePermissionRelEntity" id="RolePermissionRelMap">
|
<resultMap type="top.crushtj.xiaoyi.auth.domain.entity.RolePermissionRelEntity" id="RolePermissionRelMap">
|
||||||
<result property="id" column="id" jdbcType="INTEGER"/>
|
<result property="id" column="id" jdbcType="INTEGER"/>
|
||||||
<result property="roleId" column="role_id" jdbcType="INTEGER"/>
|
<result property="roleId" column="role_id" jdbcType="INTEGER"/>
|
||||||
<result property="permissionId" column="permission_id" jdbcType="INTEGER"/>
|
<result property="permissionId" column="permission_id" jdbcType="INTEGER"/>
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
|
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
|
||||||
<result property="isDeleted" column="is_deleted" jdbcType="BOOLEAN"/>
|
<result property="isDeleted" column="is_deleted" jdbcType="BOOLEAN"/>
|
||||||
</resultMap>
|
</resultMap>
|
||||||
<select id="selectByRoleIds" resultType="top.crushtj.xiaoyishu.auth.domain.entity.RolePermissionRelEntity">
|
<select id="selectByRoleIds" resultType="top.crushtj.xiaoyi.auth.domain.entity.RolePermissionRelEntity">
|
||||||
SELECT role_id, permission_id FROM t_role_permission_rel
|
SELECT role_id, permission_id FROM t_role_permission_rel
|
||||||
<where>
|
<where>
|
||||||
<if test="roleIds!=null and !roleIds.empty">
|
<if test="roleIds!=null and !roleIds.empty">
|
||||||
+3
-3
@@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
<mapper namespace="top.crushtj.xiaoyishu.auth.domain.mappers.UserMapper">
|
<mapper namespace="top.crushtj.xiaoyi.auth.domain.mappers.UserMapper">
|
||||||
|
|
||||||
<resultMap type="top.crushtj.xiaoyishu.auth.domain.entity.UserEntity" id="UserMap">
|
<resultMap type="top.crushtj.xiaoyi.auth.domain.entity.UserEntity" id="UserMap">
|
||||||
<result property="id" column="id" jdbcType="INTEGER"/>
|
<result property="id" column="id" jdbcType="INTEGER"/>
|
||||||
<result property="xiaoyishuId" column="xiaoyishu_id" jdbcType="VARCHAR"/>
|
<result property="xiaoyishuId" column="xiaoyishu_id" jdbcType="VARCHAR"/>
|
||||||
<result property="password" column="password" jdbcType="VARCHAR"/>
|
<result property="password" column="password" jdbcType="VARCHAR"/>
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
|
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
|
||||||
<result property="isDeleted" column="is_deleted" jdbcType="BOOLEAN"/>
|
<result property="isDeleted" column="is_deleted" jdbcType="BOOLEAN"/>
|
||||||
</resultMap>
|
</resultMap>
|
||||||
<select id = "selectByPhone" resultType = "top.crushtj.xiaoyishu.auth.domain.entity.UserEntity">
|
<select id = "selectByPhone" resultType = "top.crushtj.xiaoyi.auth.domain.entity.UserEntity">
|
||||||
SELECT id, password, xiaoyishu_id
|
SELECT id, password, xiaoyishu_id
|
||||||
FROM t_user
|
FROM t_user
|
||||||
WHERE phone = #{phone}
|
WHERE phone = #{phone}
|
||||||
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
<mapper namespace="top.crushtj.xiaoyishu.auth.domain.mappers.UserRoleRelMapper">
|
<mapper namespace="top.crushtj.xiaoyi.auth.domain.mappers.UserRoleRelMapper">
|
||||||
|
|
||||||
<resultMap type="top.crushtj.xiaoyishu.auth.domain.entity.UserRoleRelEntity" id="UserRoleRelMap">
|
<resultMap type="top.crushtj.xiaoyi.auth.domain.entity.UserRoleRelEntity" id="UserRoleRelMap">
|
||||||
<result property="id" column="id" jdbcType="INTEGER"/>
|
<result property="id" column="id" jdbcType="INTEGER"/>
|
||||||
<result property="userId" column="user_id" jdbcType="INTEGER"/>
|
<result property="userId" column="user_id" jdbcType="INTEGER"/>
|
||||||
<result property="roleId" column="role_id" jdbcType="INTEGER"/>
|
<result property="roleId" column="role_id" jdbcType="INTEGER"/>
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package top.crushtj.xiaoyishu.auth.enums;
|
package top.crushtj.xiaoyi.auth.enums;
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package top.crushtj.xiaoyishu.auth.enums;
|
package top.crushtj.xiaoyi.auth.enums;
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
+2
-2
@@ -1,4 +1,4 @@
|
|||||||
package top.crushtj.xiaoyishu.auth.exception;
|
package top.crushtj.xiaoyi.auth.exception;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@@ -10,7 +10,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
|||||||
|
|
||||||
import top.crushtj.framework.common.exception.BizException;
|
import top.crushtj.framework.common.exception.BizException;
|
||||||
import top.crushtj.framework.common.response.Response;
|
import top.crushtj.framework.common.response.Response;
|
||||||
import top.crushtj.xiaoyishu.auth.enums.ResponseCodeEnum;
|
import top.crushtj.xiaoyi.auth.enums.ResponseCodeEnum;
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package top.crushtj.xiaoyishu.auth.model.vo.user;
|
package top.crushtj.xiaoyi.auth.model.vo.user;
|
||||||
|
|
||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package top.crushtj.xiaoyishu.auth.model.vo.verificationcode;
|
package top.crushtj.xiaoyi.auth.model.vo.verificationcode;
|
||||||
|
|
||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
+14
-14
@@ -1,4 +1,4 @@
|
|||||||
package top.crushtj.xiaoyishu.auth.runner;
|
package top.crushtj.xiaoyi.auth.runner;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
@@ -9,13 +9,13 @@ import org.springframework.boot.ApplicationRunner;
|
|||||||
import org.springframework.data.redis.core.RedisTemplate;
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import top.crushtj.framework.common.utils.JsonUtils;
|
import top.crushtj.framework.common.utils.JsonUtils;
|
||||||
import top.crushtj.xiaoyishu.auth.constant.RedisKeyConstants;
|
import top.crushtj.xiaoyi.auth.constant.RedisKeyConstants;
|
||||||
import top.crushtj.xiaoyishu.auth.domain.entity.PermissionEntity;
|
import top.crushtj.xiaoyi.auth.domain.entity.PermissionEntity;
|
||||||
import top.crushtj.xiaoyishu.auth.domain.entity.RoleEntity;
|
import top.crushtj.xiaoyi.auth.domain.entity.RoleEntity;
|
||||||
import top.crushtj.xiaoyishu.auth.domain.entity.RolePermissionRelEntity;
|
import top.crushtj.xiaoyi.auth.domain.entity.RolePermissionRelEntity;
|
||||||
import top.crushtj.xiaoyishu.auth.domain.mappers.PermissionMapper;
|
import top.crushtj.xiaoyi.auth.domain.mappers.PermissionMapper;
|
||||||
import top.crushtj.xiaoyishu.auth.domain.mappers.RoleMapper;
|
import top.crushtj.xiaoyi.auth.domain.mappers.RoleMapper;
|
||||||
import top.crushtj.xiaoyishu.auth.domain.mappers.RolePermissionRelMapper;
|
import top.crushtj.xiaoyi.auth.domain.mappers.RolePermissionRelMapper;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -88,27 +88,27 @@ public class PushRolePermissions2RedisRunner implements ApplicationRunner {
|
|||||||
.collect(Collectors.toMap(PermissionEntity::getId, permissionEntity -> permissionEntity));
|
.collect(Collectors.toMap(PermissionEntity::getId, permissionEntity -> permissionEntity));
|
||||||
|
|
||||||
// 构建角色 ID 和权限 DO 列表的映射关系
|
// 构建角色 ID 和权限 DO 列表的映射关系
|
||||||
Map<Long, List<PermissionEntity>> roleIdPermissionMap = new HashMap<>();
|
Map<String, List<String>> roleIdPermissionMap = new HashMap<>();
|
||||||
roleList.forEach(role -> {
|
roleList.forEach(role -> {
|
||||||
Long roleId = role.getId();
|
Long roleId = role.getId();
|
||||||
//获取角色对应的权限id列表
|
//获取角色对应的权限id列表
|
||||||
List<Long> permissionIds = roleIdPermissionIdsMap.get(roleId);
|
List<Long> permissionIds = roleIdPermissionIdsMap.get(roleId);
|
||||||
if (CollUtil.isNotEmpty(permissionIds)) {
|
if (CollUtil.isNotEmpty(permissionIds)) {
|
||||||
List<PermissionEntity> perDOS = Lists.newArrayList();
|
List<String> permissionKeys = Lists.newArrayList();
|
||||||
permissionIds.forEach(permissionId -> {
|
permissionIds.forEach(permissionId -> {
|
||||||
// 根据权限 ID 获取具体的权限 DO 对象
|
// 根据权限 ID 获取具体的权限 DO 对象
|
||||||
PermissionEntity permissionDO = permissionIdEntityMap.get(permissionId);
|
PermissionEntity permissionDO = permissionIdEntityMap.get(permissionId);
|
||||||
if (Objects.nonNull(permissionDO)) {
|
if (Objects.nonNull(permissionDO)) {
|
||||||
perDOS.add(permissionDO);
|
permissionKeys.add(permissionDO.getPermissionKey());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
roleIdPermissionMap.put(roleId, perDOS);
|
roleIdPermissionMap.put(role.getRoleKey(), permissionKeys);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 将角色 ID 和权限 DO 列表的映射关系写入 Redis
|
// 将角色 ID 和权限 DO 列表的映射关系写入 Redis
|
||||||
roleIdPermissionMap.forEach((roleId, permission) -> {
|
roleIdPermissionMap.forEach((roleKey, permission) -> {
|
||||||
String key = RedisKeyConstants.buildRolePermissionsKey(roleId);
|
String key = RedisKeyConstants.buildRolePermissionsKey(roleKey);
|
||||||
redisTemplate.opsForValue()
|
redisTemplate.opsForValue()
|
||||||
.set(key, JsonUtils.toJsonString(permission));
|
.set(key, JsonUtils.toJsonString(permission));
|
||||||
});
|
});
|
||||||
+5
-5
@@ -1,4 +1,4 @@
|
|||||||
package top.crushtj.xiaoyishu.auth.runner.cache;
|
package top.crushtj.xiaoyi.auth.runner.cache;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
@@ -6,11 +6,11 @@ import jakarta.annotation.Resource;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import top.crushtj.xiaoyishu.auth.domain.entity.UserEntity;
|
import top.crushtj.xiaoyi.auth.domain.entity.UserEntity;
|
||||||
import top.crushtj.xiaoyishu.auth.domain.mappers.UserMapper;
|
import top.crushtj.xiaoyi.auth.domain.mappers.UserMapper;
|
||||||
|
|
||||||
import static top.crushtj.xiaoyishu.auth.constant.RedisKeyConstants.XIAOYI_ID_GENERATOR_KEY;
|
import static top.crushtj.xiaoyi.auth.constant.RedisKeyConstants.XIAOYI_ID_GENERATOR_KEY;
|
||||||
import static top.crushtj.xiaoyishu.auth.constant.XiaoyiAuthConstants.XIAOYI_ID_INITIAL_VALUE;
|
import static top.crushtj.xiaoyi.auth.constant.XiaoyiAuthConstants.XIAOYI_ID_INITIAL_VALUE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ayi
|
* @author ayi
|
||||||
+3
-3
@@ -1,9 +1,9 @@
|
|||||||
package top.crushtj.xiaoyishu.auth.service;
|
package top.crushtj.xiaoyi.auth.service;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.service.IService;
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
import top.crushtj.framework.common.response.Response;
|
import top.crushtj.framework.common.response.Response;
|
||||||
import top.crushtj.xiaoyishu.auth.domain.entity.UserEntity;
|
import top.crushtj.xiaoyi.auth.domain.entity.UserEntity;
|
||||||
import top.crushtj.xiaoyishu.auth.model.vo.user.UserLoginReqVO;
|
import top.crushtj.xiaoyi.auth.model.vo.user.UserLoginReqVO;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ayi
|
* @author ayi
|
||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
package top.crushtj.xiaoyishu.auth.service;
|
package top.crushtj.xiaoyi.auth.service;
|
||||||
|
|
||||||
import top.crushtj.framework.common.response.Response;
|
import top.crushtj.framework.common.response.Response;
|
||||||
import top.crushtj.xiaoyishu.auth.model.vo.verificationcode.SendVerificationCodeReqVO;
|
import top.crushtj.xiaoyi.auth.model.vo.verificationcode.SendVerificationCodeReqVO;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
+47
-19
@@ -1,10 +1,9 @@
|
|||||||
package top.crushtj.xiaoyishu.auth.service.impl;
|
package top.crushtj.xiaoyi.auth.service.impl;
|
||||||
|
|
||||||
import cn.dev33.satoken.stp.SaTokenInfo;
|
import cn.dev33.satoken.stp.SaTokenInfo;
|
||||||
import cn.dev33.satoken.stp.StpUtil;
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
|
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
@@ -18,25 +17,27 @@ import top.crushtj.framework.common.response.Response;
|
|||||||
import top.crushtj.framework.common.utils.IdGenerator;
|
import top.crushtj.framework.common.utils.IdGenerator;
|
||||||
import top.crushtj.framework.common.utils.JsonUtils;
|
import top.crushtj.framework.common.utils.JsonUtils;
|
||||||
import top.crushtj.framework.common.utils.MaskUtils;
|
import top.crushtj.framework.common.utils.MaskUtils;
|
||||||
import top.crushtj.xiaoyishu.auth.constant.RedisKeyConstants;
|
import top.crushtj.xiaoyi.auth.constant.RedisKeyConstants;
|
||||||
import top.crushtj.xiaoyishu.auth.domain.entity.UserEntity;
|
import top.crushtj.xiaoyi.auth.domain.entity.RoleEntity;
|
||||||
import top.crushtj.xiaoyishu.auth.domain.entity.UserRoleRelEntity;
|
import top.crushtj.xiaoyi.auth.domain.entity.UserEntity;
|
||||||
import top.crushtj.xiaoyishu.auth.domain.mappers.UserMapper;
|
import top.crushtj.xiaoyi.auth.domain.entity.UserRoleRelEntity;
|
||||||
import top.crushtj.xiaoyishu.auth.domain.mappers.UserRoleRelMapper;
|
import top.crushtj.xiaoyi.auth.domain.mappers.RoleMapper;
|
||||||
import top.crushtj.xiaoyishu.auth.enums.LoginTypeEnum;
|
import top.crushtj.xiaoyi.auth.domain.mappers.UserMapper;
|
||||||
import top.crushtj.xiaoyishu.auth.enums.ResponseCodeEnum;
|
import top.crushtj.xiaoyi.auth.domain.mappers.UserRoleRelMapper;
|
||||||
import top.crushtj.xiaoyishu.auth.model.vo.user.UserLoginReqVO;
|
import top.crushtj.xiaoyi.auth.enums.LoginTypeEnum;
|
||||||
import top.crushtj.xiaoyishu.auth.service.UserService;
|
import top.crushtj.xiaoyi.auth.enums.ResponseCodeEnum;
|
||||||
|
import top.crushtj.xiaoyi.auth.model.vo.user.UserLoginReqVO;
|
||||||
|
import top.crushtj.xiaoyi.auth.service.UserService;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import static top.crushtj.xiaoyishu.auth.constant.RedisKeyConstants.XIAOYI_ID_GENERATOR_KEY;
|
import static top.crushtj.xiaoyi.auth.constant.RedisKeyConstants.XIAOYI_ID_GENERATOR_KEY;
|
||||||
import static top.crushtj.xiaoyishu.auth.constant.RoleConstants.COMMON_USER_ROLE_ID;
|
import static top.crushtj.xiaoyi.auth.constant.RoleConstants.COMMON_USER_ROLE_ID;
|
||||||
import static top.crushtj.xiaoyishu.auth.constant.XiaoyiAuthConstants.NICK_NAME_PREFIX;
|
import static top.crushtj.xiaoyi.auth.constant.XiaoyiAuthConstants.NICK_NAME_PREFIX;
|
||||||
import static top.crushtj.xiaoyishu.auth.constant.XiaoyiAuthConstants.XIAOYI_ID_INITIAL_VALUE;
|
import static top.crushtj.xiaoyi.auth.constant.XiaoyiAuthConstants.XIAOYI_ID_INITIAL_VALUE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ayi
|
* @author ayi
|
||||||
@@ -56,6 +57,9 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> impleme
|
|||||||
@Resource
|
@Resource
|
||||||
private UserRoleRelMapper userRoleRelMapper;
|
private UserRoleRelMapper userRoleRelMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private RoleMapper roleMapper;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private TransactionTemplate transactionTemplate;
|
private TransactionTemplate transactionTemplate;
|
||||||
|
|
||||||
@@ -103,6 +107,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> impleme
|
|||||||
} else {
|
} else {
|
||||||
// 已注册,则获取其用户 ID
|
// 已注册,则获取其用户 ID
|
||||||
userId = userEntity.getId();
|
userId = userEntity.getId();
|
||||||
|
this.loadUserRole(userId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -115,6 +120,12 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> impleme
|
|||||||
return Response.success(tokenInfo.tokenValue);
|
return Response.success(tokenInfo.tokenValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户注册
|
||||||
|
*
|
||||||
|
* @param phone 手机号
|
||||||
|
* @return 用户ID
|
||||||
|
*/
|
||||||
private Long registerUser(String phone) {
|
private Long registerUser(String phone) {
|
||||||
return transactionTemplate.execute(status -> {
|
return transactionTemplate.execute(status -> {
|
||||||
try {
|
try {
|
||||||
@@ -148,9 +159,10 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> impleme
|
|||||||
userRoleRelMapper.insert(userRoleRel);
|
userRoleRelMapper.insert(userRoleRel);
|
||||||
|
|
||||||
// 将用户角色信息存储到 Redis 中
|
// 将用户角色信息存储到 Redis 中
|
||||||
List<Long> roles = new ArrayList<>();
|
RoleEntity role = roleMapper.selectById(COMMON_USER_ROLE_ID);
|
||||||
roles.add(COMMON_USER_ROLE_ID);
|
List<String> roles = new ArrayList<>(1);
|
||||||
String userRolesKey = RedisKeyConstants.buildUserRolesKey(phone);
|
roles.add(role.getRoleKey());
|
||||||
|
String userRolesKey = RedisKeyConstants.buildUserRolesKey(userId);
|
||||||
redisTemplate.opsForValue()
|
redisTemplate.opsForValue()
|
||||||
.set(userRolesKey, JsonUtils.toJsonString(roles));
|
.set(userRolesKey, JsonUtils.toJsonString(roles));
|
||||||
return userId;
|
return userId;
|
||||||
@@ -161,7 +173,8 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> impleme
|
|||||||
// 回滚 redis 自增ID
|
// 回滚 redis 自增ID
|
||||||
Long decrement = Long.valueOf(Objects.requireNonNull(redisTemplate.opsForValue()
|
Long decrement = Long.valueOf(Objects.requireNonNull(redisTemplate.opsForValue()
|
||||||
.get(XIAOYI_ID_GENERATOR_KEY))
|
.get(XIAOYI_ID_GENERATOR_KEY))
|
||||||
.toString().trim());
|
.toString()
|
||||||
|
.trim());
|
||||||
if (decrement.compareTo(XIAOYI_ID_INITIAL_VALUE) > 0) {
|
if (decrement.compareTo(XIAOYI_ID_INITIAL_VALUE) > 0) {
|
||||||
log.error("==> 用户注册异常,回滚redis自增ID: {}", decrement);
|
log.error("==> 用户注册异常,回滚redis自增ID: {}", decrement);
|
||||||
redisTemplate.opsForValue()
|
redisTemplate.opsForValue()
|
||||||
@@ -171,5 +184,20 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> impleme
|
|||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载用户角色信息到缓存
|
||||||
|
*
|
||||||
|
* @param userId 用户ID
|
||||||
|
*/
|
||||||
|
private void loadUserRole(Long userId) {
|
||||||
|
String userRolesKey = RedisKeyConstants.buildUserRolesKey(userId);
|
||||||
|
Boolean hasKey = redisTemplate.hasKey(userRolesKey);
|
||||||
|
if (!hasKey) {
|
||||||
|
List<String> roles = roleMapper.selectRoleKeyByUserId(userId);
|
||||||
|
redisTemplate.opsForValue()
|
||||||
|
.set(userRolesKey, JsonUtils.toJsonString(roles));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
+7
-7
@@ -1,4 +1,4 @@
|
|||||||
package top.crushtj.xiaoyishu.auth.service.impl;
|
package top.crushtj.xiaoyi.auth.service.impl;
|
||||||
|
|
||||||
import cn.hutool.core.util.RandomUtil;
|
import cn.hutool.core.util.RandomUtil;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
@@ -9,15 +9,15 @@ import org.springframework.stereotype.Service;
|
|||||||
import top.crushtj.framework.common.exception.BizException;
|
import top.crushtj.framework.common.exception.BizException;
|
||||||
import top.crushtj.framework.common.response.Response;
|
import top.crushtj.framework.common.response.Response;
|
||||||
import top.crushtj.framework.common.utils.MaskUtils;
|
import top.crushtj.framework.common.utils.MaskUtils;
|
||||||
import top.crushtj.xiaoyishu.auth.constant.RedisKeyConstants;
|
import top.crushtj.xiaoyi.auth.constant.RedisKeyConstants;
|
||||||
import top.crushtj.xiaoyishu.auth.enums.ResponseCodeEnum;
|
import top.crushtj.xiaoyi.auth.enums.ResponseCodeEnum;
|
||||||
import top.crushtj.xiaoyishu.auth.model.vo.verificationcode.SendVerificationCodeReqVO;
|
import top.crushtj.xiaoyi.auth.model.vo.verificationcode.SendVerificationCodeReqVO;
|
||||||
import top.crushtj.xiaoyishu.auth.service.VerificationCodeService;
|
import top.crushtj.xiaoyi.auth.service.VerificationCodeService;
|
||||||
import top.crushtj.xiaoyishu.auth.sms.AliyunSmsHelper;
|
import top.crushtj.xiaoyi.auth.sms.AliyunSmsHelper;
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import static top.crushtj.xiaoyishu.auth.constant.RedisKeyConstants.VERIFICATION_CODE_EXPIRE_TIME;
|
import static top.crushtj.xiaoyi.auth.constant.RedisKeyConstants.VERIFICATION_CODE_EXPIRE_TIME;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 验证码服务实现类
|
* 验证码服务实现类
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package top.crushtj.xiaoyishu.auth.sms;
|
package top.crushtj.xiaoyi.auth.sms;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package top.crushtj.xiaoyishu.auth.sms;
|
package top.crushtj.xiaoyi.auth.sms;
|
||||||
|
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package top.crushtj.xiaoyishu.auth.sms;
|
package top.crushtj.xiaoyi.auth.sms;
|
||||||
|
|
||||||
import com.aliyun.dypnsapi20170525.models.SendSmsVerifyCodeResponse;
|
import com.aliyun.dypnsapi20170525.models.SendSmsVerifyCodeResponse;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
@@ -1,14 +1,10 @@
|
|||||||
server:
|
|
||||||
servlet:
|
|
||||||
context-path: /xiaoyishu-auth
|
|
||||||
|
|
||||||
spring:
|
spring:
|
||||||
datasource:
|
datasource:
|
||||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
# 数据库连接信息
|
# 数据库连接信息
|
||||||
url: ENC(+rm/FjvaL/fDxkZu/G9zxQnSO2M60VFodOuZzxqlxzUyNN2ooNjH9mtVVZiqlqMm8EHDCBKYD/rI4e1VdaDlYtwL+WxdW2K6rujuUm1R0Mkjl1kphthDaiAK5tnPvtfffL4aVpBbA7oOUdsX0tCURzWvpjSFhDHlxtb4b+Ezx6JeHJoZDEJ0bErjDETLdjbIGDNdCsTCvz2wp1S5+AdW39S5C8kj4PNLIXkbckzKdhHm9ATLrixdveyBLmuBkDwLsE9XBmCFm0ipST5/SVg8WA==)
|
url: ENC(oyIjmfAXMQ/z3uHl88N68RYJ96HJhdLkXMBBpE1wq9kSbYuTlxwZV/F7Qxa6tDAu+MD6I9swGqmYl2ormW/bzxPQdk+rrQANSQVA+ekyPGd97I5RGPfLLGQCw6FExrbhh9uzKGvekSmsIC8h91dTL2sMD8dYHbuRpW9J5knEyHSCZ90Bep1ZOlu2mWNQfdbaqmbafs6Bhjj9Iu4m9SblAMomAbDVT1oREPzjtMX640ro+Ou/YGVYPuLj3LlwVooauGCoswmHK0cA2TCD0RqaIg==)
|
||||||
username: ENC(B/DozYPqx155j/Gdh3QkCX/wGmuzLf1jumsbb1kF4GY6EjrlRdAatc8BlhrrR2J6)
|
username: ENC(Hf/tJoMig8FWFHopB7CMlQFuBhu61JbpA64JQWvqrmL+fTtGi6QNuI+sHBNY43nY)
|
||||||
password: ENC(JwQdlswsrwmGE6eplp6Jk9Vp5s6zlFgra6jLN5fKBt8qOyqQHUHCJM6JcKp5g0t8B/or2wTH18ohR/d41oyV9w==)
|
password: ENC(l2k707lyMR9ACwqEHl/d3VOojwmvIyRsH7leB9fa9ppOtbKBae8YnVVuEMcRhEqMTTvcqZ4Otd/TotqZMerL/w==)
|
||||||
druid: # Druid 连接池
|
druid: # Druid 连接池
|
||||||
initial-size: 5 # 初始化连接池大小
|
initial-size: 5 # 初始化连接池大小
|
||||||
min-idle: 5 # 最小连接池数量
|
min-idle: 5 # 最小连接池数量
|
||||||
@@ -43,7 +39,7 @@ spring:
|
|||||||
data:
|
data:
|
||||||
redis:
|
redis:
|
||||||
database: 0 # Redis 数据库索引(默认为 0)
|
database: 0 # Redis 数据库索引(默认为 0)
|
||||||
host: ENC(W8nbSo0vMjCenHHLHyhvdhW2wH3oI54/FW+LQp8H9lx6xQH2Tm56nGSgNom6xsK7) # Redis 服务器地址
|
host: ENC(C1TWXF+/HzWQBF25uXCdy/0fHoRDXdCW72+NKCIJURg4l3IDnJzl278KmFhfsusX) # Redis 服务器地址
|
||||||
port: 6379 # Redis 服务器连接端口
|
port: 6379 # Redis 服务器连接端口
|
||||||
password: ENC(iK/k0IGPflACqYMUwX4N/sGvCVuysYywLcAO+Ikeqk326V8hCr8dgEGzkiEIwWOo) # Redis 服务器连接密码(默认为空)
|
password: ENC(iK/k0IGPflACqYMUwX4N/sGvCVuysYywLcAO+Ikeqk326V8hCr8dgEGzkiEIwWOo) # Redis 服务器连接密码(默认为空)
|
||||||
timeout: 5s # 读超时时间
|
timeout: 5s # 读超时时间
|
||||||
@@ -82,4 +78,20 @@ jasypt:
|
|||||||
key-obtention-iterations: 1000
|
key-obtention-iterations: 1000
|
||||||
string-output-type: base64
|
string-output-type: base64
|
||||||
provider-name: SunJCE
|
provider-name: SunJCE
|
||||||
iv-generator-classname: org.jasypt.iv.RandomIvGenerator
|
iv-generator-classname: org.jasypt.iv.RandomIvGenerator
|
||||||
|
#nacos:
|
||||||
|
# config: # Nacos 配置中心
|
||||||
|
# access-key: # 身份验证
|
||||||
|
# secret-key: # 身份验证
|
||||||
|
# data-id: xiaoyishu-auth # 指定要加载的配置数据的 Data Id
|
||||||
|
# group: DEFAULT_GROUP # 指定配置数据所属的组
|
||||||
|
# type: yaml # 指定配置数据的格式
|
||||||
|
# server-addr: http://127.0.0.1:8848/ # 指定 Nacos 配置中心的服务器地址
|
||||||
|
# auto-refresh: true # 是否自动刷新配置
|
||||||
|
# remote-first: true # 是否优先使用远程配置
|
||||||
|
# bootstrap:
|
||||||
|
# enable: true # 启动时,预热配置
|
||||||
|
|
||||||
|
rate-limit:
|
||||||
|
api:
|
||||||
|
limit: 100 # 接口限流阈值
|
||||||
@@ -10,9 +10,11 @@ logging:
|
|||||||
############## Sa-Token 配置 ##############
|
############## Sa-Token 配置 ##############
|
||||||
sa-token:
|
sa-token:
|
||||||
# token 名称(同时也是 cookie 名称)
|
# token 名称(同时也是 cookie 名称)
|
||||||
token-name: satoken
|
token-name: Authorization
|
||||||
|
token-prefix: Bearer
|
||||||
# token 有效期(单位:秒) 默认30天,-1 代表永久有效
|
# token 有效期(单位:秒) 默认30天,-1 代表永久有效
|
||||||
timeout: 2592000
|
timeout: 2592000
|
||||||
|
# timeout: 60
|
||||||
# token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结
|
# token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结
|
||||||
active-timeout: -1
|
active-timeout: -1
|
||||||
# 是否允许同一账号多地同时登录 (为 true 时允许一起登录, 为 false 时新登录挤掉旧登录)
|
# 是否允许同一账号多地同时登录 (为 true 时允许一起登录, 为 false 时新登录挤掉旧登录)
|
||||||
@@ -20,6 +22,9 @@ sa-token:
|
|||||||
# 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token)
|
# 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token)
|
||||||
is-share: true
|
is-share: true
|
||||||
# token 风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik)
|
# token 风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik)
|
||||||
token-style: uuid
|
token-style: random-128
|
||||||
# 是否输出操作日志
|
# 是否输出操作日志
|
||||||
is-log: true
|
is-log: true
|
||||||
|
|
||||||
|
alarm:
|
||||||
|
type: sms # 告警方式,sms-短信,email-邮件
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
spring:
|
||||||
|
application:
|
||||||
|
name: xiaoyishu-auth # 应用名称
|
||||||
|
profiles:
|
||||||
|
active: dev # 默认激活 dev 本地开发环境
|
||||||
|
cloud:
|
||||||
|
nacos:
|
||||||
|
config:
|
||||||
|
server-addr: http://127.0.0.1:8848 # 指定 Nacos 配置中心的服务器地址
|
||||||
|
prefix: ${spring.application.name} # 配置 Data Id 前缀,这里使用应用名称作为前缀
|
||||||
|
group: DEFAULT_GROUP # 所属组
|
||||||
|
namespace: xiaoyishu # 命名空间
|
||||||
|
file-extension: yaml # 配置文件格式
|
||||||
|
refresh-enabled: true # 是否开启动态刷新
|
||||||
|
discovery:
|
||||||
|
enabled: true # 启用服务发现
|
||||||
|
group: DEFAULT_GROUP # 所属组
|
||||||
|
namespace: xiaoyishu # 命名空间
|
||||||
|
server-addr: 127.0.0.1:8848 # 指定 Nacos 配置中心的服务器地址
|
||||||
+35
-12
@@ -1,4 +1,4 @@
|
|||||||
package top.crushtj.xiaoyishu.auth;
|
package top.crushtj.xiaoyi.auth;
|
||||||
|
|
||||||
import com.alibaba.druid.filter.config.ConfigTools;
|
import com.alibaba.druid.filter.config.ConfigTools;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
@@ -64,23 +64,46 @@ public class EncryptTest {
|
|||||||
// 待加密的原始值
|
// 待加密的原始值
|
||||||
String encry1 = ""; // 待加密的原始值1
|
String encry1 = ""; // 待加密的原始值1
|
||||||
String encry2 = ""; // 待加密的原始值2
|
String encry2 = ""; // 待加密的原始值2
|
||||||
|
String encry3 = ""; // 待加密的原始值3
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String cipherAccessKeyId = encryptor.encrypt(encry1);
|
String cipherText1 = encryptor.encrypt(encry1);
|
||||||
System.out.println("字段1加密成功,密文:" + cipherAccessKeyId);
|
System.out.println("字段1加密成功,密文:" + cipherText1);
|
||||||
|
String cipherText2 = encryptor.encrypt(encry2);
|
||||||
String decryptAccessKeyId = encryptor.decrypt(cipherAccessKeyId);
|
System.out.println("字段2加密成功,密文:" + cipherText2);
|
||||||
System.out.println("字段1解密成功,明文:" + decryptAccessKeyId);
|
String cipherText3 = encryptor.encrypt(encry3);
|
||||||
|
System.out.println("字段2加密成功,密文:" + cipherText3);
|
||||||
String cipherAccessKeySecret = encryptor.encrypt(encry2);
|
|
||||||
System.out.println("字段2加密成功,密文:" + cipherAccessKeySecret);
|
|
||||||
|
|
||||||
String decryptAccessKeySecret = encryptor.decrypt(cipherAccessKeySecret);
|
|
||||||
System.out.println("字段2解密成功,明文:" + decryptAccessKeySecret);
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
System.out.println("加密失败原因:" + e.getMessage());
|
System.out.println("加密失败原因:" + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void dencrypt(){
|
||||||
|
StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
|
||||||
|
String cipherText1 = "";//密文
|
||||||
|
String cipherText2 = "";//密文
|
||||||
|
String cipherText3 = "";//密文
|
||||||
|
String password = "";//加密密码
|
||||||
|
encryptor.setPassword(password); // AES-256要求密钥至少32位
|
||||||
|
encryptor.setAlgorithm("PBEWithHMACSHA512AndAES_256"); // JDK17原生支持的算法
|
||||||
|
encryptor.setKeyObtentionIterations(1000); // 迭代次数(固定值)
|
||||||
|
encryptor.setStringOutputType("base64"); // 输出格式(固定)
|
||||||
|
encryptor.setProviderName("SunJCE"); // 加密提供者(JDK17默认)
|
||||||
|
encryptor.setIvGenerator(new RandomIvGenerator());
|
||||||
|
try {
|
||||||
|
String text1 = encryptor.decrypt(cipherText1);
|
||||||
|
System.out.println("字段1解密成功,明文:" + text1);
|
||||||
|
String text2 = encryptor.decrypt(cipherText2);
|
||||||
|
System.out.println("字段2解密成功,明文:" + text2);
|
||||||
|
String text3 = encryptor.decrypt(cipherText3);
|
||||||
|
System.out.println("字段2解密成功,明文:" + text3);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
System.out.println("加密失败原因:" + e.getMessage());
|
||||||
|
}// AES必须的IV生成器
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package top.crushtj.xiaoyishu.auth;
|
package top.crushtj.xiaoyi.auth;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package top.crushtj.xiaoyishu.auth;
|
package top.crushtj.xiaoyi.auth;
|
||||||
|
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>top.crushtj</groupId>
|
||||||
|
<artifactId>xiaoyishu</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>xiaoyi-gateway</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<name>${project.artifactId}</name>
|
||||||
|
<description>网关服务</description>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>top.crushtj</groupId>
|
||||||
|
<artifactId>xiaoyi-common</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-bootstrap</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 服务发现 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 网关 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-gateway</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 负载均衡 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Sa-Token 权限认证,在线文档:https://sa-token.cc -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.dev33</groupId>
|
||||||
|
<artifactId>sa-token-reactor-spring-boot3-starter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.dev33</groupId>
|
||||||
|
<artifactId>sa-token-redis-jackson</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 提供Redis连接池 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-pool2</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- jasypt 加密工具 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.ulisesbocchio</groupId>
|
||||||
|
<artifactId>jasypt-spring-boot-starter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Redis -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Jackson 组件 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>top.crushtj</groupId>
|
||||||
|
<artifactId>xiaoyi-spring-boot-starter-jackson</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package top.crushtj.xiaoyi.gateway;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ayi
|
||||||
|
* @version V1.0
|
||||||
|
* @title XiaoyiGatewayApplication
|
||||||
|
* @date 2026/2/2 16:46
|
||||||
|
* @description 网关启动类
|
||||||
|
*/
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class XiaoyiGatewayApplication {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(XiaoyiGatewayApplication.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package top.crushtj.xiaoyi.gateway.auth;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.exception.NotLoginException;
|
||||||
|
import cn.dev33.satoken.exception.NotPermissionException;
|
||||||
|
import cn.dev33.satoken.exception.NotRoleException;
|
||||||
|
import cn.dev33.satoken.reactor.filter.SaReactorFilter;
|
||||||
|
import cn.dev33.satoken.router.SaRouter;
|
||||||
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ayi
|
||||||
|
* @version V1.0
|
||||||
|
* @title SaTokenConfigure
|
||||||
|
* @date 2026/2/2 17:16
|
||||||
|
* @description SaToken配置类
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class SaTokenConfigure {
|
||||||
|
// 注册 Sa-Token全局过滤器
|
||||||
|
@Bean
|
||||||
|
public SaReactorFilter getSaReactorFilter() {
|
||||||
|
return new SaReactorFilter()
|
||||||
|
// 拦截地址
|
||||||
|
.addInclude("/**") /* 拦截全部path */
|
||||||
|
// 鉴权方法:每次访问进入
|
||||||
|
.setAuth(obj -> {
|
||||||
|
// 登录校验
|
||||||
|
SaRouter.match("/**") // 拦截所有路由
|
||||||
|
.notMatch("/auth/user/login") // 排除登录接口
|
||||||
|
.notMatch("/auth/verification/code/send") // 排除验证码发送接口
|
||||||
|
.check(r -> StpUtil.checkLogin()) // 校验是否登录
|
||||||
|
;
|
||||||
|
// SaRouter.match("/auth/user/logout", r -> StpUtil.checkPermission("user"));
|
||||||
|
// 权限认证 -- 不同模块, 校验不同权限
|
||||||
|
// SaRouter.match("/user/**", r -> StpUtil.checkPermission("user"));
|
||||||
|
// SaRouter.match("/admin/**", r -> StpUtil.checkPermission("admin"));
|
||||||
|
// SaRouter.match("/goods/**", r -> StpUtil.checkPermission("goods"));
|
||||||
|
// SaRouter.match("/orders/**", r -> StpUtil.checkPermission("orders"));
|
||||||
|
|
||||||
|
// 更多匹配 ... */
|
||||||
|
})
|
||||||
|
.setError(e -> {
|
||||||
|
if (e instanceof NotLoginException) { // 未登录异常
|
||||||
|
throw new NotLoginException(e.getMessage(), null, null);
|
||||||
|
} else if (e instanceof NotPermissionException || e instanceof NotRoleException) { // 权限不足,或不具备角色,统一抛出权限不足异常
|
||||||
|
throw new NotPermissionException(e.getMessage());
|
||||||
|
} else { // 其他异常,则抛出一个运行时异常
|
||||||
|
throw new RuntimeException(e.getMessage());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
package top.crushtj.xiaoyi.gateway.auth;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.stp.StpInterface;
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import top.crushtj.xiaoyi.gateway.constant.RedisKeyConstants;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ayi
|
||||||
|
* @version V1.0
|
||||||
|
* @title StpInterfaceImpl
|
||||||
|
* @date 2026/2/2 17:14
|
||||||
|
* @description 自定义权限验证接口
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class StpInterfaceImpl implements StpInterface {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private RedisTemplate<String, String> redisTemplate;
|
||||||
|
@Resource
|
||||||
|
private ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
@Override
|
||||||
|
public List<String> getPermissionList(Object loginId, String loginType) {
|
||||||
|
log.info("## 获取用户权限列表, loginId: {}", loginId);
|
||||||
|
// 构建 用户-角色 Redis Key
|
||||||
|
String userRolesKey = RedisKeyConstants.buildUserRoleKey(Long.valueOf(loginId.toString()));
|
||||||
|
|
||||||
|
// 根据用户 ID ,从 Redis 中获取该用户的角色集合
|
||||||
|
String useRolesValue = redisTemplate.opsForValue()
|
||||||
|
.get(userRolesKey);
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(useRolesValue)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将 JSON 字符串转换为 List<String> 角色集合
|
||||||
|
List<String> userRoleKeys = objectMapper.readValue(useRolesValue, new TypeReference<>() {
|
||||||
|
});
|
||||||
|
|
||||||
|
if (CollUtil.isNotEmpty(userRoleKeys)) {
|
||||||
|
// 查询这些角色对应的权限
|
||||||
|
// 构建 角色-权限 Redis Key 集合
|
||||||
|
List<String> rolePermissionsKeys = userRoleKeys.stream()
|
||||||
|
.map(RedisKeyConstants::buildRolePermissionsKey)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
// 通过 key 集合批量查询权限,提升查询性能。
|
||||||
|
List<String> rolePermissionsValues = Objects.requireNonNull(redisTemplate.opsForValue()
|
||||||
|
.multiGet(rolePermissionsKeys))
|
||||||
|
.stream()
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
if (CollUtil.isNotEmpty(rolePermissionsValues)) {
|
||||||
|
List<String> permissions = Lists.newArrayList();
|
||||||
|
|
||||||
|
// 遍历所有角色的权限集合,统一添加到 permissions 集合中
|
||||||
|
rolePermissionsValues.forEach(jsonValue -> {
|
||||||
|
try {
|
||||||
|
// 将 JSON 字符串转换为 List<String> 权限集合
|
||||||
|
List<String> rolePermissions = objectMapper.readValue(jsonValue, new TypeReference<>() {
|
||||||
|
});
|
||||||
|
permissions.addAll(rolePermissions);
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
log.error("==> JSON 解析错误: ", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 返回此用户所拥有的权限
|
||||||
|
return permissions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
@Override
|
||||||
|
public List<String> getRoleList(Object loginId, String loginType) {
|
||||||
|
log.info("## 获取用户角色列表, loginId: {}", loginId);
|
||||||
|
String userRolesKey = RedisKeyConstants.buildUserRoleKey(Long.valueOf(loginId.toString()));
|
||||||
|
String useRolesValue = redisTemplate.opsForValue()
|
||||||
|
.get(userRolesKey);
|
||||||
|
if (StringUtils.isBlank(useRolesValue)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将 JSON 字符串转换为 List<String> 集合
|
||||||
|
return objectMapper.readValue(useRolesValue, new TypeReference<>() {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
+41
@@ -0,0 +1,41 @@
|
|||||||
|
package top.crushtj.xiaoyi.gateway.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||||
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
|
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
|
||||||
|
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||||
|
import org.springframework.lang.NonNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author ayi
|
||||||
|
* @version V1.0
|
||||||
|
* @title RedisTemplateConfig
|
||||||
|
* @description Redis 配置类
|
||||||
|
* @date 2026/01/12 19:13
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class RedisTemplateConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public RedisTemplate<String, Object> redisTemplate(@NonNull RedisConnectionFactory connectionFactory) {
|
||||||
|
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
|
||||||
|
// 设置 RedisTemplate 的连接工厂
|
||||||
|
redisTemplate.setConnectionFactory(connectionFactory);
|
||||||
|
|
||||||
|
// 使用 StringRedisSerializer 来序列化和反序列化 redis 的 key 值,确保 key 是可读的字符串
|
||||||
|
redisTemplate.setKeySerializer(new StringRedisSerializer());
|
||||||
|
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
|
||||||
|
|
||||||
|
// 使用 Jackson2JsonRedisSerializer 来序列化和反序列化 redis 的 value 值, 确保存储的是 JSON 格式
|
||||||
|
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
|
||||||
|
redisTemplate.setValueSerializer(serializer);
|
||||||
|
redisTemplate.setHashValueSerializer(serializer);
|
||||||
|
|
||||||
|
redisTemplate.afterPropertiesSet();
|
||||||
|
return redisTemplate;
|
||||||
|
}
|
||||||
|
}
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
package top.crushtj.xiaoyi.gateway.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ayi
|
||||||
|
* @version V1.0
|
||||||
|
* @title RedisKeyConstants
|
||||||
|
* @date 2026/2/3 15:24
|
||||||
|
* @description Redis key 常量
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class RedisKeyConstants {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户对应角色集合 KEY 前缀
|
||||||
|
*/
|
||||||
|
private static final String USER_ROLES_KEY_PREFIX = "user:roles:";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色对应的权限集合 KEY 前缀
|
||||||
|
*/
|
||||||
|
private static final String ROLE_PERMISSIONS_KEY_PREFIX = "role:permissions:";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建角色对应的权限集合 KEY
|
||||||
|
*
|
||||||
|
* @param roleKey 角色 key
|
||||||
|
* @return 角色对应的权限集合 KEY
|
||||||
|
*/
|
||||||
|
public static String buildRolePermissionsKey(String roleKey) {
|
||||||
|
return ROLE_PERMISSIONS_KEY_PREFIX + roleKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建用户-角色 KEY
|
||||||
|
*
|
||||||
|
* @param userId 用户 ID
|
||||||
|
* @return 用户-角色 KEY
|
||||||
|
*/
|
||||||
|
public static String buildUserRoleKey(Long userId) {
|
||||||
|
return USER_ROLES_KEY_PREFIX + userId;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package top.crushtj.xiaoyi.gateway.enums;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import top.crushtj.framework.common.exception.BaseExceptionInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ayi
|
||||||
|
* @version V1.0
|
||||||
|
* @title ResponseCodeEnum
|
||||||
|
* @date 2026/2/3 16:15
|
||||||
|
* @description 异常状态吗枚举
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum ResponseCodeEnum implements BaseExceptionInterface {
|
||||||
|
// ----------- 通用异常状态码 -----------
|
||||||
|
SYSTEM_ERROR("500", "系统繁忙,请稍后再试"),
|
||||||
|
UNAUTHORIZED("401", "没有权限"),
|
||||||
|
|
||||||
|
// ----------- 业务异常状态码 -----------
|
||||||
|
;
|
||||||
|
|
||||||
|
// 异常码
|
||||||
|
private final String errorCode;
|
||||||
|
// 错误信息
|
||||||
|
private final String errorMessage;
|
||||||
|
}
|
||||||
+78
@@ -0,0 +1,78 @@
|
|||||||
|
package top.crushtj.xiaoyi.gateway.exception;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.exception.NotLoginException;
|
||||||
|
import cn.dev33.satoken.exception.NotPermissionException;
|
||||||
|
import cn.dev33.satoken.exception.SaTokenException;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
|
||||||
|
import org.springframework.core.io.buffer.DataBufferFactory;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
import top.crushtj.framework.common.response.Response;
|
||||||
|
import top.crushtj.xiaoyi.gateway.enums.ResponseCodeEnum;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ayi
|
||||||
|
* @version V1.0
|
||||||
|
* @title GlobalExceptionHandler
|
||||||
|
* @date 2026/2/3 16:17
|
||||||
|
* @description 全局异常处理
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class GlobalExceptionHandler implements ErrorWebExceptionHandler {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
|
||||||
|
// 获取响应对象
|
||||||
|
ServerHttpResponse response = exchange.getResponse();
|
||||||
|
|
||||||
|
log.error("==> 全局异常捕获: ", ex);
|
||||||
|
|
||||||
|
// 响参
|
||||||
|
Response<?> result;
|
||||||
|
// 根据捕获的异常类型,设置不同的响应状态码和响应消息
|
||||||
|
if (ex instanceof SaTokenException) { // Sa-Token 异常
|
||||||
|
// 权限认证失败时,设置 401 状态码
|
||||||
|
response.setStatusCode(HttpStatus.UNAUTHORIZED);
|
||||||
|
if (ex instanceof NotLoginException){
|
||||||
|
// 构建响应结果
|
||||||
|
result = Response.failure(ResponseCodeEnum.UNAUTHORIZED.getErrorCode(),
|
||||||
|
ex.getMessage());
|
||||||
|
}else if (ex instanceof NotPermissionException) {
|
||||||
|
result = Response.failure(ResponseCodeEnum.UNAUTHORIZED.getErrorCode(),
|
||||||
|
ResponseCodeEnum.UNAUTHORIZED.getErrorMessage());
|
||||||
|
} else {
|
||||||
|
result = Response.failure(ResponseCodeEnum.SYSTEM_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else { // 其他异常,则统一提示 “系统繁忙” 错误
|
||||||
|
result = Response.failure(ResponseCodeEnum.SYSTEM_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置响应头的内容类型为 application/json;charset=UTF-8,表示响应体为 JSON 格式
|
||||||
|
response.getHeaders()
|
||||||
|
.setContentType(MediaType.APPLICATION_JSON_UTF8);
|
||||||
|
// 设置 body 响应体
|
||||||
|
return response.writeWith(Mono.fromSupplier(() -> { // 使用 Mono.fromSupplier 创建响应体
|
||||||
|
DataBufferFactory bufferFactory = response.bufferFactory();
|
||||||
|
try {
|
||||||
|
// 使用 ObjectMapper 将 result 对象转换为 JSON 字节数组
|
||||||
|
return bufferFactory.wrap(objectMapper.writeValueAsBytes(result));
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 如果转换过程中出现异常,则返回空字节数组
|
||||||
|
return bufferFactory.wrap(new byte[0]);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
package top.crushtj.xiaoyi.gateway.filter;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
|
||||||
|
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ayi
|
||||||
|
* @version V1.0
|
||||||
|
* @title AddUserId2HeaderFilter
|
||||||
|
* @date 2026/2/4 15:28
|
||||||
|
* @description 添加用户ID到请求头过滤器
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class AddUserId2HeaderFilter implements GlobalFilter {
|
||||||
|
|
||||||
|
private static final String HEADER_USER_ID = "userId";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||||
|
Long userId = null;
|
||||||
|
try {
|
||||||
|
userId = StpUtil.getLoginIdAsLong();
|
||||||
|
} catch (Exception e) {
|
||||||
|
return chain.filter(exchange);
|
||||||
|
}
|
||||||
|
Long finalUserId = userId;
|
||||||
|
ServerWebExchange newExchange = exchange.mutate()
|
||||||
|
.request(builder -> builder.header(HEADER_USER_ID, String.valueOf(finalUserId)))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
return chain.filter(newExchange);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
server:
|
||||||
|
port: 8000 # 指定启动端口
|
||||||
|
spring:
|
||||||
|
cloud:
|
||||||
|
gateway:
|
||||||
|
routes:
|
||||||
|
- id: auth
|
||||||
|
uri: lb://xiaoyishu-auth
|
||||||
|
predicates:
|
||||||
|
- Path=/auth/**
|
||||||
|
filters:
|
||||||
|
- StripPrefix=1
|
||||||
|
data:
|
||||||
|
redis:
|
||||||
|
database: 0 # Redis 数据库索引(默认为 0)
|
||||||
|
host: ENC(C1TWXF+/HzWQBF25uXCdy/0fHoRDXdCW72+NKCIJURg4l3IDnJzl278KmFhfsusX) # Redis 服务器地址
|
||||||
|
port: 6379 # Redis 服务器连接端口
|
||||||
|
password: ENC(iK/k0IGPflACqYMUwX4N/sGvCVuysYywLcAO+Ikeqk326V8hCr8dgEGzkiEIwWOo) # Redis 服务器连接密码(默认为空)
|
||||||
|
timeout: 5s # 读超时时间
|
||||||
|
connect-timeout: 5s # 链接超时时间
|
||||||
|
lettuce:
|
||||||
|
pool:
|
||||||
|
max-active: 200 # 连接池最大连接数
|
||||||
|
max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制)
|
||||||
|
min-idle: 0 # 连接池中的最小空闲连接
|
||||||
|
max-idle: 10 # 连接池中的最大空闲连接
|
||||||
|
|
||||||
|
############## Sa-Token 配置 ##############
|
||||||
|
sa-token:
|
||||||
|
# token 名称(同时也是 cookie 名称)
|
||||||
|
token-name: Authorization
|
||||||
|
token-prefix: Bearer
|
||||||
|
# token 有效期(单位:秒) 默认30天,-1 代表永久有效
|
||||||
|
timeout: 2592000
|
||||||
|
# timeout: 60
|
||||||
|
# token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结
|
||||||
|
active-timeout: -1
|
||||||
|
# 是否允许同一账号多地同时登录 (为 true 时允许一起登录, 为 false 时新登录挤掉旧登录)
|
||||||
|
is-concurrent: true
|
||||||
|
# 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token)
|
||||||
|
is-share: true
|
||||||
|
# token 风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik)
|
||||||
|
token-style: random-128
|
||||||
|
# 是否输出操作日志
|
||||||
|
is-log: true
|
||||||
|
|
||||||
|
jasypt:
|
||||||
|
encryptor:
|
||||||
|
password:
|
||||||
|
algorithm: PBEWithHMACSHA512AndAES_256
|
||||||
|
key-obtention-iterations: 1000
|
||||||
|
string-output-type: base64
|
||||||
|
provider-name: SunJCE
|
||||||
|
iv-generator-classname: org.jasypt.iv.RandomIvGenerator
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
spring:
|
||||||
|
application:
|
||||||
|
name: xiaoyi-gateway # 应用名称
|
||||||
|
profiles:
|
||||||
|
active: dev
|
||||||
|
cloud:
|
||||||
|
nacos:
|
||||||
|
discovery:
|
||||||
|
enabled: true # 启用服务发现
|
||||||
|
group: DEFAULT_GROUP # 所属组
|
||||||
|
namespace: xiaoyishu # 命名空间
|
||||||
|
server-addr: 127.0.0.1:8848 # Nacos 服务器地址
|
||||||
Reference in New Issue
Block a user