🔐 wbase-security
🔐 wbase-security
安全认证模块 - 基于 Spring Security 实现用户认证、权限校验、Token 验证等安全能力
📦 Maven 依赖
<dependency>
<groupId>com.wzhcode.wbase</groupId>
<artifactId>wbase-security</artifactId>
<version>1.0.0</version>
</dependency>📁 目录结构
wbase-security/src/main/java/com/wzhcode/wbase/security/
│
├── 📂 config/ # 配置类
│ ├── SecurityProperties # 安全配置属性
│ ├── WBaseSecurityAutoConfiguration # 自动配置
│ ├── WBaseWebSecurityConfigurerAdapter # Security 配置适配器
│ ├── AuthorizeRequestsCustomizer # 自定义权限规则
│ ├── HttpSecurityFilterBeforeCustomizer # 自定义前置过滤器
│ └── HttpSecurityFilterAfterCustomizer # 自定义后置过滤器
│
└── 📂 core/ # 核心功能
├── LoginUser # 登录用户信息
├── 📂 annotations/ # 注解
├── 📂 aop/ # 切面
├── 📂 context/ # 上下文策略
├── 📂 filter/ # 过滤器
├── 📂 handler/ # 异常处理器
├── 📂 pojo/ # 数据对象
├── 📂 service/ # 服务接口
└── 📂 util/ # 工具类📑 目录
⚙️ 配置说明
位置:
com.wzhcode.wbase.security.config.SecurityProperties
配置项
wbase:
security:
# Token 请求头名称
token-header: Authorization
# Token 请求参数名(用于 WebSocket 等场景)
token-parameter: token
# Mock 模式开关(生产环境务必关闭!)
mock-enable: false
# Mock 模式密钥
mock-secret: test
# 免登录 URL 列表
permit-all-urls:
- /api/public/**
- /health
# 密码加密强度(4-31,越高越安全但越慢)
password-encoder-length: 4配置项说明
| 配置项 | 说明 | 默认值 |
|---|---|---|
token-header | HTTP 请求头中 Token 的名称 | Authorization |
token-parameter | URL 参数中 Token 的名称 | token |
mock-enable | 是否开启 Mock 登录模式 | false |
mock-secret | Mock 模式的密钥前缀 | test |
permit-all-urls | 免登录的 URL 列表 | [] |
password-encoder-length | BCrypt 加密强度 | 4 |
👤 登录用户
位置:
com.wzhcode.wbase.security.core.LoginUser
LoginUser 结构
@Data
public class LoginUser implements Serializable {
private Long id; // 用户编号
private Integer userType; // 用户类型
private Map<String, String> info; // 额外信息(昵称、部门等)
private Long tenantId; // 租户编号
private List<String> scopes; // 授权范围
// 上下文字段(不持久化,用于临时缓存)
private Map<String, Object> context;
}内置信息键
| 常量 | 说明 |
|---|---|
INFO_KEY_NICKNAME | 用户昵称 |
INFO_KEY_DEPT_ID | 部门编号 |
📝 使用示例
// 获取当前登录用户
LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
// 获取用户ID
Long userId = SecurityFrameworkUtils.getLoginUserId();
// 获取用户昵称
String nickname = SecurityFrameworkUtils.getLoginUserNickname();
// 获取部门ID
Long deptId = SecurityFrameworkUtils.getLoginUserDeptId();
// 使用上下文缓存
loginUser.setContext("key", value);
Object value = loginUser.getContext("key", Object.class);🔑 Token 认证
位置:
com.wzhcode.wbase.security.core.filter.TokenAuthenticationFilter
认证流程
请求进入
│
▼
┌─────────────────────────────────────┐
│ 1. 从 Header[login-user] 获取用户 │ ← Gateway 透传场景
│ (JSON 格式,URL 编码) │
└─────────────────────────────────────┘
│ 未获取到
▼
┌─────────────────────────────────────┐
│ 2. 从 Header/Parameter 获取 Token │
│ 调用 SecurityService 校验 │
└─────────────────────────────────────┘
│ 校验失败
▼
┌─────────────────────────────────────┐
│ 3. Mock 模式(开发调试用) │
│ Token 格式: {mockSecret}{userId}│
└─────────────────────────────────────┘
│
▼
设置 SecurityContextToken 格式
Authorization: Bearer {accessToken}或通过 URL 参数:
/api/xxx?token={accessToken}🛡️ 权限校验
SecurityFrameworkService
位置:
com.wzhcode.wbase.security.core.service.SecurityFrameworkService
| 方法 | 说明 |
|---|---|
hasPermission(permission) | 判断是否有指定权限 |
hasAnyPermissions(permissions...) | 判断是否有任一权限 |
hasRole(role) | 判断是否有指定角色 |
hasAnyRoles(roles...) | 判断是否有任一角色 |
hasScope(scope) | 判断是否有指定授权范围 |
hasAnyScopes(scopes...) | 判断是否有任一授权范围 |
📝 SpEL 表达式使用
// 权限校验
@PreAuthorize("@ss.hasPermission('system:user:list')")
public List<UserVO> list() { ... }
// 角色校验
@PreAuthorize("@ss.hasRole('admin')")
public void adminOnly() { ... }
// 任一权限
@PreAuthorize("@ss.hasAnyPermissions('user:create', 'user:update')")
public void createOrUpdate() { ... }
// 组合条件
@PreAuthorize("@ss.hasPermission('order:view') and @ss.hasRole('manager')")
public void viewOrder() { ... }SecurityService 接口
需要业务系统实现此接口
public interface SecurityService {
// 校验访问令牌
OAuth2AccessTokenCheckRespDTO checkAccessToken(String accessToken);
// 判断用户是否有权限
boolean hasAnyPermissions(Long userId, String... permissions);
// 判断用户是否有角色
boolean hasAnyRoles(Long userId, String... roles);
}🎯 注解使用
@PreAuthenticated
位置:
com.wzhcode.wbase.security.core.annotations.PreAuthenticated
声明接口需要登录,未登录时抛出 UNAUTHORIZED 异常。
@PreAuthenticated
@GetMapping("/profile")
public UserVO getProfile() {
// 此接口需要登录才能访问
return userService.getProfile();
}💡 与
@PreAuthorize的区别:@PreAuthenticated未登录时返回 401,而@PreAuthorize返回 403
@PermitAll
Jakarta 标准注解
声明接口免登录,自动加入白名单。
@PermitAll
@GetMapping("/public/info")
public InfoVO getPublicInfo() {
// 此接口无需登录
return infoService.getPublicInfo();
}🔧 扩展机制
自定义权限规则
继承
AuthorizeRequestsCustomizer
@Component
public class MyAuthorizeRequestsCustomizer extends AuthorizeRequestsCustomizer {
@Override
public void customize(AuthorizeHttpRequestsConfigurer<HttpSecurity>
.AuthorizationManagerRequestMatcherRegistry registry) {
// 自定义规则
registry.requestMatchers("/admin/**").hasRole("ADMIN")
.requestMatchers("/api/internal/**").hasIpAddress("192.168.1.0/24");
}
@Override
public int getOrder() {
return 100; // 优先级
}
}自定义过滤器
// 前置过滤器
@Component
public class MyBeforeFilter extends HttpSecurityFilterBeforeCustomizer {
@Override
public KeyValue<Filter, Class<? extends Filter>> get() {
return new KeyValue<>(new MyFilter(), TokenAuthenticationFilter.class);
}
}
// 后置过滤器
@Component
public class MyAfterFilter extends HttpSecurityFilterAfterCustomizer {
@Override
public KeyValue<Filter, Class<? extends Filter>> get() {
return new KeyValue<>(new MyFilter(), TokenAuthenticationFilter.class);
}
}实现 SecurityService
@Service
public class SecurityServiceImpl implements SecurityService {
@Override
public OAuth2AccessTokenCheckRespDTO checkAccessToken(String accessToken) {
// 调用认证服务校验 Token
return oauth2Client.checkToken(accessToken);
}
@Override
public boolean hasAnyPermissions(Long userId, String... permissions) {
// 查询用户权限并判断
Set<String> userPermissions = permissionService.getPermissions(userId);
return Arrays.stream(permissions).anyMatch(userPermissions::contains);
}
@Override
public boolean hasAnyRoles(Long userId, String... roles) {
// 查询用户角色并判断
Set<String> userRoles = roleService.getRoles(userId);
return Arrays.stream(roles).anyMatch(userRoles::contains);
}
}🔒 异常处理
认证异常
位置:
com.wzhcode.wbase.security.core.handler.AuthenticationEntryPointImpl
未登录访问需要认证的资源时,返回 401 UNAUTHORIZED。
授权异常
位置:
com.wzhcode.wbase.security.core.handler.AccessDeniedHandlerImpl
已登录但权限不足时,返回 403 FORBIDDEN。
🧵 异步支持
位置:
com.wzhcode.wbase.security.core.context.TransmittableThreadLocalSecurityContextHolderStrategy
基于 TransmittableThreadLocal 实现,解决 @Async 等异步场景下 SecurityContext 丢失问题。
@Async
public void asyncMethod() {
// 异步线程中也能获取到登录用户
LoginUser user = SecurityFrameworkUtils.getLoginUser();
}📚 主要依赖
| 依赖 | 用途 | 版本管理 |
|---|---|---|
| Spring Boot Starter Security | 安全框架 | ✅ |
| Spring Boot Starter AOP | 切面支持 | ✅ |
| wbase-core | 基础核心 | ✅ |
| wbase-web | Web 支持 | ✅ |
| Guava | 缓存(权限/角色) | ✅ |
| TransmittableThreadLocal | 异步上下文传递 | ✅ |
| bizlog-sdk | 操作日志 | ✅ |
版本由
wbase-dependencies统一管理
💡 最佳实践
| 场景 | 推荐做法 |
|---|---|
| 🔐 权限校验 | 使用 @PreAuthorize("@ss.hasPermission('xxx')") |
| 👤 获取用户 | 使用 SecurityFrameworkUtils.getLoginUser() |
| 🚫 免登录接口 | 使用 @PermitAll 注解或配置 permit-all-urls |
| 🔑 Token 传递 | Header 优先,WebSocket 用 Parameter |
| 🧪 开发调试 | 开启 Mock 模式,Token 格式 {secret}{userId} |
| 🚀 生产环境 | 务必关闭 Mock 模式! |
| 🔄 异步场景 | 自动支持,无需额外配置 |
| 📝 权限缓存 | 内置 1 分钟缓存,避免频繁查询 |