简体   繁体   English

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

[英]Using Spring Security OAuth2, what's the right way to refresh the stored authentication in the TokenStore?

We're using resource-owner credentials grant type (with oauth2:password in security-config.xml . Let's play out this scenario to explain my predicament:我们正在使用资源所有者凭据授予类型(在security-config.xml使用oauth2:password 。让我们玩这个场景来解释我的困境:

  1. Bob was created with authorities ROLE_USER Bob 的创建权限为ROLE_USER
  2. Bob tries to access an oauth2 protected resource Bob 尝试访问受 oauth2 保护的资源
  3. Bob uses the official mobile app to access it, so the client credentials are correct Bob 使用官方移动应用程序访问它,因此客户端凭据是正确的
  4. Bob's access token is created and stored in the TokenStore , keyed on his username , client_id , and scope . Bob 的访问令牌被创建并存储在TokenStore ,以他的usernameclient_idscope (see DefaultAuthenticationKeyGenerator.java ) (见DefaultAuthenticationKeyGenerator.java
  5. Bob's phone tries to call the protected services with this access token, but those services require users to have an authority of ROLE_MOBILE_USER .鲍勃的电话试图调用受保护的服务,此访问令牌,但这些服务需要用户拥有一个authorityROLE_MOBILE_USER
  6. Bob contacts the db owner and has ROLE_MOBLE_USER added to his user in the database. Bob 联系数据库所有者并将ROLE_MOBLE_USER添加到他的数据库中的用户。
  7. Bob tries to get another access token, but the DefaultTokenServices returns him the same, non-working access token. Bob 尝试获取另一个访问令牌,但DefaultTokenServices他返回相同的非工作访问令牌。
  8. The only way to take advantage of his new authority is to wait until his old access token expires so he can get a new access token with the correct authority .利用他的新authority的唯一方法是等到他的旧访问令牌过期,以便他可以获得具有正确authority的新访问令牌。

There are a number of ways to address this.有多种方法可以解决这个问题。

For one, the administration app that adds ROLE_MOBILE_USER to Bob's authorities could then clear all access tokens and authorizations in the database.一方面,将ROLE_MOBILE_USER添加到 Bob 权限的管理应用程序然后可以清除数据库中的所有访问令牌和授权。 This way the DefaultTokenServices will just create a new one with the correct authorities serialized as his new OAuth2Authentication.这样DefaultTokenServices将只创建一个新的,并将正确的权限序列化为他的新 OAuth2Authentication。 However we may not want the Administration webapp to be concerned with OAuth at this point (at least not yet).但是,此时我们可能不希望管理 web 应用程序关注 OAuth(至少现在还没有)。 If possible we'd like to keep the administration app concerns as concise as possible, and right now there are no dependencies on oauth.如果可能的话,我们希望管理应用程序的问题尽可能简洁,并且现在没有对 oauth 的依赖。

We could expose the DELETE method to the /oauth/access_token endpoint and tell the mobile app to try deleting that access token and re-requesting one, just in case the stored authorities are stale.我们可以将DELETE方法暴露给/oauth/access_token端点,并告诉移动应用程序尝试删除该访问令牌并重新请求一个,以防存储的authorities过时。 This feels more like a work-around though.不过,这感觉更像是一种解决方法。

Finally I can serialize the authorities in my own defined AuthenticationKeyGenerator .最后,我可以在我自己定义的AuthenticationKeyGenerator序列化authorities It would basically use the username , client_id , scope , and authorities of the authorization and perform the same digest algorithm on them.它基本上会使用usernameclient_idscopeauthorities的权限,并对它们执行相同的摘要算法。 This way when Bob tries to log in he'll get the same access token, but the underlying token store will recognize that he has a different authentication (from the authentication manager in the token granter bean) and refresh its database.这样,当 Bob 尝试登录时,他将获得相同的访问令牌,但底层令牌存储将识别他具有不同的身份验证(来自令牌授​​予者 bean 中的身份验证管理器)并刷新其数据库。 The problem I have with this solution is that it simply relies on the implementation behavior of the underlying token store (though both InMemoryTokenStore and JdbcTokenStore behave this way).我对这个解决方案的问题是它仅仅依赖于底层令牌存储的实现行为(尽管InMemoryTokenStoreJdbcTokenStore都是这样的)。

Can you think of any better/cleaner solutions?你能想到任何更好/更清洁的解决方案吗? Am I over-thinking this?我是不是想多了?

Thanks in advance.提前致谢。

I resolved this issue in my app by deleting all tokens for a given user when the authentication information is sent.我通过在发送身份验证信息时删除给定用户的所有令牌解决了我的应用程序中的此问题。

Use a custom AuthenticationProvider bean.使用自定义 AuthenticationProvider bean。

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

Autowire in the token store bean.在令牌存储 bean 中自动装配。

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

Then in the authenticate method, remove all tokens for a given user if the credentials are passed a second time.然后在身份验证方法中,如果第二次传递凭据,则删除给定用户的所有令牌。

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

Most of the requests will be using a token and will bypass this entirely, but when the credentials are passed, a new token will be generated.大多数请求将使用令牌并将完全绕过它,但是当凭据通过时,将生成一个新令牌。 This token will be associated with the new authentication object which will include all new roles, and changes made to the user.此令牌将与新的身份验证对象相关联,该对象将包括所有新角色以及对用户所做的更改。

I had the same problem and I solved it with this function:我遇到了同样的问题,我用这个函数解决了它:

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

That is a example for update an attribute for PersonUserDetails object that is in OAuth2Authentication这是更新 OAuth2Authentication 中的 PersonUserDetails 对象的属性的示例

I Had the same problem and this was the solution我遇到了同样的问题,这是解决方案

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

 }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM