跳到主要内容

SecurityUtils 工具类

SecurityUtils 提供静态方法获取当前登录用户信息,无需查询数据库

位置: blog-common/src/main/java/com/blog/common/utils/SecurityUtils.java


📖 API 参考

getCurrentUserId

获取当前登录用户的 ID

Long userId = SecurityUtils.getCurrentUserId();

实现原理:从 JwtAuthenticationDetails 中直接获取,性能优秀。

public static Long getCurrentUserId() {
Authentication authentication = SecurityContextHolder
.getContext()
.getAuthentication();

if (authentication != null &&
authentication.getDetails() instanceof JwtAuthenticationDetails details) {
return details.getUserId();
}

return null;
}

getCurrentUsername

获取当前登录用户名

String username = SecurityUtils.getCurrentUsername();
public static String getCurrentUsername() {
Authentication authentication = SecurityContextHolder
.getContext()
.getAuthentication();

if (authentication != null && authentication.getPrincipal() instanceof String username) {
return username;
}

return null;
}

getCurrentUserRoles

获取当前用户的角色列表

List<String> roles = SecurityUtils.getCurrentUserRoles();
// ["ROLE_USER", "ROLE_ADMIN"]
public static List<String> getCurrentUserRoles() {
Authentication authentication = SecurityContextHolder
.getContext()
.getAuthentication();

if (authentication != null) {
return authentication.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toList());
}
return List.of();
}

hasRole

检查当前用户是否有指定角色

if (SecurityUtils.hasRole("ROLE_ADMIN")) {
// 管理员逻辑
}
public static boolean hasRole(String role) {
return getCurrentUserRoles().contains(role);
}

🔐 使用示例

Controller 中获取当前用户

@RestController
@RequestMapping("/api/v1/users")
public class UserController {

@GetMapping("/me")
public Result<UserVO> getCurrentUser() {
// ✅ 推荐:使用 SecurityUtils
Long userId = SecurityUtils.getCurrentUserId();
return Result.success(userService.getById(userId));
}

@PutMapping("/me")
public Result<Void> updateCurrentUser(@RequestBody UserDTO dto) {
// 强制设置为当前用户ID,防止越权
dto.setId(SecurityUtils.getCurrentUserId());
userService.updateByDto(dto);
return Result.success();
}
}

Service 中记录操作用户

@Service
public class ArticleServiceImpl {

public void publishArticle(ArticleDTO dto) {
// 设置作者为当前用户
dto.setAuthorId(SecurityUtils.getCurrentUserId());
articleMapper.insert(dto);
}
}

权限检查

public void deleteArticle(Long articleId) {
Article article = articleMapper.selectById(articleId);

// 只有作者或管理员可以删除
Long currentUserId = SecurityUtils.getCurrentUserId();
boolean isOwner = article.getAuthorId().equals(currentUserId);
boolean isAdmin = SecurityUtils.hasRole("ROLE_ADMIN");

if (!isOwner && !isAdmin) {
throw new BusinessException(ErrorCode.ACCESS_DENIED);
}

articleMapper.deleteById(articleId);
}

⚠️ 注意事项

1. 只在认证上下文中使用

// ✅ 在需要认证的接口中使用
@GetMapping("/me")
public Result<UserVO> getCurrentUser() {
Long userId = SecurityUtils.getCurrentUserId(); // 可用
}

// ❌ 在公开接口中使用会返回 null
@PostMapping("/register") // 无需认证
public Result<UserVO> register() {
Long userId = SecurityUtils.getCurrentUserId(); // null!
}

2. 避免在 Controller 参数中传 userId

// ❌ 不推荐:从请求参数获取(不安全)
@PutMapping("/users/{userId}")
public Result<Void> updateUser(@PathVariable Long userId) { }

// ✅ 推荐:从 SecurityContext 获取
@PutMapping("/me")
public Result<Void> updateCurrentUser() {
Long userId = SecurityUtils.getCurrentUserId();
}

3. 线程安全

SecurityContextHolder 默认使用 ThreadLocal,在同一线程内安全。

// ✅ 同步环境
Long userId = SecurityUtils.getCurrentUserId();

// ⚠️ 异步环境需要传递 SecurityContext
@Async
public void asyncTask() {
// SecurityContext 可能为空!
// 需要显式传递或使用 DelegatingSecurityContextAsyncTaskExecutor
}

📚 延伸阅读