当前位置: 首页 > news >正文

Spring Security如何拿到登录用户的信息

书接上文,我们刚实现了一个简单的登录功能,那么我们要如何才能拿到登录用户的具体信息呢?我进行了一下整理

“登录用户的信息” 是保存在 SecurityContext 里的,可以通过多种方式获取这个用户的信息,下面是最常用的方式:

在 Controller 里用 @AuthenticationPrincipal

@GetMapping("/me")
public String getCurrentUser(@AuthenticationPrincipal UserDetails userDetails) {return "当前登录用户:" + userDetails.getUsername();
}

这个方式适用于:你使用了 Spring Security 默认的 UserDetailsService 或者自定义了用户实体实现了 UserDetails 接口。

SecurityContextHolder.getContext().getAuthentication()

Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Object principal = authentication.getPrincipal();if (principal instanceof UserDetails userDetails) {System.out.println("用户名:" + userDetails.getUsername());
}

这个是 最底层、最万能的方式,可以在任何地方(包括 Service 层)使用。

用 Spring 注入 Principal 对象(Controller 中)

@GetMapping("/me")
public String getCurrentUser(Principal principal) {return "当前登录用户:" + principal.getName();
}

同样的,这个方式也适用于:你使用了 Spring Security 默认的 UserDetailsService 或者自定义了用户实体实现了 UserDetails 接口。

用 HttpServletRequest.getUserPrincipal()

@GetMapping("/me")
public String getUser(HttpServletRequest request) {Principal principal = request.getUserPrincipal();return principal.getName();
}

这个也常见于过滤器或 Servlet 场景。

自定义实现UserDetails接口

假设我们已经有了一个实体类TUser,如下:

package org.pp.springsecurity.entity;import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;import java.io.Serializable;
import java.time.LocalDateTime;@Data
@TableName("t_user")
public class TUser implements Serializable {private Integer id;private String loginAct;private String loginPwd;private String name;private String phone;private String email;private LocalDateTime createTime;private Integer createBy;private LocalDateTime editTime;private Integer editBy;private LocalDateTime lastLoginTime;}

此时我们有两个选择:

  • 直接让TUser实现UserDetails接口
  • 额外写一个LoginUser实现UserDetails,并包装TUser

GPT推荐的是后者,原因如下:

  1. 职责分离(Separation of Concerns)
    • TUser 是数据库实体类,只关注“数据结构”和“持久化”;
    • LoginUser 是安全上下文中的用户视图,关注“登录逻辑”和“权限校验”。

👉 如果你让 TUser 实现 UserDetails,就把认证逻辑和数据库模型耦合在了一起,违背了单一职责原则(SRP)。

  1. 避免污染实体类

UserDetails 接口有七八个方法(用户名、密码、权限、状态等),可能不是你 TUser 表实际需要的字段;

将这些方法塞进实体类里,会让 TUser 看起来非常臃肿、难维护。

  1. 增强灵活性,方便扩展

LoginUser 可以自由扩展,比如你想额外在 LoginUser 里放 JWT Token、IP 地址、登录时间等字段;

而 TUser 是实体类,不能随便乱加跟表无关的字段,否则容易导致 MyBatis 查询异常或字段映射错误。

  1. 便于做权限模型嵌套

假设以后你设计 RBAC 权限系统,LoginUser 可以包装更多内容:

public class LoginUser implements UserDetails {private TUser user;private List<String> roles;private List<String> permissions;
}

如果用 TUser 本身,结构就固定死了,扩展权限字段很不优雅。

  1. 跟框架使用方式一致
    Spring Security + MyBatis Plus 实战项目里,绝大多数工程师都选择使用封装类(比如 LoginUser),而不是直接污染实体类。你在企业代码里也会常见:
public class JwtUser implements UserDetails {private final TUser user;
}

ok最终来实现一下LoginUser:

package org.pp.springsecurity.entity;import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;import java.util.Collection;
import java.util.Collections;@Getter
@AllArgsConstructor
public class LoginUser implements UserDetails {private final TUser user;@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {// 你可以从 user 表中读取权限字段并转换为 Authority,这里先返回空集合return Collections.emptyList();}@Overridepublic String getPassword() {return user.getLoginPwd();}@Overridepublic String getUsername() {return user.getLoginAct();}@Overridepublic boolean isAccountNonExpired() {return user.getAccountNoExpired() == 1;}@Overridepublic boolean isAccountNonLocked() {return user.getAccountNoLocked() == 1;}@Overridepublic boolean isCredentialsNonExpired() {return user.getCredentialsNoExpired() == 1;}@Overridepublic boolean isEnabled() {return user.getAccountEnabled() == 1;}
}

然后就可以通过以下方式,使得返回给security框架的UserDetails是我们自己定义的,也就是包含登录用户信息的对象。现在你在用前面提到的四种方式去处理获取数据就行。

package org.pp.springsecurity.security;import org.pp.springsecurity.entity.TUser;
import org.pp.springsecurity.service.TUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;@Service
public class CustomUserDetailsService implements UserDetailsService {@Autowiredprivate TUserService tUserService;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {TUser tUser = tUserService.lambdaQuery().eq(TUser::getLoginAct, username).one();if (tUser == null) {throw new UsernameNotFoundException("用户不存在");}return new LoginUser(tUser);}
}

什么????你问我数据在哪???
你都获取到LoginUser了,它调用.getTUser不久获取到了对应数据库实体类的对象!!!
这里我们还是来讲一下吧
在controller层的方式:

@GetMapping("/me")
public TUser getLoginUser(@AuthenticationPrincipal LoginUser loginUser) {return loginUser.getUser();
}

在其他层的方式

@GetMapping("/me")
public TUser getLoginUser() {Authentication authentication = SecurityContextHolder.getContext().getAuthentication();Object principal = authentication.getPrincipal();if (principal instanceof LoginUser loginUser) {return loginUser.getUser(); // LoginUser 包装了 TUser,你可以自定义这个字段}throw new RuntimeException("未登录");
}

你用principle的方式也可以

@GetMapping("/me")
public String getCurrentUser(Principal principal) {if (principal instanceof LoginUser loginUser) {return "当前登录用户:" + loginUser.getTUser().getName();}return "未知用户";
}
http://www.xdnf.cn/news/984043.html

相关文章:

  • 安卓9.0系统修改定制化____系列讲解导读篇
  • 【C/C++】怎样设计一个合理的函数
  • 咖啡豆缺陷检测:用YOLOv8+TensorFlow实现工业级质检系统
  • 临时抱佛脚v2
  • 费用流学习笔记
  • C++内存池:减少动态分配开销的高效解决方案
  • R语言缓释制剂QBD解决方案之二
  • 如何使用vue2设计提示框组件
  • 解决华为云服务器无法ping通github问题
  • Java NIO 面试全解析:9大核心考点与深度剖析
  • Langfuse 深度使用指南:构建可观测的LLM应用系统
  • 蓝桥杯刷题
  • 腾讯位置商业授权危险地点查询开发指南
  • 【愚公系列】《生产线数字化设计与仿真》009-颜色分类站仿真(设置颜色分类站的仿真序列)
  • AI日报 - 2025年06月11日
  • ElasticSearch配置详解:什么是重平衡
  • 【MySQL 从 0 讲解系列】深入理解 GROUP BY 的本质与应用(含SQL示例+面试题)
  • 无刷直流电机控制系统仿真建模
  • 修仙处于平凡
  • 用Python撬动量化交易:深入探索开源利器vnpy
  • 彻底禁用Windows Defender通知和图标
  • Python基础数据类型与运算符全面解析
  • FaceFusion 技术深度剖析:核心算法与实现机制揭秘
  • 代码随想录算法训练营第60期第六十五天打卡
  • qt初识--01
  • OCR(光学字符识别)算法
  • IAR开发平台升级Arm和RISC-V开发工具链,加速现代嵌入式系统开发
  • 电机专用32位MCU PY32MD310,QFN32封装,内置多功能栅极驱动器
  • EtherCAT至TCP/IP异构网络互联:施耐德M580 PLC对接倍福CX5140解决方案
  • Vulkan学习笔记1—环境搭建