[英]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
。让我们玩这个场景来解释我的困境:
ROLE_USER
ROLE_USER
TokenStore
, keyed on his username
, client_id
, and scope
. TokenStore
,以他的username
、 client_id
和scope
。 (see DefaultAuthenticationKeyGenerator.java ) authority
of ROLE_MOBILE_USER
.authority
的ROLE_MOBILE_USER
。ROLE_MOBLE_USER
added to his user in the database. ROLE_MOBLE_USER
添加到他的数据库中的用户。DefaultTokenServices
returns him the same, non-working access token. DefaultTokenServices
他返回相同的非工作访问令牌。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.它基本上会使用
username
、 client_id
、 scope
和authorities
的权限,并对它们执行相同的摘要算法。 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).我对这个解决方案的问题是它仅仅依赖于底层令牌存储的实现行为(尽管
InMemoryTokenStore
和JdbcTokenStore
都是这样的)。
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.