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

CustomUserDetailsS​​ervice

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


}