【问题标题】:Using Spring Security OAuth2, what's the right way to refresh the stored authentication in the TokenStore?使用 Spring Security OAuth2,刷新 TokenStore 中存储的身份验证的正确方法是什么?
【发布时间】:2013-08-18 04:05:00
【问题描述】:

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

  1. Bob 是使用权限 ROLE_USER 创建的
  2. Bob 尝试访问受 oauth2 保护的资源
  3. Bob 使用官方移动应用访问它,因此客户端凭据正确
  4. Bob 的访问令牌被创建并存储在TokenStore 中,键入他的usernameclient_idscope。 (见DefaultAuthenticationKeyGenerator.java
  5. Bob 的手机尝试使用此访问令牌调用受保护的服务,但这些服务要求用户拥有 authorityROLE_MOBILE_USER
  6. Bob 联系数据库所有者,并在数据库中将ROLE_MOBLE_USER 添加到他的用户。
  7. Bob 尝试获取另一个访问令牌,但 DefaultTokenServices 向他返回了相同的、无效的访问令牌。
  8. 利用他的新authority 的唯一方法是等到他的旧访问令牌过期,这样他就可以使用正确的authority 获取新的访问令牌。

有很多方法可以解决这个问题。

首先,将ROLE_MOBILE_USER 添加到 Bob 权限的管理应用程序可以清除数据库中的所有访问令牌和授权。这样,DefaultTokenServices 只会创建一个新的,其正确的权限序列化为他的新 OAuth2Authentication。但是,我们现在可能不希望 Administration webapp 关注 OAuth(至少现在还没有)。如果可能的话,我们希望管理应用程序的关注点尽可能简洁,并且现在不依赖于 oauth。

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

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

您能想出更好/更清洁的解决方案吗?我是不是想多了?

提前致谢。

【问题讨论】:

  • 我会补充一点,此时我使用了自定义的AuthenticationKeyGenerator,一切正常。我仍然想要一个更好/更清洁的解决方案,因为仅当授权服务器被要求提供新令牌时才会检查密钥,但资源服务器仍可能使用旧权限。
  • 您还在坚持这个解决方案还是找到了更好的方法来处理这个问题?我现在遇到了完全相同的问题,我不知道如何解决这个问题。
  • 我们一直坚持这个解决方案,到目前为止还没有遇到任何问题。但是,我会推荐第一个解决方案,并接受更改用户授权需要接触 oauth 令牌数据库。
  • 我在资源服务器中创建了一个过滤器,用于测试是否必须重新加载权限。如果是这样,则用户的所有令牌都将失效并返回未经授权的。不完美,但可能是我认为不混合身份验证/授权和资源处理的最佳方式。另外,请查看我的帖子以及 Dave Syer 在stackoverflow.com/questions/28947285/… 中的回复。看来将来这可能更容易做到;)
  • 如何确保新权限 ROLE_MOBILE_USER 在当前会话的用户安全上下文中可用?当前权限的上下文存储在会话中,直到下次登录才会更新。

标签: java oauth spring-security


【解决方案1】:

我在我的应用中解决了这个问题,方法是在发送身份验证信息时删除给定用户的所有令牌。

使用自定义的 AuthenticationProvider bean。

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

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

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

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

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

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

【讨论】:

  • 我理解这种做事方式,但没有更好的方法吗?我很难相信 spring oauth2 开发人员没有考虑到这个问题......我似乎不应该让凭证再次旅行以使先前的状态无效,因为首先应用程序不应该存储凭据,这就是 oauth 的全部意义,其次,仅仅因为我们无法进行基本的服务器端操作而再次询问用户凭据似乎是不对的:/您仍然坚持使用此操作还是找到任何其他解决方案这个问题?
  • 我用同样的方法解决了。我认为我们错过了从 oauth2 更新身份验证的支持。
【解决方案2】:

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

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 对象的属性的示例

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-12-03
      • 2017-06-14
      • 2019-04-23
      • 2019-01-28
      • 1970-01-01
      • 1970-01-01
      • 2014-09-15
      • 2015-04-27
      相关资源
      最近更新 更多