Spring Security @AuthenticationPrincipal

我一直在努力让@AuthenticationPrincipal与自定义User类一起正常工作。 不幸的是,用户始终为空。 这是代码:

调节器

@RequestMapping(value = "/", method = RequestMethod.GET) public ModelAndView index(@AuthenticationPrincipal User user) { ModelAndView mav= new ModelAndView("/web/index"); mav.addObject("user", user); return mav; } 

安全配置

 @Configuration @EnableWebMvcSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired CustomUserDetailsService customUserDetailsService; @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(customUserDetailsService).passwordEncoder(passwordEncoder()); } 

}

CustomUserDetailsS​​ervice

 @Component public class CustomUserDetailsService implements UserDetailsService { @Autowired UserRepository userRepository; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // Spring Data findByXY function return userRepository.findByUsername(username); } 

用户实体

 public class User implements UserDetails{ private String username; private String password; private Collection authorities; // Getters and Setters } 

权威实体

 public class Authority implements GrantedAuthority{ private User user; private String role; // Getters and Setters @Override public String getAuthority() { return this.getRole(); } } 

我尝试过在网上找到的各种解决方案,例如转换我的自定义用户对象:

 return new org.springframework.security.core.userdetails.User(user.getLogin(), user.getPassword(), true, true, true, true, authorities); 

获取活动用户的其他方法没有问题,但我发现@AuthenticationProvider CustomUserObject是最干净的方式,这就是为什么我想让它工作。 任何帮助是极大的赞赏。

您可以直接在方法参数中为经过身份validation的用户指定依赖项,而不是使用@AuthenticationPrincipal。 下面给出的东西

 @RequestMapping(value = "/", method = RequestMethod.GET) public ModelAndView index(Principal user) { ModelAndView mav= new ModelAndView("/web/index"); mav.addObject("user", user); return mav; } 

此Principal对象将是通过spring security进行身份validation的实际对象。 当调用该方法时,Spring会为您注入此内容。

我找到了另一个解决方案,即使这不是规范的解决方案。

在你的控制器中

 @RequestMapping(value = "/login", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE) @ResponseBody public ResponseEntity login(@RequestBody UserDTO user){ try { usersService.authenticate(user.getUsername(), user.getPassword()); return new ResponseEntity(user, HttpStatus.OK); } catch (BadCredentialsException e){ return new ResponseEntity(HttpStatus.UNAUTHORIZED); } } 

UserDTO os只是一个包含用户名和密码的表单

在CustomUserDetailsS​​ervice中

 public void authenticate(String username, String password){ try{ User user = new User(username, password); Authentication request = new UsernamePasswordAuthenticationToken(user, password, Arrays.asList(WebSecurityConfiguration.USER)); Authentication result = authenticationManager.authenticate(request); SecurityContextHolder.getContext().setAuthentication(result); } catch (InternalAuthenticationServiceException e){ // treat as a bad credential } } 

实现您自己的AuthenticationManager

 @Component class DefaultAuthenticationManager implements AuthenticationManager { @Autowired private CustomUserDetailsService usersService; public Authentication authenticate(Authentication auth) throws AuthenticationException { UserDetails user = usersService.loadUserByUsername(((User)auth.getPrincipal()).getUsername()); if (user != null) { return new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities()); } // Handle bad credentials here } } 

基本原则是CustomUserDetailsService#authenticate中的Principal是一个对象,而不是经过身份validation的用户的名称,因此框架可以处理它,然后通过@AuthenticationPrincipal机制注入。 这对我有用。

 @RequestMapping(method= RequestMethod.GET,value="/authenticate", produces = MediaType.APPLICATION_JSON_VALUE) public Object authenticate(@AuthenticationPrincipal Object obj) { return obj; } 

我有同样的问题,我能够做到这一点。 您可以将它与映射器一起使用

 @RequestMapping(method = RequestMethod.GET, value = "/authenticate2", produces = MediaType.APPLICATION_JSON_VALUE) public User authenticate2(@AuthenticationPrincipal Object obj) throws IOException { return mapper.readValue(mapper.writeValueAsString(obj), User.class); } 

这些对我有用,我希望它对未来的任何人都有用

我面临与你完全相同的问题。 出于某种原因,Spring Security的@EnableWebSecurity不会自动添加argumentResolver,您需要手动添加它:

      

在我的例子中,我得到一个String back(用户名)而不是UserDetails对象,即你应该将方法签名定义为

 public ModelAndView index(@AuthenticationPrincipal String username) 

这并不奇怪,因为@AuthenticationPrincipal实际上返回Authentication.getPrincipal()并根据文档:

对于使用用户名和密码的身份validation请求,这将是用户名。 呼叫者应填充身份validation请求的主体。

AuthenticationManager实现通常会返回包含更丰富信息的身份validation作为应用程序使用的主体。 许多身份validation提供程序将创建UserDetails对象作为主体。 请参阅: https : //docs.spring.io/spring-security/site/docs/5.0.0.RELEASE/api/

所以,我假设您的AuthenticationManager实现只返回一个用户名

您需要检查是否正在使用正确版本的@AuthenticationPrincipal注释和AuthenticationPrincipalArgumentResolver类的正确版本。

在Spring Security 4.0版之前,您必须使用类:

  • org.springframework.security.web.bind.annotation.AuthenticationPrincipal

  • org.springframework.security.web.bind.support.AuthenticationPrincipalArgumentResolver

从版本4.0开始,您必须使用:

  • org.springframework.security.core.annotation.AuthenticationPrincipal

  • org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver

查看@AuthenticationPrincipal官方文档以获取配置示例。