如何从spring security获取当前登录的用户对象?

Posted

技术标签:

【中文标题】如何从spring security获取当前登录的用户对象?【英文标题】:How to get the current logged in user object from spring security? 【发布时间】:2015-11-10 04:33:15 【问题描述】:

我正在使用 Spring 安全版本 3.1.4.RELEASE。 如何访问当前登录的用户对象?

SecurityContextHolder.getContext().getAuthentication().getPrinciple()

返回用户名,而不是用户对象。那么如何使用返回的 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);
    

【问题讨论】:

您如何验证您的用户?什么是AuthenticationProvider,什么是Filter? 好的,你的自定义 AuthenticationFilter 离 UsernamePasswordAuthenticationFilter 不远。使用 DaoAuthenticationProvider 是很常见的。您是否在任何地方配置了setForcePrincipalAsString(True) (或将forcePrincipalAsString 设置为true)? @SergeBallesta 不,我没有使用那些方法 使用显示的代码和默认(或通用)配置,SecurityContextHolder.getContext().getAuthentication().getPrincipal() 应该返回由MyUserDetailsService 提供的User 对象。您应该尝试使用调试器来跟踪完整的身份验证请求(在为 SpringSecurity 下载源代码之后) 或者您是否有任何东西(过滤器或?)可以使用身份验证对象将请求主体设置为用户名? 【参考方案1】:
SecurityContextHolder.getContext().getAuthentication().getPrincipal();

返回当前用户对象。这可以是UserUserDetails 或您的自定义用户 对象。 您需要将返回对象转换为 UserDetails 或您自己的用户对象(如果它是自定义对象)。

或者您可以将AuthenticationPrincipal 直接注入到您的控制器中。 原则是你的UserDetails/custom 用户对象。

注意:UserDetails 是一个接口

【讨论】:

Also a mkyong how-to w/ slightly more details. 如何获取用户id? User/UserDetails 类/接口中有一个用户名(字符串)字段,如果您的用户的 id 是用户名。 我收到 java.lang.ClassCastException。请帮帮我【参考方案2】:

你可以像这样使用它

Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();

if (principal instanceof UserDetails) 
String username = ((UserDetails)principal).getUsername();
 else 
String username = principal.toString();

在 spring 安全参考http://docs.spring.io/spring-security/site/docs/4.0.2.RELEASE/reference/htmlsingle/#obtaining-information-about-the-current-user

【讨论】:

我得到的是用户名,但不是用户对象。 我认为您必须检查上述docs.spring.io/spring-security/site/docs/4.0.2.RELEASE/… 之后的下一段,才能使用 UserDetailsS​​ervice 获取用户详细信息【参考方案3】:

你刚刚走了一步。 SecurityContextHolder.getContext().getAuthentication() 返回一个 Authentication 对象。 应该知道您是如何验证用户的,以及实现Authentication 的具体类可以做什么。假设它是AbstractAuthenticationToken 的子类(Spring 提供的所有实现都是),并且getDetails() 返回UserDetails,您可以使用:

AbstractAuthenticationToken auth = (AbstractAuthenticationToken)
    SecurityContextHolder.getContext().getAuthentication();
UserDetails details = (UserDetails) auth.getDetails();

【讨论】:

我得到了一个 WebAuthenticationDetails 的实例,而不是 UserDetails。【参考方案4】:

您可以简单地将身份验证接口注入您的控制器并获取登录用户的用户名,如下所示:

    @GetMapping(value = "/username")
    @ResponseBody
    public String currentUserName(Authentication authentication) 
    
        if (authentication != null)
            return authentication.getName();
        else
            return "";
    

【讨论】:

【参考方案5】:

我通过使用SecurityContextHolderAuthentication.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);

【讨论】:

【参考方案6】:

从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 + "!";

【讨论】:

【参考方案7】:

如果要获取当前用户的所有属性,首先转到实现 UserDetails 的类,更可能的是它称为 UserPrincipal 并为每个属性编写一个 get 方法,例如:getAge(), seconde go给你的 HTML 文件并写下这个

<span th:text="$#request.userPrincipal.principal.age> </span>

顺便说一下,您不需要在控制器中添加任何 ModelAttribute 希望能解决问题,你可以问我

【讨论】:

您没有回答最初的问题:“如何获取 UserDetails 对象?” @GetMapping("update") public String updateProfil(Authentication authentication) User u= userRepository.findByUsername(authentication.getName()).get(); System.out.println(u.getGender());返回“更新”; @Controller public class HomeController @Autowired UserRepository userRepository; private UserPrincipalDetailsService userPrincipalDetailsService; public HomeController(UserPrincipalDetailsService userPrincipalDetailsService) this.userPrincipalDetailsService=userPrincipalDetailsService; @GetMapping("") public String updateProfil(Authentication authentication ) User u= userRepository.findByUsername(authentication.getName()).get(); //you can get anything from the object System.out.println(u.getGender()); return " "; 【参考方案8】:

您可以通过以下方式获取当前登录用户:

    @Authenticationprincipal

    SecurityContextHolder.getContext().getAuthentication().getPrinciple()

【讨论】:

【参考方案9】:

这可能是一篇很好的文章。 本文展示了如何在 Spring 应用中获取用户信息,从常见的静态访问机制开始,然后介绍几种更好的注入 principal 的方法。

https://www.baeldung.com/get-user-in-spring-security

【讨论】:

【参考方案10】:

您需要将主体向下转换为其实现的类,然后您可以提取您在 securityContext 中设置的上下文对象。

 AbstractAuthenticationToken a = (AbstractAuthenticationToken) request.getUserPrincipal();
 UserContext context = (UserContext) a.getPrincipal();

【讨论】:

【参考方案11】:

这个解决方案适用于我的 spring boot 2.5

1- 用户主体类

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 ....


2- 用户类

public class User 

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long id;
    String username;
    String password;

   //getters ans setters ...

三级用户验证

 public class UserAuth 
    
     public String getUsername()
    
            UserPrincipal principal = (UserPrincipal) 
           SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    
            String username=principal.getUser().getUsername();
           return username;
    

【讨论】:

【参考方案12】:

所以几乎每个答案似乎都是正确和可行的,向所有贡献者致敬,但删除样板代码可能有用且容易: 创建一个包含所有实用程序方法的接口及其实现,然后只需 @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 security获取当前登录的用户对象?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用spring security在grails中获取所有当前登录用户的列表(包括rememberme cookie)

Spring Security:从数据库表中获取当前用户的字段

spring security 获取当前登录用户

Spring Security-获取当前登录用户的详细信息

Spring Security-获取当前登录用户的详细信息

如何使用 ldap 和 spring security 从登录的用户那里获取电子邮件