项目使用InMemoryTokenStore时,token有效期设置与强制清除某客户端持有的token
来源:互联网 发布:上海定制服装淘宝网 编辑:程序博客网 时间:2024/06/06 09:33
1. 设置token有效期
在使用InMemoryTokenStore(token存储在内存)token生成策略时,系统默认的token的有效时间是12小时。
从oauth源码的默认token生成方法中,可以看出
public class DefaultTokenServices implements AuthorizationServerTokenServices, ResourceServerTokenServices,ConsumerTokenServices, InitializingBean {private int refreshTokenValiditySeconds = 60 * 60 * 24 * 30; // default 30 days.private int accessTokenValiditySeconds = 60 * 60 * 12; // default 12 hours.............................. private OAuth2AccessToken createAccessToken(OAuth2Authentication authentication, OAuth2RefreshToken refreshToken) { DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(UUID.randomUUID().toString()); int validitySeconds = getAccessTokenValiditySeconds(authentication.getOAuth2Request()); if (validitySeconds > 0) { token.setExpiration(new Date(System.currentTimeMillis() + (validitySeconds * 1000L))); } token.setRefreshToken(refreshToken); token.setScope(authentication.getOAuth2Request().getScope()); return accessTokenEnhancer != null ? accessTokenEnhancer.enhance(token, authentication) : token; } /** * The access token validity period in seconds * @param clientAuth the current authorization request * @return the access token validity period in seconds */ protected int getAccessTokenValiditySeconds(OAuth2Request clientAuth) { if (clientDetailsService != null) { ClientDetails client = clientDetailsService.loadClientByClientId(clientAuth.getClientId()); Integer validity = client.getAccessTokenValiditySeconds(); if (validity != null) { return validity; } } return accessTokenValiditySeconds; } ......................}从上面官方源码我们可以了解到2个事情
1. 在token对象里面包含了token的过期时间
2. ClientDetails 中的AccessTokenValiditySeconds字段可以指定token的有效期
目前我已经有一个自定义的客户端验证服务类。那么可以在验证客户端对象时,直接调用AccessTokenValiditySeconds的set方法,设置token有效期,这个时间的单位是秒
我的代码:
import java.util.ArrayList;import java.util.Collection;import java.util.List;import org.springframework.security.core.GrantedAuthority;import org.springframework.security.core.authority.SimpleGrantedAuthority;import org.springframework.security.oauth2.provider.ClientDetails;import org.springframework.security.oauth2.provider.ClientDetailsService;import org.springframework.security.oauth2.provider.ClientRegistrationException;import org.springframework.security.oauth2.provider.client.BaseClientDetails;import bap.core.config.util.spring.SpringContextHolder;import bap.core.dao.BaseDao;import bap.pp.core.config.item.domain.ConfigItem;import bap.pp.strongbox.client.domain.ClientDetail;/** * 客户端身份验证 * @author Amanda.Z * */public class ClientDetailConfig implements ClientDetailsService{private BaseDao baseDao;public ClientDetailConfig() {baseDao=SpringContextHolder.getBean(BaseDao.class);}/* * 根据客户端clientid检查客户端有效性,如果有效,则封装为oauth2客户端对象 * @see org.springframework.security.oauth2.provider.ClientDetailsService#loadClientByClientId(java.lang.String) */@Overridepublic ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException {BaseClientDetails details=new BaseClientDetails();ClientDetail client=(ClientDetail) this.baseDao.getUniqueResultByHql("from ClientDetail where clientName=? and deleted=0",clientId);if(client!=null){//设置客户端iddetails.setClientId(client.getClientName());//客户端私钥details.setClientSecret(client.getClientSecret());//受保护资源idList<String> resourceList=new ArrayList<>();resourceList.add(client.getResource().getResourceKey());details.setResourceIds(resourceList);//oauth2保护模式,本项目默认全部是客户端认证模式List<String> authorizedGrantTypes=new ArrayList<>();authorizedGrantTypes.add("client_credentials");details.setAuthorizedGrantTypes(authorizedGrantTypes);//客户端传入的参数List<String> scopList=new ArrayList<>();scopList.add("view");scopList.add(client.getScope());details.setScope(scopList);//设置此客户端持有的用户组Collection<GrantedAuthority> auths = new ArrayList<GrantedAuthority>();if(bap.util.StringUtil.isNotEmpty(client.getAuthoritites())){String [] authArray=client.getAuthoritites().split(",");for (String auth : authArray) {auths.add(new SimpleGrantedAuthority(auth));}}details.setAuthorities(auths);//设置token有效时间,单位是秒,(如果不设置,框架内部默认是12小时,本平台设置默认2小时)details.setAccessTokenValiditySeconds(Integer.parseInt(ConfigItem.TokenValiditySeconds.getVal()));}return details;}}
注意:这里通过ClientDail设置进去的token有效时间,只要有效时间没有过,不管这个时候,客户端是否被删除,都不会影响这个token的访问。
这个时候 ,产生了一个明显的问题,认证端管理员创建了某个客户端账户,并分配了权限,这个客户端获取了一个token,如果这个时候管理员希望把这个客户端删除,理论上应该连带这个客户端产生的token也删除。
接下来,介绍如何解决上面的问题,在自己的服务类中,删除某个客户端账户产生的token数据
首先还是先看源码,我使用的是InMemoryTokenStore,其中已经具备了删除token的方法:
public class InMemoryTokenStore implements TokenStore {private static final int DEFAULT_FLUSH_INTERVAL = 1000;private final ConcurrentHashMap<String, OAuth2AccessToken> accessTokenStore = new ConcurrentHashMap<String, OAuth2AccessToken>();private final ConcurrentHashMap<String, OAuth2AccessToken> authenticationToAccessTokenStore = new ConcurrentHashMap<String, OAuth2AccessToken>();private final ConcurrentHashMap<String, Collection<OAuth2AccessToken>> userNameToAccessTokenStore = new ConcurrentHashMap<String, Collection<OAuth2AccessToken>>();private final ConcurrentHashMap<String, Collection<OAuth2AccessToken>> clientIdToAccessTokenStore = new ConcurrentHashMap<String, Collection<OAuth2AccessToken>>();private final ConcurrentHashMap<String, OAuth2RefreshToken> refreshTokenStore = new ConcurrentHashMap<String, OAuth2RefreshToken>();private final ConcurrentHashMap<String, String> accessTokenToRefreshTokenStore = new ConcurrentHashMap<String, String>();private final ConcurrentHashMap<String, OAuth2Authentication> authenticationStore = new ConcurrentHashMap<String, OAuth2Authentication>();private final ConcurrentHashMap<String, OAuth2Authentication> refreshTokenAuthenticationStore = new ConcurrentHashMap<String, OAuth2Authentication>();private final ConcurrentHashMap<String, String> refreshTokenToAccessTokenStore = new ConcurrentHashMap<String, String>();private final DelayQueue<TokenExpiry> expiryQueue = new DelayQueue<TokenExpiry>();private final ConcurrentHashMap<String, TokenExpiry> expiryMap = new ConcurrentHashMap<String, TokenExpiry>();private int flushInterval = DEFAULT_FLUSH_INTERVAL;private AuthenticationKeyGenerator authenticationKeyGenerator = new DefaultAuthenticationKeyGenerator();private AtomicInteger flushCounter = new AtomicInteger(0); ............................. //从内存中清除token记录 public void removeAccessToken(OAuth2AccessToken accessToken) {removeAccessToken(accessToken.getValue());} //根据clientid获取其所有tokenpublic Collection<OAuth2AccessToken> findTokensByClientId(String clientId) { Collection<OAuth2AccessToken> result = clientIdToAccessTokenStore.get(clientId); return result != null ? Collections.<OAuth2AccessToken> unmodifiableCollection(result) : Collections .<OAuth2AccessToken> emptySet(); } //根据token值,在内存中移除这条token的记录public void removeAccessToken(String tokenValue) {OAuth2AccessToken removed = this.accessTokenStore.remove(tokenValue);this.accessTokenToRefreshTokenStore.remove(tokenValue);// Don't remove the refresh token - it's up to the caller to do thatOAuth2Authentication authentication = this.authenticationStore.remove(tokenValue);if (authentication != null) {this.authenticationToAccessTokenStore.remove(authenticationKeyGenerator.extractKey(authentication));Collection<OAuth2AccessToken> tokens;String clientId = authentication.getOAuth2Request().getClientId();tokens = this.userNameToAccessTokenStore.get(getApprovalKey(clientId, authentication.getName()));if (tokens != null) {tokens.remove(removed);}tokens = this.clientIdToAccessTokenStore.get(clientId);if (tokens != null) {tokens.remove(removed);}this.authenticationToAccessTokenStore.remove(authenticationKeyGenerator.extractKey(authentication));}} ...............}
我们看到这些remove方法,入参要求传入token的,显然我并不知道这个客户端的token的值,我只有这个客户端的对象本身,那么,再看另外的方法findTokensByClientId通过这个方法,可以由clientid获取到这个客户端所有token值,也就是说,接下来,只需要调用这个方法,遍历结果集,逐个调用remove方法移除即可。
事实上,真正坑爹的是,你得不到InMemoryTokensStrore这个类的实例对象,从Spring容器中,竟然获取不了,我试着用@autowride获取它,直接异常。这是,求助官网文档,但是连个P都没说。所以继续扣源码!!
public final class AuthorizationServerEndpointsConfigurer {private AuthorizationServerTokenServices tokenServices;private ConsumerTokenServices consumerTokenServices;private AuthorizationCodeServices authorizationCodeServices;private ResourceServerTokenServices resourceTokenServices;private TokenStore tokenStore;private TokenEnhancer tokenEnhancer;private AccessTokenConverter accessTokenConverter;private ApprovalStore approvalStore;private TokenGranter tokenGranter;private OAuth2RequestFactory requestFactory;private OAuth2RequestValidator requestValidator;private UserApprovalHandler userApprovalHandler;private AuthenticationManager authenticationManager;private ClientDetailsService clientDetailsService;private String prefix;private Map<String, String> patternMap = new HashMap<String, String>();private Set<HttpMethod> allowedTokenEndpointRequestMethods = new HashSet<HttpMethod>();private FrameworkEndpointHandlerMapping frameworkEndpointHandlerMapping;private boolean approvalStoreDisabled;private List<Object> interceptors = new ArrayList<Object>();private DefaultTokenServices defaultTokenServices;private UserDetailsService userDetailsService;private boolean tokenServicesOverride = false;private boolean userDetailsServiceOverride = false;private boolean reuseRefreshToken = true;private WebResponseExceptionTranslator exceptionTranslator; ...................... //记住这里先 public boolean isUserDetailsServiceOverride() {return userDetailsServiceOverride;} ............. public AuthorizationServerEndpointsConfigurer userDetailsService(UserDetailsService userDetailsService) {if (userDetailsService != null) {this.userDetailsService = userDetailsService;this.userDetailsServiceOverride = true;}return this;} ................. //tokenStore是InMemoryTokenStore的父类,这里是关键,我们亲爱的get方法 public TokenStore getTokenStore() {return tokenStore();}
那么如何获取AuthorizationServerEndpointsConfigurer,我尝试用@autowride注入我的服务类,然并卵。那么继续挖,在oauth2验证端的原始配置类中,找到了如下语句:
@Configuration@Order(0)@Import({ ClientDetailsServiceConfiguration.class, AuthorizationServerEndpointsConfiguration.class })public class AuthorizationServerSecurityConfiguration extends WebSecurityConfigurerAdapter {@Autowiredprivate List<AuthorizationServerConfigurer> configurers = Collections.emptyList();@Autowiredprivate ClientDetailsService clientDetailsService;@Autowiredprivate AuthorizationServerEndpointsConfiguration endpoints;@Autowiredpublic void configure(ClientDetailsServiceConfigurer clientDetails) throws Exception {for (AuthorizationServerConfigurer configurer : configurers) {configurer.configure(clientDetails);}}@Overrideprotected void configure(HttpSecurity http) throws Exception {AuthorizationServerSecurityConfigurer configurer = new AuthorizationServerSecurityConfigurer();FrameworkEndpointHandlerMapping handlerMapping = endpoints.oauth2EndpointHandlerMapping();http.setSharedObject(FrameworkEndpointHandlerMapping.class, handlerMapping);configure(configurer);http.apply(configurer);String tokenEndpointPath = handlerMapping.getServletPath("/oauth/token");String tokenKeyPath = handlerMapping.getServletPath("/oauth/token_key");String checkTokenPath = handlerMapping.getServletPath("/oauth/check_token");if (!endpoints.getEndpointsConfigurer().isUserDetailsServiceOverride()) {UserDetailsService userDetailsService = http.getSharedObject(UserDetailsService.class); //注意看这行代码 endpoints.getEndpointsConfigurer().userDetailsService(userDetailsService);}
endpoints.getEndpointsConfigurer().userDetailsService(userDetailsService);看到这一行,你会明白,用这个endpoints.getEndpointsConfigurer(),你可以获取到AuthorizationServerEndpointsConfigurrer对象。那么这个endpoints这货,是怎么拿到的,是@autowride来的,他在spring容器!妥了这回!
我的客户端全部都是自定义的,有自己的服务类,所以,现在我可以在我自己的服务类中的删除客户端方法中这样写:
@Autowiredprivate AuthorizationServerEndpointsConfiguration endpoints;@Transactionalpublic boolean delete(String[] ck_ids) {//执行逻辑删除操作for (String id : ck_ids) {ClientDetail detail=this.baseDao.get(ClientDetail.class, id);//移除这个客户端创建的所有tokenInMemoryTokenStore tokenStore=(InMemoryTokenStore) endpoints.getEndpointsConfigurer().getTokenStore();Collection<OAuth2AccessToken> tokens=tokenStore.findTokensByClientId(detail.getClientName());if(tokens!=null&&tokens.size()>0){for(OAuth2AccessToken accessToken:tokens){tokenStore.removeAccessToken(accessToken);}}//对客户端进行逻辑删除detail.setDeleted(1);this.baseDao.update(detail);}return true;}
打完收工
- 项目使用InMemoryTokenStore时,token有效期设置与强制清除某客户端持有的token
- 新版接口access token的有效期
- struts的token使用
- struts token的使用。
- struts2 token的使用
- token的使用
- Android Token的使用
- struts2的token 使用
- [token] -- token的生成
- Android客户端与服务器交互中的token
- Android客户端与服务器交互中的token
- Android客户端与服务器交互中的token
- Android客户端与服务器交互中的token
- Android客户端与服务器交互中的token
- Android客户端与服务器交互中的token
- Android客户端与服务器交互中的token
- Android客户端与服务器交互中的token
- Android客户端与服务器交互中的token
- c++中通过char*收简单定长报文
- gtk3.0学习笔记5-创建应用 这章楼主还没搞明白 先这样
- Jenkins进阶系列之——04Publish Over FTP Plugin插件
- react native DrawerLayoutAndroid 使用详解
- 微信实验十五、ThinkPHP5.0分页浏览及源码下载
- 项目使用InMemoryTokenStore时,token有效期设置与强制清除某客户端持有的token
- java面试需知
- 推断性统计学(一,二)
- phpmyadmin导出mysql查询的数据为excel分号格式转换为列
- Android 代码里设置ImageView的src和background
- ElasticSearch-5.x 安装head插件
- 问题:基础提供程序在 open 上失败。
- 数据源 和数据库连接池
- 格格她爹讲程序---用传统程序员的方式玩UE4(三)