Spring Security Oauth2:主体对象未返回扩展的用户详细信息

Posted

技术标签:

【中文标题】Spring Security Oauth2:主体对象未返回扩展的用户详细信息【英文标题】:Spring Security Ouath2 : Extended UserDetails not returned by the Principal object 【发布时间】:2016-01-09 18:46:47 【问题描述】:

上周我开始扩展 UserDetails 类以支持自定义字段。这个字段的特殊之处在于它填充了一个取决于请求参数的值。我设法正确地实现了这一点(所以问题不集中于此)。

现在的问题是,在成功登录后,UserDetails 对象被正确填充(我能够使用 AuthenticationSuccessHandler 看到这一点)并且客户端从 OAuth2 提供程序接收到 JWT 令牌。然后,客户端尝试通过访问“/uaa/user”端点来获取有关用户的更多详细信息。这设置为返回 Principal 对象。但是在检查了 Principal 对象的内容后,我惊讶地发现 UserDetails 对象丢失了。 getPrincipal() 方法只返回用户名而不是 UserDetails 对象。

根据this question,这是登录失败的结果。 AuthenticationToken(在本例中为 UsernamePasswordAuthenticationToken)被 AuthenticationManager 拒绝。我不知道为什么它应该做这样的事情。使用默认 UserDetails 对象的身份验证似乎工作得很好。有人可以帮我解决这个问题吗?

实现类的一些细节(如上所述)。由于某些原因,这里省略了一些代码。

自定义用户详情

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;

此处返回的 Principal 对象内部应包含 UserDetails 对象,并应将其返回给客户端。但是,当您调用 getPrincipal(); 时,它只会返回带有用户名的字符串;

最后,我希望用户端点(返回 Principle 对象)返回的 JSON 包含我添加到 UserDetails 的自定义字段。

提前致谢。

【问题讨论】:

你能显示更多细节或更好地链接到你的项目,比如github吗?所以我们可以通过跟踪代码轻松发现问题。 您尝试将@AuthenticationPrincipal CustomUserDetails details 作为getDetails 方法参数吗? 我完全忘记了这个问题。我们最终使用了一种不同的方法来获取用户详细信息(改为调用用户微服务端点)。但为了回答这个问题,我会尝试找到一个不涉及单独应用程序的解决方案。 我使用的是 MongoDB 和 OAuth2。我必须像 github.com/gtiwari333/floop/blob/master/src/main/java/io/floop/… 一样使用 Converter 到 OAuth2Authentication 对象 【参考方案1】:

一般情况下,您需要注解@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 ..

在这种情况下,你可以返回任何你想要的值。

【讨论】:

这对我来说似乎是一个很好的解决方案。刚刚对其进行了测试,这正是我需要的结果(在我将端点移动到另一个应用程序之前)。 @gt_ebuddy 这个解决方案也适合你吗? 其实我用的是mongodb,后来发现它需要Converter实现为:gist.github.com/gtiwari333/…,这样我就可以在那里创建自定义主体对象。【参考方案2】:

您可以在任何情况下使用此方法在控制器或您需要的任何地方获取扩展的用户详细信息对象。这种方法可能有缺点,但它很有效。

 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;




【讨论】:

以上是关于Spring Security Oauth2:主体对象未返回扩展的用户详细信息的主要内容,如果未能解决你的问题,请参考以下文章

资源服务器获取用户信息,java - Spring Security OAuth2资源服务器无法获取包含详细信息的主体 - 堆栈内存溢出...

资源服务器获取用户信息,java - Spring Security OAuth2资源服务器无法获取包含详细信息的主体 - 堆栈内存溢出...

资源服务器获取用户信息,java - Spring Security OAuth2资源服务器无法获取包含详细信息的主体 - 堆栈内存溢出...

使用OAuth2和匿名访问的Spring Security

Spring Security - 来自 JWT 的自定义主体

在 Spring Security 中获取访问令牌