Spring Security Ouath2:Principal 对象未返回扩展的 UserDetails
Spring Security Ouath2 : Extended UserDetails not returned by the Principal object
上周我开始扩展 UserDetails class 以支持自定义字段。这个字段的特殊之处在于它会填充一个取决于请求参数的值。我设法正确地实现了这个(所以问题不关注那个)。
现在的问题是,在成功登录后,UserDetails 对象被正确填充(我能够使用 AuthenticationSuccessHandler 看到这一点)并且客户端从 OAuth2 提供程序接收到 JWT 令牌。然后,客户端尝试通过访问“/uaa/user”端点来获取有关用户的更多详细信息。这设置为 return Principal 对象。但是在检查了 Principal 对象的内容后,我惊讶地发现 UserDetails 对象丢失了。方法 getPrincipal() 仅 returned 用户名而不是 UserDetails 对象。
根据this question,这是登录失败的结果。 AuthenticationToken(在本例中为 UsernamePasswordAuthenticationToken)被 AuthenticationManager 拒绝。我不知道为什么它应该做这样的事情。使用默认 UserDetails 对象的身份验证似乎工作正常。有人可以帮我解决这个问题吗?
关于实现的 classes 的一些细节(如上所述)。由于某些原因,此处省略了一些代码。
自定义用户详细信息
public class CustomUserDetails extends User {
private final Integer custom;
public CustomUserDetails (...default params..., Integer custom) {
super(...default params...);
this.custom = custom;
}
}
CustomUserDetailsService
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Override
public CustomUserDetails loadUserByUsername(String username) throw UsernameNotFoundException {
return new CustomUserDetails(...default params..., 12345);
}
}
配置
@Autowired
private CustomUserDetails userDetails;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetails);
}
用户端点
@RequestMapping(value = "/user", method = RequestMethod.GET)
@ResponseBody
public Principal getDetails(Principal user) {
return user;
}
此处 return 的 Principal 对象应该在其中包含 UserDetails 对象,并且应该 return 将其发送给客户端。但是,当您调用 getPrincipal();
时,它只是 return 一个带有用户名的字符串
最后,我希望由用户端点(returns Principle 对象)编辑的 JSON return 包含我添加的自定义字段到 UserDetails.
提前致谢。
通常,您需要注释 @AuthenticationPrincipal
,但我建议您构建自己的注释,如下所示:
/**
* Our own {@link AuthenticationPrincipal} annotation as suggested by
* http://docs.spring.io/spring-security/site/docs/3.2.x/reference/htmlsingle/#mvc-authentication-principal
*
*/
@Target({ElementType.PARAMETER, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@AuthenticationPrincipal
public @interface CurrentUser {}
那么你可以这样Principal
:
@RequestMapping(..)
public Principal test(@CurrentUser Principal principal){..}
但是,恕我直言,您应该拥有自己的 Principal Impl,或者扩展现有的 Impl。像这样:
public MyPrincipal extends org.springframework.security.core.userdetails.User {..}
在这种情况下,您可以 return 任意值。
您可以使用此方法,在任何情况下,在控制器中或您需要的任何地方获取扩展的用户详细信息对象。这种方法可能有缺点,但很有效。
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
MyUserDetails myUser = (MyUserDetails) auth.getPrincipal();
public class MyUserDetails implements
org.springframework.security.core.userdetails.UserDetails {
private User user; //This is the user domain.
private String firstName;
private String lastName;
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>();
authList.add(new SimpleGrantedAuthority(user.getRole().getName()));
return authList;
}
public String getPassword() {
return user.getPassword();
}
public String getUsername() {
return user.getEmail();
}
public boolean isAccountNonExpired() {
return ((user.getAccountState() == AccountState.InActive) || (user.getAccountState() == AccountState.Blocked) ? false : true);
}
public boolean isAccountNonLocked() {
return (user.getAccountState() == AccountState.Locked) ? false : true;
}
public boolean isCredentialsNonExpired() {
return true;
}
public boolean isEnabled() {
return ((user.getAccountState() == AccountState.Active)
|| (user.getAccountState() == AccountState.PasswordReset)
|| (user.getAccountState() == AccountState.UnVerified) ? true
: false);
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
上周我开始扩展 UserDetails class 以支持自定义字段。这个字段的特殊之处在于它会填充一个取决于请求参数的值。我设法正确地实现了这个(所以问题不关注那个)。
现在的问题是,在成功登录后,UserDetails 对象被正确填充(我能够使用 AuthenticationSuccessHandler 看到这一点)并且客户端从 OAuth2 提供程序接收到 JWT 令牌。然后,客户端尝试通过访问“/uaa/user”端点来获取有关用户的更多详细信息。这设置为 return Principal 对象。但是在检查了 Principal 对象的内容后,我惊讶地发现 UserDetails 对象丢失了。方法 getPrincipal() 仅 returned 用户名而不是 UserDetails 对象。
根据this question,这是登录失败的结果。 AuthenticationToken(在本例中为 UsernamePasswordAuthenticationToken)被 AuthenticationManager 拒绝。我不知道为什么它应该做这样的事情。使用默认 UserDetails 对象的身份验证似乎工作正常。有人可以帮我解决这个问题吗?
关于实现的 classes 的一些细节(如上所述)。由于某些原因,此处省略了一些代码。
自定义用户详细信息
public class CustomUserDetails extends User {
private final Integer custom;
public CustomUserDetails (...default params..., Integer custom) {
super(...default params...);
this.custom = custom;
}
}
CustomUserDetailsService
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Override
public CustomUserDetails loadUserByUsername(String username) throw UsernameNotFoundException {
return new CustomUserDetails(...default params..., 12345);
}
}
配置
@Autowired
private CustomUserDetails userDetails;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetails);
}
用户端点
@RequestMapping(value = "/user", method = RequestMethod.GET)
@ResponseBody
public Principal getDetails(Principal user) {
return user;
}
此处 return 的 Principal 对象应该在其中包含 UserDetails 对象,并且应该 return 将其发送给客户端。但是,当您调用 getPrincipal();
时,它只是 return 一个带有用户名的字符串最后,我希望由用户端点(returns Principle 对象)编辑的 JSON return 包含我添加的自定义字段到 UserDetails.
提前致谢。
通常,您需要注释 @AuthenticationPrincipal
,但我建议您构建自己的注释,如下所示:
/**
* Our own {@link AuthenticationPrincipal} annotation as suggested by
* http://docs.spring.io/spring-security/site/docs/3.2.x/reference/htmlsingle/#mvc-authentication-principal
*
*/
@Target({ElementType.PARAMETER, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@AuthenticationPrincipal
public @interface CurrentUser {}
那么你可以这样Principal
:
@RequestMapping(..)
public Principal test(@CurrentUser Principal principal){..}
但是,恕我直言,您应该拥有自己的 Principal Impl,或者扩展现有的 Impl。像这样:
public MyPrincipal extends org.springframework.security.core.userdetails.User {..}
在这种情况下,您可以 return 任意值。
您可以使用此方法,在任何情况下,在控制器中或您需要的任何地方获取扩展的用户详细信息对象。这种方法可能有缺点,但很有效。
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
MyUserDetails myUser = (MyUserDetails) auth.getPrincipal();
public class MyUserDetails implements
org.springframework.security.core.userdetails.UserDetails {
private User user; //This is the user domain.
private String firstName;
private String lastName;
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>();
authList.add(new SimpleGrantedAuthority(user.getRole().getName()));
return authList;
}
public String getPassword() {
return user.getPassword();
}
public String getUsername() {
return user.getEmail();
}
public boolean isAccountNonExpired() {
return ((user.getAccountState() == AccountState.InActive) || (user.getAccountState() == AccountState.Blocked) ? false : true);
}
public boolean isAccountNonLocked() {
return (user.getAccountState() == AccountState.Locked) ? false : true;
}
public boolean isCredentialsNonExpired() {
return true;
}
public boolean isEnabled() {
return ((user.getAccountState() == AccountState.Active)
|| (user.getAccountState() == AccountState.PasswordReset)
|| (user.getAccountState() == AccountState.UnVerified) ? true
: false);
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}