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>
|
||||
<module>xiaoyi-auth</module>
|
||||
<module>xiaoyi-framework</module>
|
||||
<module>xiaoyi-gateway</module>
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
@@ -41,7 +42,7 @@
|
||||
<jackson.version>2.16.1</jackson.version>
|
||||
<mysql-connector-java.version>8.0.29</mysql-connector-java.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>
|
||||
<jansi.version>1.18</jansi.version>
|
||||
<sa-token.version>1.38.0</sa-token.version>
|
||||
@@ -50,6 +51,7 @@
|
||||
<commons-lang3.version>3.12.0</commons-lang3.version>
|
||||
<dypnsapi.version>2.0.0</dypnsapi.version>
|
||||
<jasypt-starter.version>3.0.5</jasypt-starter.version>
|
||||
<nacos-config.version>0.3.0-RC</nacos-config.version>
|
||||
</properties>
|
||||
|
||||
<!-- 统一依赖管理 -->
|
||||
@@ -144,6 +146,11 @@
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot3-starter</artifactId>
|
||||
<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>
|
||||
<!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
|
||||
<dependency>
|
||||
@@ -190,6 +197,12 @@
|
||||
<artifactId>jasypt-spring-boot-starter</artifactId>
|
||||
<version>${jasypt-starter.version}</version>
|
||||
</dependency>
|
||||
<!-- Nacos配置中心 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.boot</groupId>
|
||||
<artifactId>nacos-config-spring-boot-starter</artifactId>
|
||||
<version>${nacos-config.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
|
||||
@@ -96,6 +96,26 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</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>
|
||||
|
||||
<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.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.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.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.Configuration;
|
||||
@@ -7,7 +7,7 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
import static top.crushtj.xiaoyishu.auth.constant.ConfigConstants.*;
|
||||
import static top.crushtj.xiaoyi.auth.constant.ConfigConstants.*;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package top.crushtj.xiaoyishu.auth.constant;
|
||||
package top.crushtj.xiaoyi.auth.constant;
|
||||
|
||||
/**
|
||||
* @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
|
||||
*
|
||||
* @param phone 用户手机号
|
||||
* @param userId 用户手机号
|
||||
* @return 用户角色数据 KEY
|
||||
*/
|
||||
public static String buildUserRolesKey(String phone) {
|
||||
return USER_ROLES_KEY_PREFIX + phone;
|
||||
public static String buildUserRolesKey(Long userId) {
|
||||
return USER_ROLES_KEY_PREFIX + userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建角色权限数据 KEY
|
||||
*
|
||||
* @param roleId 角色 ID
|
||||
* @param roleKey 角色 ID
|
||||
* @return 角色权限数据 KEY
|
||||
*/
|
||||
public static String buildRolePermissionsKey(Long roleId) {
|
||||
return ROLE_PERMISSIONS_KEY_PREFIX + roleId;
|
||||
public static String buildRolePermissionsKey(String roleKey) {
|
||||
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
|
||||
+4
-1
@@ -1,4 +1,4 @@
|
||||
package top.crushtj.xiaoyishu.auth.constant;
|
||||
package top.crushtj.xiaoyi.auth.constant;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
@@ -11,4 +11,7 @@ package top.crushtj.xiaoyishu.auth.constant;
|
||||
public class XiaoyiAuthConstants {
|
||||
public static final String NICK_NAME_PREFIX = "咿呀_";
|
||||
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";
|
||||
}
|
||||
}
|
||||
+11
-7
@@ -1,16 +1,13 @@
|
||||
package top.crushtj.xiaoyishu.auth.controller;
|
||||
package top.crushtj.xiaoyi.auth.controller;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import top.crushtj.framework.biz.operationlog.aspect.ApiOperationLog;
|
||||
import top.crushtj.framework.common.response.Response;
|
||||
import top.crushtj.xiaoyishu.auth.model.vo.user.UserLoginReqVO;
|
||||
import top.crushtj.xiaoyishu.auth.service.UserService;
|
||||
import top.crushtj.xiaoyi.auth.model.vo.user.UserLoginReqVO;
|
||||
import top.crushtj.xiaoyi.auth.service.UserService;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
@@ -33,5 +30,12 @@ public class UserController {
|
||||
public Response<String> loginOrRegister(@RequestBody @Validated UserLoginReqVO 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.common.response.Response;
|
||||
import top.crushtj.xiaoyishu.auth.model.vo.verificationcode.SendVerificationCodeReqVO;
|
||||
import top.crushtj.xiaoyishu.auth.service.VerificationCodeService;
|
||||
import top.crushtj.xiaoyi.auth.model.vo.verificationcode.SendVerificationCodeReqVO;
|
||||
import top.crushtj.xiaoyi.auth.service.VerificationCodeService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
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.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.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.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.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.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 top.crushtj.xiaoyishu.auth.domain.entity.PermissionEntity;
|
||||
import top.crushtj.xiaoyi.auth.domain.entity.PermissionEntity;
|
||||
|
||||
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 top.crushtj.xiaoyishu.auth.domain.entity.RoleEntity;
|
||||
import top.crushtj.xiaoyi.auth.domain.entity.RoleEntity;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -21,5 +21,7 @@ public interface RoleMapper extends BaseMapper<RoleEntity> {
|
||||
*/
|
||||
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 top.crushtj.xiaoyishu.auth.domain.entity.RolePermissionRelEntity;
|
||||
import top.crushtj.xiaoyi.auth.domain.entity.RolePermissionRelEntity;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
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 top.crushtj.xiaoyishu.auth.domain.entity.UserEntity;
|
||||
import top.crushtj.xiaoyi.auth.domain.entity.UserEntity;
|
||||
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 top.crushtj.xiaoyi.auth.domain.entity.UserRoleRelEntity;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
+3
-3
@@ -1,8 +1,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">
|
||||
<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="parentId" column="parent_id" jdbcType="INTEGER"/>
|
||||
<result property="name" column="name" jdbcType="VARCHAR"/>
|
||||
@@ -16,7 +16,7 @@
|
||||
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
|
||||
<result property="isDeleted" column="is_deleted" jdbcType="BOOLEAN"/>
|
||||
</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
|
||||
FROM t_permission p
|
||||
WHERE p.status = 1
|
||||
+9
-3
@@ -1,8 +1,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">
|
||||
<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="roleName" column="role_name" jdbcType="VARCHAR"/>
|
||||
<result property="roleKey" column="role_key" jdbcType="VARCHAR"/>
|
||||
@@ -14,10 +14,16 @@
|
||||
<result property="isDeleted" column="is_deleted" jdbcType="BOOLEAN"/>
|
||||
</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
|
||||
FROM t_role
|
||||
WHERE status = 1
|
||||
AND is_deleted = 0
|
||||
</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>
|
||||
+3
-3
@@ -1,8 +1,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">
|
||||
<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="roleId" column="role_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="isDeleted" column="is_deleted" jdbcType="BOOLEAN"/>
|
||||
</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
|
||||
<where>
|
||||
<if test="roleIds!=null and !roleIds.empty">
|
||||
+3
-3
@@ -1,8 +1,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">
|
||||
<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="xiaoyishuId" column="xiaoyishu_id" jdbcType="VARCHAR"/>
|
||||
<result property="password" column="password" jdbcType="VARCHAR"/>
|
||||
@@ -18,7 +18,7 @@
|
||||
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
|
||||
<result property="isDeleted" column="is_deleted" jdbcType="BOOLEAN"/>
|
||||
</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
|
||||
FROM t_user
|
||||
WHERE phone = #{phone}
|
||||
+2
-2
@@ -1,8 +1,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">
|
||||
<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="userId" column="user_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.Getter;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package top.crushtj.xiaoyishu.auth.enums;
|
||||
package top.crushtj.xiaoyi.auth.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
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;
|
||||
|
||||
@@ -10,7 +10,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import top.crushtj.framework.common.exception.BizException;
|
||||
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 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.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 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 com.google.common.collect.Lists;
|
||||
@@ -9,13 +9,13 @@ import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
import top.crushtj.framework.common.utils.JsonUtils;
|
||||
import top.crushtj.xiaoyishu.auth.constant.RedisKeyConstants;
|
||||
import top.crushtj.xiaoyishu.auth.domain.entity.PermissionEntity;
|
||||
import top.crushtj.xiaoyishu.auth.domain.entity.RoleEntity;
|
||||
import top.crushtj.xiaoyishu.auth.domain.entity.RolePermissionRelEntity;
|
||||
import top.crushtj.xiaoyishu.auth.domain.mappers.PermissionMapper;
|
||||
import top.crushtj.xiaoyishu.auth.domain.mappers.RoleMapper;
|
||||
import top.crushtj.xiaoyishu.auth.domain.mappers.RolePermissionRelMapper;
|
||||
import top.crushtj.xiaoyi.auth.constant.RedisKeyConstants;
|
||||
import top.crushtj.xiaoyi.auth.domain.entity.PermissionEntity;
|
||||
import top.crushtj.xiaoyi.auth.domain.entity.RoleEntity;
|
||||
import top.crushtj.xiaoyi.auth.domain.entity.RolePermissionRelEntity;
|
||||
import top.crushtj.xiaoyi.auth.domain.mappers.PermissionMapper;
|
||||
import top.crushtj.xiaoyi.auth.domain.mappers.RoleMapper;
|
||||
import top.crushtj.xiaoyi.auth.domain.mappers.RolePermissionRelMapper;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -88,27 +88,27 @@ public class PushRolePermissions2RedisRunner implements ApplicationRunner {
|
||||
.collect(Collectors.toMap(PermissionEntity::getId, permissionEntity -> permissionEntity));
|
||||
|
||||
// 构建角色 ID 和权限 DO 列表的映射关系
|
||||
Map<Long, List<PermissionEntity>> roleIdPermissionMap = new HashMap<>();
|
||||
Map<String, List<String>> roleIdPermissionMap = new HashMap<>();
|
||||
roleList.forEach(role -> {
|
||||
Long roleId = role.getId();
|
||||
//获取角色对应的权限id列表
|
||||
List<Long> permissionIds = roleIdPermissionIdsMap.get(roleId);
|
||||
if (CollUtil.isNotEmpty(permissionIds)) {
|
||||
List<PermissionEntity> perDOS = Lists.newArrayList();
|
||||
List<String> permissionKeys = Lists.newArrayList();
|
||||
permissionIds.forEach(permissionId -> {
|
||||
// 根据权限 ID 获取具体的权限 DO 对象
|
||||
PermissionEntity permissionDO = permissionIdEntityMap.get(permissionId);
|
||||
if (Objects.nonNull(permissionDO)) {
|
||||
perDOS.add(permissionDO);
|
||||
permissionKeys.add(permissionDO.getPermissionKey());
|
||||
}
|
||||
});
|
||||
roleIdPermissionMap.put(roleId, perDOS);
|
||||
roleIdPermissionMap.put(role.getRoleKey(), permissionKeys);
|
||||
}
|
||||
});
|
||||
|
||||
// 将角色 ID 和权限 DO 列表的映射关系写入 Redis
|
||||
roleIdPermissionMap.forEach((roleId, permission) -> {
|
||||
String key = RedisKeyConstants.buildRolePermissionsKey(roleId);
|
||||
roleIdPermissionMap.forEach((roleKey, permission) -> {
|
||||
String key = RedisKeyConstants.buildRolePermissionsKey(roleKey);
|
||||
redisTemplate.opsForValue()
|
||||
.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 jakarta.annotation.PostConstruct;
|
||||
@@ -6,11 +6,11 @@ import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
import top.crushtj.xiaoyishu.auth.domain.entity.UserEntity;
|
||||
import top.crushtj.xiaoyishu.auth.domain.mappers.UserMapper;
|
||||
import top.crushtj.xiaoyi.auth.domain.entity.UserEntity;
|
||||
import top.crushtj.xiaoyi.auth.domain.mappers.UserMapper;
|
||||
|
||||
import static top.crushtj.xiaoyishu.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.RedisKeyConstants.XIAOYI_ID_GENERATOR_KEY;
|
||||
import static top.crushtj.xiaoyi.auth.constant.XiaoyiAuthConstants.XIAOYI_ID_INITIAL_VALUE;
|
||||
|
||||
/**
|
||||
* @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 top.crushtj.framework.common.response.Response;
|
||||
import top.crushtj.xiaoyishu.auth.domain.entity.UserEntity;
|
||||
import top.crushtj.xiaoyishu.auth.model.vo.user.UserLoginReqVO;
|
||||
import top.crushtj.xiaoyi.auth.domain.entity.UserEntity;
|
||||
import top.crushtj.xiaoyi.auth.model.vo.user.UserLoginReqVO;
|
||||
|
||||
/**
|
||||
* @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.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.StpUtil;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
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.JsonUtils;
|
||||
import top.crushtj.framework.common.utils.MaskUtils;
|
||||
import top.crushtj.xiaoyishu.auth.constant.RedisKeyConstants;
|
||||
import top.crushtj.xiaoyishu.auth.domain.entity.UserEntity;
|
||||
import top.crushtj.xiaoyishu.auth.domain.entity.UserRoleRelEntity;
|
||||
import top.crushtj.xiaoyishu.auth.domain.mappers.UserMapper;
|
||||
import top.crushtj.xiaoyishu.auth.domain.mappers.UserRoleRelMapper;
|
||||
import top.crushtj.xiaoyishu.auth.enums.LoginTypeEnum;
|
||||
import top.crushtj.xiaoyishu.auth.enums.ResponseCodeEnum;
|
||||
import top.crushtj.xiaoyishu.auth.model.vo.user.UserLoginReqVO;
|
||||
import top.crushtj.xiaoyishu.auth.service.UserService;
|
||||
import top.crushtj.xiaoyi.auth.constant.RedisKeyConstants;
|
||||
import top.crushtj.xiaoyi.auth.domain.entity.RoleEntity;
|
||||
import top.crushtj.xiaoyi.auth.domain.entity.UserEntity;
|
||||
import top.crushtj.xiaoyi.auth.domain.entity.UserRoleRelEntity;
|
||||
import top.crushtj.xiaoyi.auth.domain.mappers.RoleMapper;
|
||||
import top.crushtj.xiaoyi.auth.domain.mappers.UserMapper;
|
||||
import top.crushtj.xiaoyi.auth.domain.mappers.UserRoleRelMapper;
|
||||
import top.crushtj.xiaoyi.auth.enums.LoginTypeEnum;
|
||||
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.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static top.crushtj.xiaoyishu.auth.constant.RedisKeyConstants.XIAOYI_ID_GENERATOR_KEY;
|
||||
import static top.crushtj.xiaoyishu.auth.constant.RoleConstants.COMMON_USER_ROLE_ID;
|
||||
import static top.crushtj.xiaoyishu.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.RedisKeyConstants.XIAOYI_ID_GENERATOR_KEY;
|
||||
import static top.crushtj.xiaoyi.auth.constant.RoleConstants.COMMON_USER_ROLE_ID;
|
||||
import static top.crushtj.xiaoyi.auth.constant.XiaoyiAuthConstants.NICK_NAME_PREFIX;
|
||||
import static top.crushtj.xiaoyi.auth.constant.XiaoyiAuthConstants.XIAOYI_ID_INITIAL_VALUE;
|
||||
|
||||
/**
|
||||
* @author ayi
|
||||
@@ -56,6 +57,9 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> impleme
|
||||
@Resource
|
||||
private UserRoleRelMapper userRoleRelMapper;
|
||||
|
||||
@Resource
|
||||
private RoleMapper roleMapper;
|
||||
|
||||
@Resource
|
||||
private TransactionTemplate transactionTemplate;
|
||||
|
||||
@@ -103,6 +107,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> impleme
|
||||
} else {
|
||||
// 已注册,则获取其用户 ID
|
||||
userId = userEntity.getId();
|
||||
this.loadUserRole(userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -115,6 +120,12 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> impleme
|
||||
return Response.success(tokenInfo.tokenValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户注册
|
||||
*
|
||||
* @param phone 手机号
|
||||
* @return 用户ID
|
||||
*/
|
||||
private Long registerUser(String phone) {
|
||||
return transactionTemplate.execute(status -> {
|
||||
try {
|
||||
@@ -148,9 +159,10 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> impleme
|
||||
userRoleRelMapper.insert(userRoleRel);
|
||||
|
||||
// 将用户角色信息存储到 Redis 中
|
||||
List<Long> roles = new ArrayList<>();
|
||||
roles.add(COMMON_USER_ROLE_ID);
|
||||
String userRolesKey = RedisKeyConstants.buildUserRolesKey(phone);
|
||||
RoleEntity role = roleMapper.selectById(COMMON_USER_ROLE_ID);
|
||||
List<String> roles = new ArrayList<>(1);
|
||||
roles.add(role.getRoleKey());
|
||||
String userRolesKey = RedisKeyConstants.buildUserRolesKey(userId);
|
||||
redisTemplate.opsForValue()
|
||||
.set(userRolesKey, JsonUtils.toJsonString(roles));
|
||||
return userId;
|
||||
@@ -161,7 +173,8 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> impleme
|
||||
// 回滚 redis 自增ID
|
||||
Long decrement = Long.valueOf(Objects.requireNonNull(redisTemplate.opsForValue()
|
||||
.get(XIAOYI_ID_GENERATOR_KEY))
|
||||
.toString().trim());
|
||||
.toString()
|
||||
.trim());
|
||||
if (decrement.compareTo(XIAOYI_ID_INITIAL_VALUE) > 0) {
|
||||
log.error("==> 用户注册异常,回滚redis自增ID: {}", decrement);
|
||||
redisTemplate.opsForValue()
|
||||
@@ -171,5 +184,20 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> impleme
|
||||
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 jakarta.annotation.Resource;
|
||||
@@ -9,15 +9,15 @@ import org.springframework.stereotype.Service;
|
||||
import top.crushtj.framework.common.exception.BizException;
|
||||
import top.crushtj.framework.common.response.Response;
|
||||
import top.crushtj.framework.common.utils.MaskUtils;
|
||||
import top.crushtj.xiaoyishu.auth.constant.RedisKeyConstants;
|
||||
import top.crushtj.xiaoyishu.auth.enums.ResponseCodeEnum;
|
||||
import top.crushtj.xiaoyishu.auth.model.vo.verificationcode.SendVerificationCodeReqVO;
|
||||
import top.crushtj.xiaoyishu.auth.service.VerificationCodeService;
|
||||
import top.crushtj.xiaoyishu.auth.sms.AliyunSmsHelper;
|
||||
import top.crushtj.xiaoyi.auth.constant.RedisKeyConstants;
|
||||
import top.crushtj.xiaoyi.auth.enums.ResponseCodeEnum;
|
||||
import top.crushtj.xiaoyi.auth.model.vo.verificationcode.SendVerificationCodeReqVO;
|
||||
import top.crushtj.xiaoyi.auth.service.VerificationCodeService;
|
||||
import top.crushtj.xiaoyi.auth.sms.AliyunSmsHelper;
|
||||
|
||||
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 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 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 jakarta.annotation.Resource;
|
||||
@@ -1,14 +1,10 @@
|
||||
server:
|
||||
servlet:
|
||||
context-path: /xiaoyishu-auth
|
||||
|
||||
spring:
|
||||
datasource:
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
# 数据库连接信息
|
||||
url: ENC(+rm/FjvaL/fDxkZu/G9zxQnSO2M60VFodOuZzxqlxzUyNN2ooNjH9mtVVZiqlqMm8EHDCBKYD/rI4e1VdaDlYtwL+WxdW2K6rujuUm1R0Mkjl1kphthDaiAK5tnPvtfffL4aVpBbA7oOUdsX0tCURzWvpjSFhDHlxtb4b+Ezx6JeHJoZDEJ0bErjDETLdjbIGDNdCsTCvz2wp1S5+AdW39S5C8kj4PNLIXkbckzKdhHm9ATLrixdveyBLmuBkDwLsE9XBmCFm0ipST5/SVg8WA==)
|
||||
username: ENC(B/DozYPqx155j/Gdh3QkCX/wGmuzLf1jumsbb1kF4GY6EjrlRdAatc8BlhrrR2J6)
|
||||
password: ENC(JwQdlswsrwmGE6eplp6Jk9Vp5s6zlFgra6jLN5fKBt8qOyqQHUHCJM6JcKp5g0t8B/or2wTH18ohR/d41oyV9w==)
|
||||
url: ENC(oyIjmfAXMQ/z3uHl88N68RYJ96HJhdLkXMBBpE1wq9kSbYuTlxwZV/F7Qxa6tDAu+MD6I9swGqmYl2ormW/bzxPQdk+rrQANSQVA+ekyPGd97I5RGPfLLGQCw6FExrbhh9uzKGvekSmsIC8h91dTL2sMD8dYHbuRpW9J5knEyHSCZ90Bep1ZOlu2mWNQfdbaqmbafs6Bhjj9Iu4m9SblAMomAbDVT1oREPzjtMX640ro+Ou/YGVYPuLj3LlwVooauGCoswmHK0cA2TCD0RqaIg==)
|
||||
username: ENC(Hf/tJoMig8FWFHopB7CMlQFuBhu61JbpA64JQWvqrmL+fTtGi6QNuI+sHBNY43nY)
|
||||
password: ENC(l2k707lyMR9ACwqEHl/d3VOojwmvIyRsH7leB9fa9ppOtbKBae8YnVVuEMcRhEqMTTvcqZ4Otd/TotqZMerL/w==)
|
||||
druid: # Druid 连接池
|
||||
initial-size: 5 # 初始化连接池大小
|
||||
min-idle: 5 # 最小连接池数量
|
||||
@@ -43,7 +39,7 @@ spring:
|
||||
data:
|
||||
redis:
|
||||
database: 0 # Redis 数据库索引(默认为 0)
|
||||
host: ENC(W8nbSo0vMjCenHHLHyhvdhW2wH3oI54/FW+LQp8H9lx6xQH2Tm56nGSgNom6xsK7) # Redis 服务器地址
|
||||
host: ENC(C1TWXF+/HzWQBF25uXCdy/0fHoRDXdCW72+NKCIJURg4l3IDnJzl278KmFhfsusX) # Redis 服务器地址
|
||||
port: 6379 # Redis 服务器连接端口
|
||||
password: ENC(iK/k0IGPflACqYMUwX4N/sGvCVuysYywLcAO+Ikeqk326V8hCr8dgEGzkiEIwWOo) # Redis 服务器连接密码(默认为空)
|
||||
timeout: 5s # 读超时时间
|
||||
@@ -83,3 +79,19 @@ jasypt:
|
||||
string-output-type: base64
|
||||
provider-name: SunJCE
|
||||
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:
|
||||
# token 名称(同时也是 cookie 名称)
|
||||
token-name: satoken
|
||||
token-name: Authorization
|
||||
token-prefix: Bearer
|
||||
# token 有效期(单位:秒) 默认30天,-1 代表永久有效
|
||||
timeout: 2592000
|
||||
# timeout: 60
|
||||
# token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结
|
||||
active-timeout: -1
|
||||
# 是否允许同一账号多地同时登录 (为 true 时允许一起登录, 为 false 时新登录挤掉旧登录)
|
||||
@@ -20,6 +22,9 @@ sa-token:
|
||||
# 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token)
|
||||
is-share: true
|
||||
# token 风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik)
|
||||
token-style: uuid
|
||||
token-style: random-128
|
||||
# 是否输出操作日志
|
||||
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 lombok.SneakyThrows;
|
||||
@@ -64,23 +64,46 @@ public class EncryptTest {
|
||||
// 待加密的原始值
|
||||
String encry1 = ""; // 待加密的原始值1
|
||||
String encry2 = ""; // 待加密的原始值2
|
||||
String encry3 = ""; // 待加密的原始值3
|
||||
|
||||
try {
|
||||
String cipherAccessKeyId = encryptor.encrypt(encry1);
|
||||
System.out.println("字段1加密成功,密文:" + cipherAccessKeyId);
|
||||
|
||||
String decryptAccessKeyId = encryptor.decrypt(cipherAccessKeyId);
|
||||
System.out.println("字段1解密成功,明文:" + decryptAccessKeyId);
|
||||
|
||||
String cipherAccessKeySecret = encryptor.encrypt(encry2);
|
||||
System.out.println("字段2加密成功,密文:" + cipherAccessKeySecret);
|
||||
|
||||
String decryptAccessKeySecret = encryptor.decrypt(cipherAccessKeySecret);
|
||||
System.out.println("字段2解密成功,明文:" + decryptAccessKeySecret);
|
||||
String cipherText1 = encryptor.encrypt(encry1);
|
||||
System.out.println("字段1加密成功,密文:" + cipherText1);
|
||||
String cipherText2 = encryptor.encrypt(encry2);
|
||||
System.out.println("字段2加密成功,密文:" + cipherText2);
|
||||
String cipherText3 = encryptor.encrypt(encry3);
|
||||
System.out.println("字段2加密成功,密文:" + cipherText3);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
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.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 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