我正在使用 Spring 安全版本 3.1.4.RELEASE。 如何访问当前登录的用户对象?
SecurityContextHolder.getContext().getAuthentication().getPrincipal()
返回用户名,而不是用户对象。那么如何使用返回的 Username 并获取 UserDetails 对象呢?
我尝试过以下代码:
public UserDetails getLoggedInUser(){
final Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null && auth.isAuthenticated() && !(auth instanceof AnonymousAuthenticationToken))
{
if(auth.getDetails() !=null)
System.out.println(auth.getDetails().getClass());
if( auth.getDetails() instanceof UserDetails)
{
System.out.println("UserDetails");
}
else
{
System.out.println("!UserDetails");
}
}
return null;
}
结果如下:
[2015-08-17 19:44:46.738] INFO http-bio-8443-exec-423 System.out class org.springframework.security.web.authentication.WebAuthenticationDetails
[2015-08-17 19:44:46.738] INFO http-bio-8443-exec-423 System.out !UserDetails
AuthenticationFilter 类如下:
public class CustomUsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "j_username";
public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "j_password";
public static final String SPRING_SECURITY_LAST_USERNAME_KEY = "SPRING_SECURITY_LAST_USERNAME";
private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
private boolean postOnly = true;
public CustomUsernamePasswordAuthenticationFilter() {
super("/j_spring_security_check");
}
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
String username = obtainUsername(request);
String password = obtainPassword(request);
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
username = username.trim();
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
if(this.getAuthenticationManager()==null){
logger.info("Authentication manager is null.");
} else {
logger.info("Authentication manager was "+this.getAuthenticationManager().getClass().getName());
}
return this.getAuthenticationManager().authenticate(authRequest);
}
protected String obtainPassword(HttpServletRequest request) {
return request.getParameter(passwordParameter);
}
protected String obtainUsername(HttpServletRequest request) {
return request.getParameter(usernameParameter);
}
protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) {
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
}
public void setUsernameParameter(String usernameParameter) {
this.usernameParameter = usernameParameter;
}
public void setPasswordParameter(String passwordParameter) {
this.passwordParameter = passwordParameter;
}
public void setPostOnly(boolean postOnly) {
this.postOnly = postOnly;
}
public final String getUsernameParameter() {
return usernameParameter;
}
public final String getPasswordParameter() {
return passwordParameter;
}
}
AuthenticationProvider 如下:
@Component
public class CustomAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
private MyUserDetailsService userDetailsService;
public MyUserDetailsService getUserDetailsService() {
return userDetailsService;
}
public void setUserDetailsService(MyUserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
@Override
protected void additionalAuthenticationChecks(UserDetails arg0,
UsernamePasswordAuthenticationToken arg1)
throws AuthenticationException {
}
@Override
protected UserDetails retrieveUser(String arg0,
UsernamePasswordAuthenticationToken arg1)
throws AuthenticationException {
return userDetailsService.loadUserByUsername(arg0);
}
}
UserDetails 类如下:
public class MyUserDetailsService implements UserDetailsService {
private final Map<String, UserDetails> usersList;
public MyUserDetailsService() {
Collection<GrantedAuthority> authorityList;
final SimpleGrantedAuthority supervisorAuthority = new SimpleGrantedAuthority("supervisor");
final SimpleGrantedAuthority userAuthority = new SimpleGrantedAuthority("user");
usersList = new TreeMap<String, UserDetails>();
authorityList = new ArrayList<GrantedAuthority>();
authorityList.add(supervisorAuthority);
authorityList.add(userAuthority);
usersList.put("admin", new User("admin", "admin", authorityList));
authorityList = new ArrayList<GrantedAuthority>();
authorityList.add(userAuthority);
usersList.put("peter", new User("peter", "password123", authorityList));
//probably don't use this in production
for(Map.Entry<String, UserDetails> user : usersList.entrySet()){
logger.info(user.getValue().toString());
}
}
@Override
public UserDetails loadUserByUsername(String username)throws UsernameNotFoundException {
UserDetails ud = usersList.get(username);
if (ud != null) {
logger.info("loadUserByUsername: found match, returning "
+ ud.getUsername() + ":" + ud.getPassword() + ":"
+ ud.getAuthorities().toString());
return new User(ud.getUsername(), ud.getPassword(),
ud.getAuthorities());
}
logger.info("loadUserByUsername: did not find match, throwing UsernameNotFoundException");
throw new UsernameNotFoundException(username);
}
}
SecurityContextHolder.getContext().getAuthentication().getPrincipal();
返回当前用户对象。这可以是
User
、UserDetails
或您的 自定义用户 对象。
您需要将返回对象转换为 UserDetails
或您自己的用户对象(如果是自定义对象)。
或者您可以将
Authentication
或 Principal
直接注入控制器。
原则是您的 UserDetails
/自定义用户对象。
注意:
UserDetails
是一个接口
你可以像这样使用它
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
String username = ((UserDetails)principal).getUsername();
} else {
String username = principal.toString();
}
您可以简单地将身份验证接口注入控制器并获取登录用户的用户名,如下所示:
@GetMapping(value = "/username")
@ResponseBody
public String currentUserName(Authentication authentication) {
if (authentication != null)
return authentication.getName();
else
return "";
}
你刚刚迈出了一步。
SecurityContextHolder.getContext().getAuthentication()
返回一个 Authentication
对象。 您应该知道如何对用户进行身份验证,以及具体类可以实现什么Authentication
。假设它是AbstractAuthenticationToken
的子类(所有Spring提供的实现都是),并且getDetails()
返回一个UserDetails
,你可以使用:
AbstractAuthenticationToken auth = (AbstractAuthenticationToken)
SecurityContextHolder.getContext().getAuthentication();
UserDetails details = (UserDetails) auth.getDetails();
我通过使用
SecurityContextHolder
和 Authentication.getName()
解决了这个问题:
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String login = authentication.getName();
User user = usersService.getUserByLogin(login);
从 5.2 版本开始,您可以使用 CurrentSecurityContext 注解来获取当前用户身份验证:
@GetMapping("/authentication")
public Object authentication(@CurrentSecurityContext(expression="authentication")
Authentication authentication) {
return authentication.getDetails();
}
甚至:
@GetMapping("/hello")
public String hello(@CurrentSecurityContext(expression="authentication.name")
String username) {
return "Hello, " + username + "!";
}
public class User implements UserDetails {
private String firstname;
private String lastname;
}
假设您有一个实现 UserDetails 类的自定义用户
@RestController
@RequestMapping("/api/user")
class UsersController {
@GetMapping
public User fetchUser(@AuthenticationPrincipal User user) {
return user;
}
}
如果你想获取当前用户的所有属性,首先转到实现
UserDetails
的类,更可能的是它称为 UserPrincipal
并为每个属性编写一个 get 方法,例如:getAge()
,其次去找你HTML 文件并写下这个
<span th:text="${#request.userPrincipal.principal.age}> </span>
顺便说一句,您不需要在控制器中添加任何 ModelAttribute 希望它能解决问题,你可以问我
您可以通过以下方式获取当前登录用户:
@Authenticationprincipal
SecurityContextHolder.getContext().getAuthentication().getPrinciple()
您需要将principal向下转换为其实现的类,然后您可以提取在securityContext中设置的上下文对象。
AbstractAuthenticationToken a = (AbstractAuthenticationToken) request.getUserPrincipal();
UserContext context = (UserContext) a.getPrincipal();
这个解决方案对我来说适用于 Spring Boot 2.5
首先,定义一个
User
Principal
类
public class UserPrincipal implements UserDetails {
private static final long serialVersionUID = 1L;
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
// other methods ....
}
其次,定义一个
User
类:
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
String username;
String password;
//getters ans setters ...
}
三、定义一个
UserAuth
类:
public class UserAuth {
public String getUsername()
{
UserPrincipal principal
= (UserPrincipal)SecurityContextHolder
.getContext()
.getAuthentication()
.getPrincipal();
return principal.getUser().getUsername();
}
}
最后,您可以根据需要自动连接
UserAuth
类。
我选择实现自己的自定义 UserDetails 并添加一个额外的方法来轻松检索经过身份验证的当前用户。为了做到这一点,我创建了一个名为
UserDetailsImpl
的类,如下所示:
public class UserDetailsImpl implements UserDetails {
private User user;
public UserDetailsImpl(User user) {
this.user = user;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return List.of(new SimpleGrantedAuthority(user.getRole().name()));
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getEmail().toString();
}
public User getUser() {
return user;
}
}
User
是一个领域类。现在,要检索它,您只需执行以下操作:
@PostMapping()
public APIBaseResponse createClient(@RequestBody ProfileDTO profileDTO) {
var authenticationToken = (Authentication) SecurityContextHolder.getContext().getAuthentication();
var details = (UserDetailsImpl) authenticationToken.getPrincipal();
var user = details.getUser();
createProfileUseCase.createProfile(profileDTO, user);
return new APIBaseResponse(
HttpStatus.CREATED.value(),
"Profile created successfully"
);
}
我决定这样做是为了避免在请求处理期间需要时查询用户的数据库。但是,您必须小心,不要暴露凭据或任何类型的敏感数据。就我而言,我可以从 JWT 令牌重建整个用户(凭证除外)。
因此,几乎每个答案似乎都是正确且可行的,向所有贡献者致敬,但删除样板代码可能有用且简单: 创建一个包含所有实用方法的接口及其实现,然后简单地
@Autowire
。
public interface AuthHelper {
Authentication getAuthentication();
public String getName();
public UserDetails getUserDetails()
}
@Component
public class AuthHelperImpl implements AuthHelper {
@Override
public Authentication getAuthentication() {
return SecurityContextHolder.getContext().getAuthentication();
}
public String getName() {
return getAuthentication().getName();
}
public UserDetails getUserDetails() {
return (UserDetails) getAuthentication().getPrincipal();
}
//and more utilities you need
//you can also cast with UserPrincipal
}
现在,在控制器处:
@Controller
public class DemoController {
@Autowired
private AuthHelper authHelper;
@RequestMapping(value = "/username", method = RequestMethod.GET)
@ResponseBody
public String currentUserNameSimple() {
return authHelper.getName;
}
}
这可能是一篇值得一读的好文章。 本文展示了如何在 Spring 应用程序中获取用户信息,从常见的静态访问机制开始,然后介绍几种更好的注入主体的方法。