我一直试图让@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<Authority> 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是最干净的方式,这就是为什么我想让它工作。非常感谢任何帮助。
答案 0 :(得分:4)
您可以直接在方法参数中为经过身份验证的用户指定依赖项,而不是使用@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进行身份验证的实际对象。当调用该方法时,Spring将为您注入此内容。
答案 1 :(得分:3)
在我的情况下,我得到String
返回(用户名)而不是UserDetails
对象,即您应该将方法签名定义为
public ModelAndView index(@AuthenticationPrincipal String username)
这并不奇怪,因为@AuthenticationPrincipal
实际上会返回Authentication.getPrincipal()
并根据文档:
如果是使用用户名和密码的身份验证请求, 这将是用户名。呼叫者预计将填补 身份验证请求。
AuthenticationManager实现通常会返回一个 身份验证包含更丰富的信息作为使用的主体 通过申请。许多身份验证提供程序将创建一个 UserDetails对象作为主体。 请参阅:https://docs.spring.io/spring-security/site/docs/5.0.0.RELEASE/api/
所以,我假设你的AuthenticationManager
实现只返回一个用户名
答案 2 :(得分:1)
@RequestMapping(method= RequestMethod.GET,value="/authenticate", produces = MediaType.APPLICATION_JSON_VALUE)
public Object authenticate(@AuthenticationPrincipal Object obj) {
return obj;
}
我有同样的问题,我能够做到这一点。 您可以将它与mapper一起使用
@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);
}
这些对我有用,我希望将来对任何人都有用
答案 3 :(得分:1)
摘自文档:
Class AuthenticationPrincipalArgumentResolver 将使用以下命令解析CustomUser参数 SecurityContextHolder中的Authentication.getPrincipal()。如果 Authentication或Authentication.getPrincipal()为null,它将 返回null。如果类型不匹配,则返回null,除非 AuthenticationPrincipal.errorOnInvalidType()为true时, 将抛出ClassCastException。
这个简单的代码对我有用:
@RestController
public class ApiController {
@RequestMapping(method = RequestMethod.GET, path = "/api/public/{id}")
public ResponseEntity<ApiResponse> getPublicResource(
@PathVariable Integer id,
@AuthenticationPrincipal String principal
) {
if (id % 2 == 0) {
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
return ResponseEntity.ok(new ApiResponse(
authentication.getPrincipal().toString(),
"this method scrapes principal directly from SpringSecurityContext"));
}
return ResponseEntity.ok(new ApiResponse(
principal.toString(),
"this method retrieves principal from method argument"));
}
}
只需检查Authentication对象中的主体类型。
答案 4 :(得分:0)
我找到了另一个解决方案,即使这不是规范的解决方案。
在您的控制器中
@RequestMapping(value = "/login", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public ResponseEntity<UserDTO> login(@RequestBody UserDTO user){
try {
usersService.authenticate(user.getUsername(), user.getPassword());
return new ResponseEntity<UserDTO>(user, HttpStatus.OK);
}
catch (BadCredentialsException e){
return new ResponseEntity<UserDTO>(HttpStatus.UNAUTHORIZED);
}
}
UserDTO只是一个包含用户名和密码的表单
在您的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
}
}
基本的是Principal
中的CustomUserDetailsService#authenticate
是一个对象,而不是经过身份验证的用户的名称,因此框架可以处理它,然后通过@AuthenticationPrincipal
机制注入。这对我有用。
答案 5 :(得分:0)
我面临与你完全相同的问题。 由于某种原因,spring security的@EnableWebSecurity不会自动添加argumentResolver,你需要手动添加它:
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver"/>
</mvc:argument-resolvers>
</mvc:annotation-driven>
答案 6 :(得分:0)
您需要检查是否正在使用@AuthenticationPrincipal
注释的正确版本以及AuthenticationPrincipalArgumentResolver
类的正确版本。
在Spring版本的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官方文档以了解配置示例。
答案 7 :(得分:0)
我通过扩展AbstractUserDetailsAuthenticationProvider的身份验证提供程序实现了这一目标。
在我的提供者中,我重写了以下方法:
受保护的UserDetails检索用户( 字符串用户名, UsernamePasswordAuthenticationToken身份验证)抛出AuthenticationException {}
,在这种方法中,我将创建我的UserDetails实现并返回它。然后,我可以使用AuthenticationPrincipal批注在控制器中获取它。
答案 8 :(得分:0)
即使文档中提到了有关customUser的内容,但我认为这是不可能的。将@AuthenticationPrincipal
与java.lang.Object
或org.springframework.security.core.userdetails.User
一起用作弹簧控制器方法参数类型。
如果类型不匹配,将返回null,除非 AuthenticationPrincipal.errorOnInvalidType()为true时, 将抛出ClassCastException。 Spring API Documentation