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()); }
}
CustomUserDetailsService
@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只是一个包含用户名和密码的表单
在CustomUserDetailsService中
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官方文档以获取配置示例。