使用Spring Security OAuth2,在TokenStore中刷新存储的身份验证的正确方法是什么?

时间:2013-08-15 20:52:40

标签: java oauth spring-security

我们正在使用资源所有者凭据授权类型(oauth2:password中使用security-config.xml。让我们用这个场景来解释我的困境:

  1. Bob是使用权限ROLE_USER
  2. 创建的
  3. Bob尝试访问受oauth2保护的资源
  4. Bob使用官方移动应用程序访问它,因此客户端凭据正确
  5. 创建了Bob的访问令牌并将其存储在TokenStore中,并锁定在usernameclient_idscope上。 (见DefaultAuthenticationKeyGenerator.java
  6. Bob的手机尝试使用此访问令牌调用受保护的服务,但这些服务要求用户拥有authority ROLE_MOBILE_USER
  7. Bob与数据库所有者联系,并在数据库中为其用户添加了ROLE_MOBLE_USER
  8. Bob尝试获取另一个访问令牌,但DefaultTokenServices会向他返回相同的非工作访问令牌。
  9. 利用他的新authority的唯一方法是等到他的旧访问令牌过期,这样他就可以获得具有正确authority的新访问令牌。
  10. 有很多方法可以解决这个问题。

    例如,将ROLE_MOBILE_USER添加到Bob的权限的管理应用程序可以清除数据库中的所有访问令牌和授权。这样DefaultTokenServices只会创建一个新的,将正确的权限序列化为新的OAuth2Authentication。但是,我们可能不希望管理Web应用程序此时关注OAuth(至少现在还没有)。如果可能的话,我们希望尽可能简洁地保持管理应用程序的关注点,而且现在对oauth没有任何依赖性。

    我们可以将DELETE方法暴露给/oauth/access_token端点并告诉移动应用尝试删除该访问令牌并重新请求一个,以防存储的authorities过时。这感觉更像是一种解决方法。

    最后,我可以在我自己定义的authorities中序列化AuthenticationKeyGenerator。它基本上使用授权的usernameclient_idscopeauthorities,并对它们执行相同的摘要算法。这样,当Bob尝试登录时,他将获得相同的访问令牌,但底层令牌存储将识别出他具有不同的身份验证(来自令牌granter bean中的身份验证管理器)并刷新其数据库。我对此解决方案的问题在于它只依赖于底层令牌存储的实现行为(尽管InMemoryTokenStoreJdbcTokenStore都以这种方式运行。)

    您能想到更好/更清洁的解决方案吗?我是在想这个吗?

    提前致谢。

3 个答案:

答案 0 :(得分:2)

我在我的应用中通过在发送身份验证信息时删除给定用户的所有令牌来解决此问题。

使用自定义AuthenticationProvider bean。

@Component("authenticationProvider")
public class AuthenticationProviderImpl implements AuthenticationProvider

在令牌存储bean中自动装配。

@Autowired
@Qualifier("tokenStore")
private TokenStore tokenStore;

然后在authenticate方法中,如果第二次传递凭据,则删除给定用户的所有令牌。

@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) authentication;

    try {
         //Do authentication

        //Delete previous tokens
        Collection<OAuth2AccessToken> tokenCollection = tokenStore.findTokensByUserName(token.getName());
        for (OAuth2AccessToken oToken : tokenCollection){
            tokenStore.removeAccessToken(oToken);
        }

        //return Authentication;
    }
}

大多数请求都将使用令牌并完全绕过它,但是当传递凭据时,将生成新令牌。此令牌将与新的身份验证对象相关联,该身份验证对象将包括所有新角色以及对用户所做的更改。

答案 1 :(得分:0)

我遇到了同样的问题,我用这个函数解决了它:

protected void reloadUserFromSecurityContext(SecurityContext securityContext, Person user){
    OAuth2Authentication requestingUser = (OAuth2Authentication) securityContext.getUserPrincipal();
    Object principal = (PersonUserDetails) requestingUser.getUserAuthentication().getPrincipal();
    if(principal instanceof PersonUserDetails) {
        ((PersonUserDetails) principal).setPerson(user);
    }
    OAuth2AuthenticationDetails authDetails = (OAuth2AuthenticationDetails) requestingUser.getDetails();
    OAuth2AccessToken tokenStored = jdbcTokenStore.readAccessToken(authDetails.getTokenValue());
    jdbcTokenStore.storeAccessToken(tokenStored, requestingUser);
}

这是更新OAuth2Authentication中的PersonUserDetails对象的属性的示例

答案 2 :(得分:0)

我遇到了同样的问题,这就是解决方法

@RequestMapping(value = "/updateToken", method = RequestMethod.POST)
 void updateToken(@RequestBody tokenReq req) { 
            Collection<OAuth2AccessToken> tokenCollection = tokenStore.findTokensByClientIdAndUserName(req.idclient, req.username);
            for (OAuth2AccessToken AToken : tokenCollection){
                OAuth2Authentication Auth = tokenStore.readAuthentication(AToken);
                OAuth2AccessToken newToken = tokenServices.createAccessToken(Auth);
                tokenStore.removeAccessToken(AToken);
                tokenStore.storeAccessToken(newToken, Auth);
            }

 }