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

问题描述 投票:0回答:14

我正在使用 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);
    }
}
java spring spring-mvc spring-security
14个回答
67
投票
SecurityContextHolder.getContext().getAuthentication().getPrincipal();

返回当前用户对象。这可以是

User
UserDetails
或您的 自定义用户 对象。 您需要将返回对象转换为
UserDetails
或您自己的用户对象(如果是自定义对象)。

或者您可以将

Authentication
Principal
直接注入控制器。 原则是您的
UserDetails
/自定义用户对象。

注意:

UserDetails
是一个接口


29
投票

你可以像这样使用它

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


11
投票

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

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

8
投票

你刚刚迈出了一步。

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

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

8
投票

我通过使用

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
投票

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

3
投票
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;
    }
}

1
投票

如果你想获取当前用户的所有属性,首先转到实现

UserDetails
的类,更可能的是它称为
UserPrincipal
并为每个属性编写一个 get 方法,例如:
getAge()
,其次去找你HTML 文件并写下这个

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

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


0
投票

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

  1. @Authenticationprincipal

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


0
投票

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

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

0
投票

这个解决方案对我来说适用于 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
类。


0
投票

我选择实现自己的自定义 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 令牌重建整个用户(凭证除外)。


-1
投票

因此,几乎每个答案似乎都是正确且可行的,向所有贡献者致敬,但删除样板代码可能有用且简单: 创建一个包含所有实用方法的接口及其实现,然后简单地

@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;
    }
}

-2
投票

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

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

© www.soinside.com 2019 - 2024. All rights reserved.