feat(gateway): 新增网关服务及权限认证功能
- 新增网关服务模块 han-note-gateway,包含基础配置和启动类 - 实现全局过滤器 AddUserId2HeaderFilter,自动将用户ID添加到请求头(目前有问题) - 配置 Sa-Token 权限认证,支持 JWT 格式的 Token 解析和鉴权 - 新增全局异常处理器 GlobalExceptionHandler,统一处理未登录和权限不足异常 - 实现 StpInterfaceImpl 接口,从 Redis 获取用户角色和权限信息- 配置 RedisTemplate 支持 JSON 序列化,用于存储用户角色和权限数据 - 在 auth 服务中增加登出接口,支持用户退出登录(待完成) - 引入 Nacos 配置中心和注册中心依赖,支持配置动态刷新和服务发现 - 更新 Redis Key 构造方式,使用 userId 和 roleKey 替代 email 和 roleId - 新增告警模块,支持邮件和短信告警方式的配置与切换 -优化角色权限同步逻辑,使用角色 Key 替代角色 ID 存储权限信息 - 添加 bootstrap.yml 配置文件,支持从 Nacos 读取配置
This commit is contained in:
2
.idea/encodings.xml
generated
2
.idea/encodings.xml
generated
@@ -3,6 +3,8 @@
|
|||||||
<component name="Encoding" defaultCharsetForPropertiesFiles="UTF-8">
|
<component name="Encoding" defaultCharsetForPropertiesFiles="UTF-8">
|
||||||
<file url="file://$PROJECT_DIR$/han-note-auth/src/main/java" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/han-note-auth/src/main/java" charset="UTF-8" />
|
||||||
<file url="file://$PROJECT_DIR$/han-note-auth/src/main/resources" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/han-note-auth/src/main/resources" charset="UTF-8" />
|
||||||
|
<file url="file://$PROJECT_DIR$/han-note-gateway/src/main/java" charset="UTF-8" />
|
||||||
|
<file url="file://$PROJECT_DIR$/han-note-gateway/src/main/resources" charset="UTF-8" />
|
||||||
<file url="file://$PROJECT_DIR$/hanserwei-framework/hanserwei-common/src/main/java" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/hanserwei-framework/hanserwei-common/src/main/java" charset="UTF-8" />
|
||||||
<file url="file://$PROJECT_DIR$/hanserwei-framework/hanserwei-common/src/main/resources" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/hanserwei-framework/hanserwei-common/src/main/resources" charset="UTF-8" />
|
||||||
<file url="file://$PROJECT_DIR$/hanserwei-framework/hanserwei-spring-boot-starter-biz-operationlog/src/main/java" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/hanserwei-framework/hanserwei-spring-boot-starter-biz-operationlog/src/main/java" charset="UTF-8" />
|
||||||
|
|||||||
7
.idea/inspectionProfiles/Project_Default.xml
generated
7
.idea/inspectionProfiles/Project_Default.xml
generated
@@ -1,6 +1,13 @@
|
|||||||
<component name="InspectionProjectProfileManager">
|
<component name="InspectionProjectProfileManager">
|
||||||
<profile version="1.0">
|
<profile version="1.0">
|
||||||
<option name="myName" value="Project Default" />
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="IncorrectHttpHeaderInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="customHeaders">
|
||||||
|
<set>
|
||||||
|
<option value="userId" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
</inspection_tool>
|
||||||
<inspection_tool class="PyPep8NamingInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
<inspection_tool class="PyPep8NamingInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||||
<option name="ignoredErrors">
|
<option name="ignoredErrors">
|
||||||
<list>
|
<list>
|
||||||
|
|||||||
@@ -69,6 +69,31 @@
|
|||||||
<groupId>cn.dev33</groupId>
|
<groupId>cn.dev33</groupId>
|
||||||
<artifactId>sa-token-redis-jackson</artifactId>
|
<artifactId>sa-token-redis-jackson</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- 使用 Spring Cloud Alibaba Nacos Config -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!-- Spring Cloud Context (用于动态刷新) -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-context</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!-- Spring Cloud Bootstrap (如果使用 bootstrap 配置) -->
|
||||||
|
<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>com.hanserwei</groupId>
|
||||||
|
<artifactId>hanserwei-common</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.hanserwei.hannote.auth.alarm;
|
||||||
|
|
||||||
|
import com.hanserwei.hannote.auth.alarm.impl.MailAlarmHelper;
|
||||||
|
import com.hanserwei.hannote.auth.alarm.impl.SmsAlarmHelper;
|
||||||
|
import org.apache.commons.lang3.Strings;
|
||||||
|
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;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@RefreshScope
|
||||||
|
public class AlarmConfig {
|
||||||
|
|
||||||
|
@Value("${alarm.type}")
|
||||||
|
private String alarmType;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public AlarmInterface alarmHelper() {
|
||||||
|
// 根据配置文件中的告警类型,初始化选择不同的告警实现类
|
||||||
|
if (Strings.CS.equals("sms", alarmType)) {
|
||||||
|
return new SmsAlarmHelper();
|
||||||
|
} else if (Strings.CS.equals("mail", alarmType)) {
|
||||||
|
return new MailAlarmHelper();
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("错误的告警类型...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.hanserwei.hannote.auth.alarm;
|
||||||
|
|
||||||
|
public interface AlarmInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送告警信息
|
||||||
|
*
|
||||||
|
* @param message 告警信息
|
||||||
|
* @return 发送结果
|
||||||
|
*/
|
||||||
|
boolean send(String message);
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package com.hanserwei.hannote.auth.alarm.impl;
|
||||||
|
|
||||||
|
import com.hanserwei.hannote.auth.alarm.AlarmInterface;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class MailAlarmHelper implements AlarmInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送告警信息
|
||||||
|
*
|
||||||
|
* @param message 告警信息
|
||||||
|
* @return 响应
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean send(String message) {
|
||||||
|
log.info("==> 【邮件告警】:{}", message);
|
||||||
|
|
||||||
|
// 业务逻辑...
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package com.hanserwei.hannote.auth.alarm.impl;
|
||||||
|
|
||||||
|
import com.hanserwei.hannote.auth.alarm.AlarmInterface;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class SmsAlarmHelper implements AlarmInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送告警信息
|
||||||
|
*
|
||||||
|
* @param message 告警信息
|
||||||
|
* @return 响应
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean send(String message) {
|
||||||
|
log.info("==> 【短信告警】:{}", message);
|
||||||
|
|
||||||
|
// 业务逻辑...
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -35,20 +35,20 @@ public class RedisKeyConstants {
|
|||||||
/**
|
/**
|
||||||
* 构建用户-角色 Key
|
* 构建用户-角色 Key
|
||||||
*
|
*
|
||||||
* @param email 邮箱
|
* @param userId 邮箱
|
||||||
* @return 用户角色key
|
* @return 用户角色key
|
||||||
*/
|
*/
|
||||||
public static String buildUserRoleKey(String email) {
|
public static String buildUserRoleKey(Long userId) {
|
||||||
return USER_ROLES_KEY_PREFIX + email;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8,10 +8,7 @@ import jakarta.annotation.Resource;
|
|||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
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;
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/user")
|
@RequestMapping("/user")
|
||||||
@@ -27,4 +24,13 @@ public class UserController {
|
|||||||
public Response<String> loginAndRegister(@Validated @RequestBody UserLoginReqVO userLoginReqVO) {
|
public Response<String> loginAndRegister(@Validated @RequestBody UserLoginReqVO userLoginReqVO) {
|
||||||
return userService.loginAndRegister(userLoginReqVO);
|
return userService.loginAndRegister(userLoginReqVO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/logout")
|
||||||
|
@ApiOperationLog(description = "账号登出")
|
||||||
|
public Response<?> logout(@RequestHeader("userId") String userId) {
|
||||||
|
log.info("==> 网关透传过来的用户 ID: {}", userId);
|
||||||
|
// todo 账号退出登录逻辑待实现
|
||||||
|
|
||||||
|
return Response.success();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ public interface RoleDOMapper extends BaseMapper<RoleDO> {
|
|||||||
/**
|
/**
|
||||||
* 查询所有被启用的角色
|
* 查询所有被启用的角色
|
||||||
*
|
*
|
||||||
* @return
|
* @return 角色列表
|
||||||
*/
|
*/
|
||||||
List<RoleDO> selectEnabledList();
|
List<RoleDO> selectEnabledList();
|
||||||
|
|
||||||
|
RoleDO selectByPrimaryKey(Long commonUserRoleId);
|
||||||
}
|
}
|
||||||
@@ -20,7 +20,6 @@ import org.springframework.stereotype.Component;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@@ -73,29 +72,29 @@ public class PushRolePermissions2RedisRunner implements ApplicationRunner {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// 组织 角色ID-权限 关系
|
// 组织 角色ID-权限 关系
|
||||||
Map<Long, List<PermissionDO>> roleIdPermissionDOMap = Maps.newHashMap();
|
Map<String, List<String>> roleKeyPermissionMap = Maps.newHashMap();
|
||||||
|
|
||||||
// 循环所有角色
|
// 循环所有角色
|
||||||
roleDOS.forEach(roleDO -> {
|
roleDOS.forEach(roleDO -> {
|
||||||
|
// 当前角色 roleKey
|
||||||
|
String roleKey = roleDO.getRoleKey();
|
||||||
// 当前角色 ID
|
// 当前角色 ID
|
||||||
Long roleId = roleDO.getId();
|
Long roleId = roleDO.getId();
|
||||||
// 当前角色 ID 对应的权限 ID 集合
|
// 当前角色 ID 对应的权限 ID 集合
|
||||||
List<Long> permissionIds = roleIdPermissionIdsMap.get(roleId);
|
List<Long> permissionIds = roleIdPermissionIdsMap.get(roleId);
|
||||||
if (CollUtil.isNotEmpty(permissionIds)) {
|
if (CollUtil.isNotEmpty(permissionIds)) {
|
||||||
List<PermissionDO> perDOS = Lists.newArrayList();
|
List<String> permissionKeys = Lists.newArrayList();
|
||||||
permissionIds.forEach(permissionId -> {
|
permissionIds.forEach(permissionId -> {
|
||||||
// 根据权限 ID 获取具体的权限 DO 对象
|
// 根据权限 ID 获取具体的权限 DO 对象
|
||||||
PermissionDO permissionDO = permissionIdDOMap.get(permissionId);
|
PermissionDO permissionDO = permissionIdDOMap.get(permissionId);
|
||||||
if (Objects.nonNull(permissionDO)) {
|
permissionKeys.add(permissionDO.getPermissionKey());
|
||||||
perDOS.add(permissionDO);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
roleIdPermissionDOMap.put(roleId, perDOS);
|
roleKeyPermissionMap.put(roleKey, permissionKeys);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 同步至 Redis 中,方便后续网关查询鉴权使用
|
// 同步至 Redis 中,方便后续网关查询鉴权使用
|
||||||
roleIdPermissionDOMap.forEach((roleId, permissions) -> {
|
roleKeyPermissionMap.forEach((roleId, permissions) -> {
|
||||||
String key = RedisKeyConstants.buildRolePermissionsKey(roleId);
|
String key = RedisKeyConstants.buildRolePermissionsKey(roleId);
|
||||||
redisTemplate.opsForValue().set(key, JsonUtils.toJsonString(permissions));
|
redisTemplate.opsForValue().set(key, JsonUtils.toJsonString(permissions));
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import cn.hutool.core.util.StrUtil;
|
|||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
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 com.google.common.collect.Lists;
|
|
||||||
import com.hanserwei.framework.common.enums.DeletedEnum;
|
import com.hanserwei.framework.common.enums.DeletedEnum;
|
||||||
import com.hanserwei.framework.common.enums.StatusEnum;
|
import com.hanserwei.framework.common.enums.StatusEnum;
|
||||||
import com.hanserwei.framework.common.exception.ApiException;
|
import com.hanserwei.framework.common.exception.ApiException;
|
||||||
@@ -14,8 +13,10 @@ import com.hanserwei.framework.common.response.Response;
|
|||||||
import com.hanserwei.framework.common.utils.JsonUtils;
|
import com.hanserwei.framework.common.utils.JsonUtils;
|
||||||
import com.hanserwei.hannote.auth.constant.RedisKeyConstants;
|
import com.hanserwei.hannote.auth.constant.RedisKeyConstants;
|
||||||
import com.hanserwei.hannote.auth.constant.RoleConstants;
|
import com.hanserwei.hannote.auth.constant.RoleConstants;
|
||||||
|
import com.hanserwei.hannote.auth.domain.dataobject.RoleDO;
|
||||||
import com.hanserwei.hannote.auth.domain.dataobject.UserDO;
|
import com.hanserwei.hannote.auth.domain.dataobject.UserDO;
|
||||||
import com.hanserwei.hannote.auth.domain.dataobject.UserRoleDO;
|
import com.hanserwei.hannote.auth.domain.dataobject.UserRoleDO;
|
||||||
|
import com.hanserwei.hannote.auth.domain.mapper.RoleDOMapper;
|
||||||
import com.hanserwei.hannote.auth.domain.mapper.UserDOMapper;
|
import com.hanserwei.hannote.auth.domain.mapper.UserDOMapper;
|
||||||
import com.hanserwei.hannote.auth.domain.mapper.UserRoleDOMapper;
|
import com.hanserwei.hannote.auth.domain.mapper.UserRoleDOMapper;
|
||||||
import com.hanserwei.hannote.auth.enums.LoginTypeEnum;
|
import com.hanserwei.hannote.auth.enums.LoginTypeEnum;
|
||||||
@@ -30,6 +31,7 @@ import org.springframework.stereotype.Service;
|
|||||||
import org.springframework.transaction.support.TransactionTemplate;
|
import org.springframework.transaction.support.TransactionTemplate;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
@@ -42,6 +44,7 @@ public class UserServiceImpl extends ServiceImpl<UserDOMapper, UserDO> implement
|
|||||||
private final RedisTemplate<String, Object> redisTemplate;
|
private final RedisTemplate<String, Object> redisTemplate;
|
||||||
private final UserRoleDOMapper userRoleDOMapper;
|
private final UserRoleDOMapper userRoleDOMapper;
|
||||||
private final TransactionTemplate transactionTemplate;
|
private final TransactionTemplate transactionTemplate;
|
||||||
|
private final RoleDOMapper roleDOMapper;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response<String> loginAndRegister(UserLoginReqVO reqVO) {
|
public Response<String> loginAndRegister(UserLoginReqVO reqVO) {
|
||||||
@@ -122,10 +125,13 @@ public class UserServiceImpl extends ServiceImpl<UserDOMapper, UserDO> implement
|
|||||||
.build();
|
.build();
|
||||||
userRoleDOMapper.insert(userRoleDO);
|
userRoleDOMapper.insert(userRoleDO);
|
||||||
|
|
||||||
// 将该用户的角色 ID 存入 Redis 中
|
RoleDO roleDO = roleDOMapper.selectByPrimaryKey(RoleConstants.COMMON_USER_ROLE_ID);
|
||||||
List<Long> roles = Lists.newArrayList();
|
|
||||||
roles.add(RoleConstants.COMMON_USER_ROLE_ID);
|
// 将该用户的角色 ID 存入 Redis 中,指定初始容量为 1,这样可以减少在扩容时的性能开销
|
||||||
String userRolesKey = RedisKeyConstants.buildUserRoleKey(email);
|
List<String> roles = new ArrayList<>(1);
|
||||||
|
roles.add(roleDO.getRoleKey());
|
||||||
|
|
||||||
|
String userRolesKey = RedisKeyConstants.buildUserRoleKey(userId);
|
||||||
redisTemplate.opsForValue().set(userRolesKey, JsonUtils.toJsonString(roles));
|
redisTemplate.opsForValue().set(userRolesKey, JsonUtils.toJsonString(roles));
|
||||||
|
|
||||||
return userId;
|
return userId;
|
||||||
|
|||||||
@@ -47,7 +47,9 @@ logging:
|
|||||||
############## Sa-Token 配置 (文档: https://sa-token.cc) ##############
|
############## Sa-Token 配置 (文档: https://sa-token.cc) ##############
|
||||||
sa-token:
|
sa-token:
|
||||||
# token 名称(同时也是 cookie 名称)
|
# token 名称(同时也是 cookie 名称)
|
||||||
token-name: satoken
|
token-name: Authorization
|
||||||
|
# token前缀
|
||||||
|
token-prefix: Bearer
|
||||||
# token 有效期(单位:秒) 默认30天,-1 代表永久有效
|
# token 有效期(单位:秒) 默认30天,-1 代表永久有效
|
||||||
timeout: 2592000
|
timeout: 2592000
|
||||||
# token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结
|
# token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结
|
||||||
@@ -57,6 +59,8 @@ 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: simple-uuid
|
token-style: random-128
|
||||||
# 是否输出操作日志
|
# 是否输出操作日志
|
||||||
is-log: true
|
is-log: true
|
||||||
|
alarm:
|
||||||
|
type: mail # 告警类型
|
||||||
|
|||||||
19
han-note-auth/src/main/resources/bootstrap.yml
Normal file
19
han-note-auth/src/main/resources/bootstrap.yml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
spring:
|
||||||
|
profiles:
|
||||||
|
active: dev # 激活的环境
|
||||||
|
application:
|
||||||
|
name: han-note-auth # 必须在 bootstrap 阶段就设置应用名
|
||||||
|
cloud:
|
||||||
|
nacos:
|
||||||
|
config:
|
||||||
|
server-addr: http://127.0.0.1:8848 # 指定 Nacos 配置中心的服务器地址
|
||||||
|
prefix: ${spring.application.name} # 配置 Data Id 前缀,这里使用应用名称作为前缀
|
||||||
|
group: DEFAULT_GROUP # 所属组
|
||||||
|
namespace: han-note # 命名空间
|
||||||
|
file-extension: yaml # 配置文件格式
|
||||||
|
refresh-enabled: true # 是否开启动态刷新
|
||||||
|
discovery:
|
||||||
|
enabled: true # 启用服务发现
|
||||||
|
group: DEFAULT_GROUP # 所属组
|
||||||
|
namespace: han-note # 命名空间
|
||||||
|
server-addr: 127.0.0.1:8848 # 指定 Nacos 配置中心的服务器地址
|
||||||
@@ -25,4 +25,13 @@
|
|||||||
where status = 0
|
where status = 0
|
||||||
and is_deleted = 0;
|
and is_deleted = 0;
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<select id="selectByPrimaryKey" resultMap="BaseResultMap">
|
||||||
|
select
|
||||||
|
<include refid="Base_Column_List"/>
|
||||||
|
from t_role
|
||||||
|
where id = #{id,jdbcType=BIGINT}
|
||||||
|
and is_deleted = 0
|
||||||
|
and status = 0;
|
||||||
|
</select>
|
||||||
</mapper>
|
</mapper>
|
||||||
78
han-note-gateway/pom.xml
Normal file
78
han-note-gateway/pom.xml
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
<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>com.hanserwei</groupId>
|
||||||
|
<artifactId>han-note</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>han-note-gateway</artifactId>
|
||||||
|
<name>${project.artifactId}</name>
|
||||||
|
<description>网关服务(负责路由转发、接口鉴权等功能)</description>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<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>
|
||||||
|
<!-- Redis -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Jackson 组件 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.hanserwei</groupId>
|
||||||
|
<artifactId>hanserwei-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,11 @@
|
|||||||
|
package com.hanserwei.hannote.gateway;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class HannoteGatewayApplication {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(HannoteGatewayApplication.class, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package com.hanserwei.hannote.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;
|
||||||
|
|
||||||
|
@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("app:note:publish"));
|
||||||
|
// 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 -> {
|
||||||
|
// return SaResult.error(e.getMessage());
|
||||||
|
// 手动抛出异常,抛给全局异常处理器
|
||||||
|
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,85 @@
|
|||||||
|
package com.hanserwei.hannote.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 com.hanserwei.hannote.gateway.constants.RedisKeyConstants;
|
||||||
|
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 java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义权限验证接口扩展
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
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) {
|
||||||
|
// 返回此 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;
|
||||||
|
}
|
||||||
|
List<String> userRoleKeys = objectMapper.readValue(useRolesValue, new TypeReference<>() {
|
||||||
|
});
|
||||||
|
if (CollUtil.isNotEmpty(userRoleKeys)) {
|
||||||
|
// 构建角色权限 Redis Key
|
||||||
|
List<String> rolePermissionKeys = userRoleKeys.stream().map(RedisKeyConstants::buildRolePermissionsKey).toList();
|
||||||
|
// 根据角色权限 Redis Key 批量获取角色权限集合
|
||||||
|
List<String> rolePermissionsValues = redisTemplate.opsForValue().multiGet(rolePermissionKeys);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
|
public List<String> getRoleList(Object loginId, String loginType) {
|
||||||
|
// 返回此 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;
|
||||||
|
}
|
||||||
|
return objectMapper.readValue(useRolesValue, new TypeReference<>() {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package com.hanserwei.hannote.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;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class RedisTemplateConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public RedisTemplate<String, Object> redisTemplate(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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package com.hanserwei.hannote.gateway.constants;
|
||||||
|
|
||||||
|
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,24 @@
|
|||||||
|
package com.hanserwei.hannote.gateway.enums;
|
||||||
|
|
||||||
|
import com.hanserwei.framework.common.exception.BaseExceptionInterface;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum ResponseCodeEnum implements BaseExceptionInterface {
|
||||||
|
|
||||||
|
// ----------- 通用异常状态码 -----------
|
||||||
|
SYSTEM_ERROR("500", "系统繁忙,请稍后再试"),
|
||||||
|
UNAUTHORIZED("401", "权限不足"),
|
||||||
|
|
||||||
|
|
||||||
|
// ----------- 业务异常状态码 -----------
|
||||||
|
;
|
||||||
|
|
||||||
|
// 异常码
|
||||||
|
private final String errorCode;
|
||||||
|
// 错误信息
|
||||||
|
private final String errorMsg;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package com.hanserwei.hannote.gateway.exception;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.exception.NotLoginException;
|
||||||
|
import cn.dev33.satoken.exception.NotPermissionException;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.hanserwei.framework.common.response.Response;
|
||||||
|
import com.hanserwei.hannote.gateway.enums.ResponseCodeEnum;
|
||||||
|
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;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
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 NotLoginException) { // Sa-Token 异常
|
||||||
|
// 权限认证失败时,设置 401 状态码
|
||||||
|
response.setStatusCode(HttpStatus.UNAUTHORIZED);
|
||||||
|
// 构建响应结果
|
||||||
|
result = Response.fail(ResponseCodeEnum.UNAUTHORIZED.getErrorCode(), "未携带Token令牌!");
|
||||||
|
} else if (ex instanceof NotPermissionException) {
|
||||||
|
// 权限认证失败时,设置 401 状态码
|
||||||
|
response.setStatusCode(HttpStatus.UNAUTHORIZED);
|
||||||
|
// 构建响应结果
|
||||||
|
result = Response.fail(ResponseCodeEnum.UNAUTHORIZED.getErrorCode(), ResponseCodeEnum.UNAUTHORIZED.getErrorMsg());
|
||||||
|
} else { // 其他异常,则统一提示 “系统繁忙” 错误
|
||||||
|
result = Response.fail(ResponseCodeEnum.SYSTEM_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置响应头的内容类型为 application/json;charset=UTF-8,表示响应体为 JSON 格式
|
||||||
|
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
|
||||||
|
// 设置 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]);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package com.hanserwei.hannote.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;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class AddUserId2HeaderFilter implements GlobalFilter {
|
||||||
|
/**
|
||||||
|
* 请求头中,用户 ID 的键
|
||||||
|
*/
|
||||||
|
private static final String HEADER_USER_ID = "userId";
|
||||||
|
@Override
|
||||||
|
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||||
|
log.info("==================> TokenConvertFilter");
|
||||||
|
// 用户 ID
|
||||||
|
Long userId = null;
|
||||||
|
try {
|
||||||
|
// 获取当前登录用户的 ID
|
||||||
|
userId = StpUtil.getLoginIdAsLong();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("==> 用户未登录, 获取用户 ID 失败: ", e);
|
||||||
|
// 若没有登录,则直接放行
|
||||||
|
return chain.filter(exchange);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("## 当前登录的用户 ID: {}", userId);
|
||||||
|
Long finalUserId = userId;
|
||||||
|
ServerWebExchange newExchange = exchange.mutate()
|
||||||
|
.request(builder -> builder.header(HEADER_USER_ID, String.valueOf(finalUserId))) // 将用户 ID 设置到请求头中
|
||||||
|
.build();
|
||||||
|
return chain.filter(newExchange);
|
||||||
|
}
|
||||||
|
}
|
||||||
43
han-note-gateway/src/main/resources/application.yml
Normal file
43
han-note-gateway/src/main/resources/application.yml
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
server:
|
||||||
|
port: 8000 # 指定启动端口
|
||||||
|
spring:
|
||||||
|
cloud:
|
||||||
|
gateway:
|
||||||
|
routes:
|
||||||
|
- id: auth
|
||||||
|
uri: lb://han-note-auth
|
||||||
|
predicates:
|
||||||
|
- Path=/auth/**
|
||||||
|
filters:
|
||||||
|
- StripPrefix=1
|
||||||
|
data:
|
||||||
|
redis:
|
||||||
|
database: 5 # Redis 数据库索引(默认为 0)
|
||||||
|
host: 127.0.0.1 # Redis 服务器地址
|
||||||
|
port: 6379 # Redis 服务器连接端口
|
||||||
|
password: redis # Redis 服务器连接密码(默认为空)
|
||||||
|
timeout: 5s # 读超时时间
|
||||||
|
connect-timeout: 5s # 链接超时时间
|
||||||
|
lettuce:
|
||||||
|
pool:
|
||||||
|
max-active: 200 # 连接池最大连接数
|
||||||
|
max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制)
|
||||||
|
min-idle: 0 # 连接池中的最小空闲连接
|
||||||
|
max-idle: 10 # 连接池中的最大空闲连接
|
||||||
|
sa-token:
|
||||||
|
# token 名称(同时也是 cookie 名称)
|
||||||
|
token-name: Authorization
|
||||||
|
# token前缀
|
||||||
|
token-prefix: Bearer
|
||||||
|
# token 有效期(单位:秒) 默认30天,-1 代表永久有效
|
||||||
|
timeout: 2592000
|
||||||
|
# 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
|
||||||
12
han-note-gateway/src/main/resources/bootstrap.yml
Normal file
12
han-note-gateway/src/main/resources/bootstrap.yml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
spring:
|
||||||
|
application:
|
||||||
|
name: han-note-gateway # 应用名称
|
||||||
|
profiles:
|
||||||
|
active: dev
|
||||||
|
cloud:
|
||||||
|
nacos:
|
||||||
|
discovery:
|
||||||
|
enabled: true # 启用服务发现
|
||||||
|
group: DEFAULT_GROUP # 所属组
|
||||||
|
namespace: han-note # 命名空间
|
||||||
|
server-addr: 127.0.0.1:8848 # NaCos 服务器地址
|
||||||
6
pom.xml
6
pom.xml
@@ -13,6 +13,7 @@
|
|||||||
<modules>
|
<modules>
|
||||||
<module>han-note-auth</module>
|
<module>han-note-auth</module>
|
||||||
<module>hanserwei-framework</module>
|
<module>hanserwei-framework</module>
|
||||||
|
<module>han-note-gateway</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
@@ -144,6 +145,11 @@
|
|||||||
<artifactId>sa-token-redis-jackson</artifactId>
|
<artifactId>sa-token-redis-jackson</artifactId>
|
||||||
<version>${sa-token.version}</version>
|
<version>${sa-token.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.dev33</groupId>
|
||||||
|
<artifactId>sa-token-reactor-spring-boot3-starter</artifactId>
|
||||||
|
<version>${sa-token.version}</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user