热点新闻
OAuth2生成token代码备忘
2023-07-26 15:18  浏览:5721  搜索引擎搜索“微商筹货网”
温馨提示:信息一旦丢失不一定找得到,请务必收藏信息以备急用!本站所有信息均是注册会员发布如遇到侵权请联系文章中的联系方式或客服删除!
联系我时,请说明是在微商筹货网看到的信息,谢谢。
展会发布 展会网站大全 报名观展合作 软文发布

一、登录接口(用户名+密码)

1、前端请求auth服务

http://127.0.0.1:72/oauth/pwdLogin

2、请求数据

{ "mobile": "134178101xx", "password": "123456" }

3、Controller方法

@SneakyThrows @PostMapping("pwdLogin") @SignMemberLoginLog(value = "APP_PWD", desc = "密码登录") @ApiOperation(value = "会员登录(密码登录)") public Result<Oauth2TokenDto> pwdLogin(@RequestBody MemberLoginPwdVO vo, HttpServletRequest request) { if (StringUtil.isEmpty(vo.getClientId())) { vo.setClientId("app"); } vo.setIp(IpUtils.ip(request)); Map<String, String> params = getMemberbaseParam(vo, SecurityLoginTypeEnum.APP_PWD.getCode()); params.put("mobile", vo.getMobile()); params.put("password", vo.getPassword()); List<GrantedAuthority> grantedAuthorities = new ArrayList<>(); Oauth2TokenDto oauth2TokenDto = authTokenComponent.getAccessToken(vo.getClientId(), "app", grantedAuthorities, params); return Result.success(oauth2TokenDto); } private Map<String, String> getMemberbaseParam(MemberLoginbaseVO vo, String grantType) { Map<String, String> params = new HashMap<>(); params.put("client_id", vo.getClientId()); params.put("client_secret", "app"); params.put("grant_type", grantType); params.put("scope", "all"); params.put("platform", vo.getPlatform()); //附加信息 params.put("version", vo.getVersion()); params.put("device", vo.getDevice()); params.put("iemi", vo.getIemi()); params.put("location", vo.getLocation()); params.put("ip", vo.getIp()); params.put("recommendCode", vo.getRecommendCode()); return params; }

二、授权接口调用逻辑

2.1 AuthTokenComponent类

import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint; @Component public class AuthTokenComponent { @Autowired private TokenEndpoint tokenEndpoint; public Oauth2TokenDto getAccessToken(String clientId, String clientSecurity , List<GrantedAuthority> grantedAuthorities, Map<String, String> params) throws HttpRequestMethodNotSupportedException { User principle = new User(clientId,clientSecurity,true,true,true,true,grantedAuthorities); return getAccessToken(principle,params); } public Oauth2TokenDto getAccessToken(User principle, Map<String, String> params) throws HttpRequestMethodNotSupportedException { UsernamePasswordAuthenticationToken principal = new UsernamePasswordAuthenticationToken(principle,null,principle.getAuthorities()); OAuth2AccessToken oAuth2AccessToken = tokenEndpoint.postAccessToken(principal, params).getBody(); Oauth2TokenDto oauth2TokenDto = Oauth2TokenDto.builder() .token(oAuth2AccessToken.getValue()) .refreshToken(oAuth2AccessToken.getRefreshToken().getValue()) .expiresIn(oAuth2AccessToken.getExpiresIn()) .tokenHead("Bearer ").build(); return oauth2TokenDto; } }

调用tokenEndpoint.postAccessToken生成token时,接口调用逻辑:
1、调用AuthenticationProvider接口(AdminAuthenticationProvider实现类)密码校验

2、调用UserDetailsService接口(MyUserDetailsService实现类)获取用户信息

3、调用DefaultTokenServices接口(CustomTokenServices实现类)生成token

2.2 AuthenticationProvider接口

1、MobilePasswordAuthenticationProvider实现类

@Setter public class MobilePasswordAuthenticationProvider implements AuthenticationProvider { private QmUserDetailsService userDetailsService; private PasswordEncoder passwordEncoder; @Override public Authentication authenticate(Authentication authentication) { MobilePasswordAuthenticationToken authenticationToken = (MobilePasswordAuthenticationToken) authentication; String mobile = (String) authenticationToken.getPrincipal(); String password = (String) authenticationToken.getCredentials(); SecurityUser user = userDetailsService.loadUserByMobile(mobile); if (user == null) { throw new QiMiaoException(ResultCode.JWT_USER_INVALID); } if(userDetailsService.checkBlock(mobile)) { throw new QiMiaoException(ResultCode.JWT_USER_BLOCK); } if (!passwordEncoder.matches(password, user.getPassword())) { userDetailsService.inc(mobile); throw new QiMiaoException(ResultCode.JWT_USER_INVALID_PWD); } Map<String, String> parameters = (Map<String, String>)authenticationToken.getDetails(); if(null != parameters.get("platform")) { user.setPlatform(parameters.get("platform")); } MobilePasswordAuthenticationToken authenticationResult = new MobilePasswordAuthenticationToken(user, password, user.getAuthorities()); authenticationResult.setDetails(authenticationToken.getDetails()); return authenticationResult; } @Override public boolean supports(Class<?> authentication) { return MobilePasswordAuthenticationToken.class.isAssignableFrom(authentication); } }

2、MobileAuthenticationSecurityConfig配置类

@Component public class MobileAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> { @Autowired private QmUserDetailsService userDetailsService; @Autowired private PasswordEncoder passwordEncoder; @Autowired private SmsVcodeRdsHelper smsVcodeRdsHelper; @Override public void configure(HttpSecurity http) { MobilePasswordAuthenticationProvider provider = new MobilePasswordAuthenticationProvider(); provider.setUserDetailsService(userDetailsService); provider.setPasswordEncoder(passwordEncoder); http.authenticationProvider(provider); MobileSmsAuthenticationProvider smsProvider = new MobileSmsAuthenticationProvider(); smsProvider.setUserDetailsService(userDetailsService); smsProvider.setSmsVcodeRdsHelper(smsVcodeRdsHelper); http.authenticationProvider(smsProvider); MobileoneKeyAuthenticationProvider oneKeyProvider = new MobileoneKeyAuthenticationProvider(); oneKeyProvider.setUserDetailsService(userDetailsService); http.authenticationProvider(oneKeyProvider); VisitorAuthenticationProvider visitorAuthenticationProvider = new VisitorAuthenticationProvider(); visitorAuthenticationProvider.setUserDetailsService(userDetailsService); http.authenticationProvider(visitorAuthenticationProvider); QRCodeAuthenticationProvider qrCodeAuthenticationProvider = new QRCodeAuthenticationProvider(); qrCodeAuthenticationProvider.setUserDetailsService(userDetailsService); http.authenticationProvider(qrCodeAuthenticationProvider); SocialAuthenticationProvider socialAuthenticationProvider = new SocialAuthenticationProvider(); socialAuthenticationProvider.setUserDetailsService(userDetailsService); http.authenticationProvider(socialAuthenticationProvider); } }

3、WebSecurityConfig配置类

@Configuration @EnableWebSecurity @import(DefaultPasswordConfig.class) public class WebSecurityConfig extends WebSecurityConfigurerAdapter { //管理系统登录 @Autowired private AdminAuthenticationSecurityConfig adminAuthenticationSecurityConfig; //App登录 @Autowired private MobileAuthenticationSecurityConfig mobileAuthenticationSecurityConfig; @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll() .antMatchers("/rsa/publicKey", "/actuator @Bean public TokenGranter tokenGranter() { if (tokenGranter == null) { tokenGranter = new TokenGranter() { private CompositeTokenGranter delegate; @Override public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) { if (delegate == null) { delegate = new CompositeTokenGranter(getAllTokenGranters()); } return delegate.grant(grantType, tokenRequest); } }; } return tokenGranter; } private List<TokenGranter> getAllTokenGranters() { AuthorizationServerTokenServices tokenServices = tokenServices(); AuthorizationCodeServices authorizationCodeServices = authorizationCodeServices(); OAuth2RequestFactory requestFactory = requestFactory(); //获取默认的授权模式 List<TokenGranter> tokenGranters = getDefaultTokenGranters(tokenServices, authorizationCodeServices, requestFactory); if (authenticationManager != null) { // 添加social模式 tokenGranters.add(new SocialGranter(authenticationManager, tokenServices, clientDetailsService, requestFactory)); // 添加手机号加密码授权模式 tokenGranters.add(new MobilePwdGranter(authenticationManager, tokenServices, clientDetailsService, requestFactory)); // 添加手机号加密码授权模式 tokenGranters.add(new MobileSmsGranter(authenticationManager, tokenServices, clientDetailsService, requestFactory)); tokenGranters.add(new AdminPwdGranter(authenticationManager, tokenServices, clientDetailsService, requestFactory)); tokenGranters.add(new MobileoneKeyGranter(authenticationManager, tokenServices, clientDetailsService, requestFactory)); tokenGranters.add(new VisitorGranter(authenticationManager, tokenServices, clientDetailsService, requestFactory)); tokenGranters.add(new QRCodeGranter(authenticationManager, tokenServices, clientDetailsService, requestFactory)); } return tokenGranters; } private List<TokenGranter> getDefaultTokenGranters(AuthorizationServerTokenServices tokenServices , AuthorizationCodeServices authorizationCodeServices, OAuth2RequestFactory requestFactory) { List<TokenGranter> tokenGranters = new ArrayList<>(); // 添加授权码模式 tokenGranters.add(new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices, clientDetailsService, requestFactory)); // 添加刷新令牌的模式 tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetailsService, requestFactory)); // 添加隐士授权模式 tokenGranters.add(new ImplicitTokenGranter(tokenServices, clientDetailsService, requestFactory)); // 添加客户端模式 tokenGranters.add(new ClientCredentialsTokenGranter(tokenServices, clientDetailsService, requestFactory)); if (authenticationManager != null) { // 添加密码模式 tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices, clientDetailsService, requestFactory)); } return tokenGranters; } private AuthorizationServerTokenServices tokenServices() { if (tokenServices != null) { return tokenServices; } this.tokenServices = createDefaultTokenServices(); return tokenServices; } private AuthorizationCodeServices authorizationCodeServices() { if (authorizationCodeServices == null) { authorizationCodeServices = new InMemoryAuthorizationCodeServices(); } return authorizationCodeServices; } private OAuth2RequestFactory requestFactory() { return new DefaultOAuth2RequestFactory(clientDetailsService); } private DefaultTokenServices createDefaultTokenServices() { //token互踢 DefaultTokenServices tokenServices = new CustomTokenServices(true); tokenServices.setTokenStore(tokenStore); tokenServices.setSupportRefreshToken(true); tokenServices.setReuseRefreshToken(reuseRefreshToken); tokenServices.setClientDetailsService(clientDetailsService); tokenServices.setTokenEnhancer(tokenEnhancer()); addUserDetailsService(tokenServices, this.userDetailsService); return tokenServices; } private TokenEnhancer tokenEnhancer() { if (tokenEnhancer != null) { TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain(); tokenEnhancerChain.setTokenEnhancers(tokenEnhancer); return tokenEnhancerChain; } return null; } private void addUserDetailsService(DefaultTokenServices tokenServices, UserDetailsService userDetailsService) { if (userDetailsService != null) { PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider(); provider.setPreAuthenticatedUserDetailsService(new UserDetailsByNameServiceWrapper<>(userDetailsService)); tokenServices.setAuthenticationManager(new ProviderManager(Collections.singletonList(provider))); } } }

四、生成toekn详解

生成token前,先从tokenStore里去toeken,看是否已经存在,如果存在则执行挤下线逻辑。tokenStore对应的是实现类com.auth.store.CustomRedisTokenStore

4.1 TokenStore配置类

@Configuration public class AuthRedisTokenStore { @Bean public TokenStore tokenStore(RedisConnectionFactory connectionFactory, RedisSerializer<Object> redisValueSerializer) { return new CustomRedisTokenStore(connectionFactory, redisValueSerializer); } }

4.2 TokenStore实现类

@Slf4j public class CustomRedisTokenStore implements TokenStore { private static final String ACCESS = "{auth}access:"; private static final String AUTH_TO_ACCESS = "{auth}auth_to_access:"; private static final String REFRESH_AUTH = "{auth}refresh_auth:"; private static final String ACCESS_TO_REFRESH = "{auth}access_to_refresh:"; private static final String REFRESH = "{auth}refresh:"; private static final String REFRESH_TO_ACCESS = "{auth}refresh_to_access:"; private static final String RELATION_ID_TOKEN = "{auth}relation_id_token:"; private static final boolean springDataRedis_2_0 = ClassUtils.isPresent( "org.springframework.data.redis.connection.RedisStandaloneConfiguration", RedisTokenStore.class.getClassLoader()); private final RedisConnectionFactory connectionFactory; private AuthenticationKeyGenerator authenticationKeyGenerator = new DefaultAuthenticationKeyGenerator(); private RedisTokenStoreSerializationStrategy serializationStrategy = new JdkSerializationStrategy(); private String prefix = ""; private Method redisConnectionSet_2_0; private RedisSerializer<Object> redisValueSerializer; public CustomRedisTokenStore(RedisConnectionFactory connectionFactory, RedisSerializer<Object> redisValueSerializer) { this.connectionFactory = connectionFactory; this.redisValueSerializer = redisValueSerializer; if (springDataRedis_2_0) { this.loadRedisConnectionMethods_2_0(); } } public void setAuthenticationKeyGenerator(AuthenticationKeyGenerator authenticationKeyGenerator) { this.authenticationKeyGenerator = authenticationKeyGenerator; } public void setSerializationStrategy(RedisTokenStoreSerializationStrategy serializationStrategy) { this.serializationStrategy = serializationStrategy; } public void setPrefix(String prefix) { this.prefix = prefix; } private void loadRedisConnectionMethods_2_0() { this.redisConnectionSet_2_0 = ReflectionUtils.findMethod( RedisConnection.class, "set", byte[].class, byte[].class); } private RedisConnection getConnection() { return connectionFactory.getConnection(); } private byte[] serialize(Object object) { return serializationStrategy.serialize(object); } private byte[] serializeKey(String object) { return serialize(prefix + object); } private OAuth2AccessToken deserializeAccessToken(byte[] bytes) { return serializationStrategy.deserialize(bytes, OAuth2AccessToken.class); } private OAuth2Authentication deserializeAuthentication(byte[] bytes) { return serializationStrategy.deserialize(bytes, OAuth2Authentication.class); } private OAuth2RefreshToken deserializeRefreshToken(byte[] bytes) { return serializationStrategy.deserialize(bytes, OAuth2RefreshToken.class); } private ClientDetails deserializeClientDetails(byte[] bytes) { return (ClientDetails)redisValueSerializer.deserialize(bytes); } private byte[] serialize(String string) { return serializationStrategy.serialize(string); } private String deserializeString(byte[] bytes) { return serializationStrategy.deserializeString(bytes); } @Override public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) { String key = authenticationKeyGenerator.extractKey(authentication); byte[] serializedKey = serializeKey(AUTH_TO_ACCESS + key); byte[] bytes; RedisConnection conn = getConnection(); try { bytes = conn.get(serializedKey); } finally { conn.close(); } OAuth2AccessToken accessToken = deserializeAccessToken(bytes); if (accessToken != null) { OAuth2Authentication storedAuthentication = readAuthentication(accessToken.getValue()); if ((storedAuthentication == null || !key.equals(authenticationKeyGenerator.extractKey(storedAuthentication)))) { // Keep the stores consistent (maybe the same user is // represented by this authentication but the details have // changed) storeAccessToken(accessToken, authentication); } } return accessToken; } @Override public OAuth2Authentication readAuthentication(OAuth2AccessToken token) { OAuth2Authentication auth2Authentication = readAuthentication(token.getValue()); //是否开启token续签 boolean isRenew = true; if (isRenew && auth2Authentication != null) { OAuth2Request clientAuth = auth2Authentication.getOAuth2Request(); //判断当前应用是否需要自动续签 if (checkRenewClientId(clientAuth.getClientId())) { //获取过期时长 int validitySeconds = 2592000; double expiresRatio = token.getExpiresIn() / (double)validitySeconds; //判断是否需要续签,当前剩余时间小于过期时长的50%则续签 if (expiresRatio <= 0.5) { //更新AccessToken过期时间 DefaultOAuth2AccessToken oAuth2AccessToken = (DefaultOAuth2AccessToken) token; oAuth2AccessToken.setExpiration(new Date(System.currentTimeMillis() + (validitySeconds * 1000L))); storeAccessToken(oAuth2AccessToken, auth2Authentication, true); } } } return auth2Authentication; } private boolean checkRenewClientId(String clientId) { boolean result = true; return result; } public String getToken(String id){ byte[] relationIdTokenKey = getRelationIdTokenKey(id); RedisConnection conn = getConnection(); try { byte[] bytes = conn.get(relationIdTokenKey); return deserializeString(bytes); } finally { conn.close(); } } private int getAccessTokenValiditySeconds(String clientId) { RedisConnection conn = getConnection(); byte[] bytes; try { bytes = conn.get(serializeKey(SecurityConstants.CACHE_CLIENT_KEY + ":" + clientId)); } finally { conn.close(); } if (bytes != null) { ClientDetails clientDetails = deserializeClientDetails(bytes); if (clientDetails.getAccessTokenValiditySeconds() != null) { return clientDetails.getAccessTokenValiditySeconds(); } } //返回默认值 return SecurityConstants.ACCESS_TOKEN_VALIDITY_SECONDS; } @Override public OAuth2Authentication readAuthentication(String token) { byte[] bytes; RedisConnection conn = getConnection(); try { bytes = conn.get(serializeKey(SecurityConstants.REDIS_TOKEN_AUTH + token)); } finally { conn.close(); } return deserializeAuthentication(bytes); } @Override public OAuth2Authentication readAuthenticationForRefreshToken(OAuth2RefreshToken token) { return readAuthenticationForRefreshToken(token.getValue()); } public OAuth2Authentication readAuthenticationForRefreshToken(String token) { RedisConnection conn = getConnection(); try { byte[] bytes = conn.get(serializeKey(REFRESH_AUTH + token)); return deserializeAuthentication(bytes); } finally { conn.close(); } } @Override public void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) { storeAccessToken(token, authentication, false); } private byte[] getRelationIdTokenKey(String id){ return serializeKey(RELATION_ID_TOKEN + id); } private byte[] getRelationIdTokenKey(OAuth2Authentication authentication){ byte[] relationIdTokenKey = null; Object obj = authentication.getPrincipal(); Object details = authentication.getUserAuthentication().getDetails(); if(obj!=null && details!=null){ if(obj instanceof SecurityUser && details instanceof HashMap){ SecurityUser user = (SecurityUser)obj; Map map = (Map)details; String clientId = String.valueOf(map.get("client_id")); Long userId = user.getId(); relationIdTokenKey = serializeKey(RELATION_ID_TOKEN +clientId+":"+userId); } }else{ log.error("storeAccessToken 没有取到principal"); } return relationIdTokenKey; } public SecurityUser getSecurityUser(String token){ SecurityUser user = null; OAuth2Authentication auth2Authentication = readAuthentication(token); if (auth2Authentication != null) { Object obj = auth2Authentication.getPrincipal(); if (obj!=null && obj instanceof SecurityUser) { user = (SecurityUser) obj; }else{ log.error("getSecurityUser:解析User失败,"+obj); } }else{ log.error("getSecurityUser:auth2Authentication 为nuLl"); } return user; } private void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication, boolean isRenew) { byte[] serializedAccessToken = serialize(token); byte[] serializedAuth = serialize(authentication); byte[] serializedToken = serialize(token.getValue()); byte[] accessKey = serializeKey(ACCESS + token.getValue()); byte[] authKey = serializeKey(SecurityConstants.REDIS_TOKEN_AUTH + token.getValue()); byte[] authToAccessKey = serializeKey(AUTH_TO_ACCESS + authenticationKeyGenerator.extractKey(authentication)); byte[] approvalKey = serializeKey(SecurityConstants.REDIS_UNAME_TO_ACCESS + getApprovalKey(authentication)); byte[] clientId = serializeKey(SecurityConstants.REDIS_CLIENT_ID_TO_ACCESS + authentication.getOAuth2Request().getClientId()); byte[] relationIdTokenKey = getRelationIdTokenKey(authentication); RedisConnection conn = getConnection(); try { byte[] oldAccessToken = conn.get(accessKey); //如果token已存在,并且不是续签的话直接返回 if (!isRenew && oldAccessToken != null) { return; } conn.openPipeline(); if (springDataRedis_2_0) { try { this.redisConnectionSet_2_0.invoke(conn, accessKey, serializedAccessToken); this.redisConnectionSet_2_0.invoke(conn, authKey, serializedAuth); this.redisConnectionSet_2_0.invoke(conn, authToAccessKey, serializedAccessToken); if(relationIdTokenKey!=null){ this.redisConnectionSet_2_0.invoke(conn, relationIdTokenKey, serializedToken); } } catch (Exception ex) { throw new RuntimeException(ex); } } else { conn.set(accessKey, serializedAccessToken); conn.set(authKey, serializedAuth); conn.set(authToAccessKey, serializedAccessToken); if(relationIdTokenKey!=null){ conn.set(relationIdTokenKey, serializedToken); } } //如果是续签token,需要先删除集合里旧的值 if (oldAccessToken != null) { if (!authentication.isClientonly()) { conn.lRem(approvalKey, 1, oldAccessToken); } conn.lRem(clientId, 1, oldAccessToken); } if (!authentication.isClientonly()) { conn.rPush(approvalKey, serializedAccessToken); } conn.rPush(clientId, serializedAccessToken); if (token.getExpiration() != null) { int seconds = token.getExpiresIn(); conn.expire(accessKey, seconds); conn.expire(authKey, seconds); conn.expire(authToAccessKey, seconds); conn.expire(clientId, seconds); conn.expire(approvalKey, seconds); } OAuth2RefreshToken refreshToken = token.getRefreshToken(); if (refreshToken != null && refreshToken.getValue() != null) { byte[] refresh = serialize(token.getRefreshToken().getValue()); byte[] auth = serialize(token.getValue()); byte[] refreshToAccessKey = serializeKey(REFRESH_TO_ACCESS + token.getRefreshToken().getValue()); byte[] accessToRefreshKey = serializeKey(ACCESS_TO_REFRESH + token.getValue()); if (springDataRedis_2_0) { try { this.redisConnectionSet_2_0.invoke(conn, refreshToAccessKey, auth); this.redisConnectionSet_2_0.invoke(conn, accessToRefreshKey, refresh); } catch (Exception ex) { throw new RuntimeException(ex); } } else { conn.set(refreshToAccessKey, auth); conn.set(accessToRefreshKey, refresh); } expireRefreshToken(refreshToken, conn, refreshToAccessKey, accessToRefreshKey); } conn.closePipeline(); } finally { conn.close(); } } private static String getApprovalKey(OAuth2Authentication authentication) { String userName = authentication.getUserAuthentication() == null ? "" : authentication.getUserAuthentication().getName(); return getApprovalKey(authentication.getOAuth2Request().getClientId(), userName); } private static String getApprovalKey(String clientId, String userName) { return clientId + (userName == null ? "" : ":" + userName); } @Override public void removeAccessToken(OAuth2AccessToken accessToken) { removeAccessToken(accessToken.getValue()); } @Override public OAuth2AccessToken readAccessToken(String tokenValue) { byte[] key = serializeKey(ACCESS + tokenValue); byte[] bytes; RedisConnection conn = getConnection(); try { bytes = conn.get(key); } finally { conn.close(); } return deserializeAccessToken(bytes); } public void removeAccessToken(String tokenValue) { byte[] accessKey = serializeKey(ACCESS + tokenValue); byte[] authKey = serializeKey(SecurityConstants.REDIS_TOKEN_AUTH + tokenValue); byte[] accessToRefreshKey = serializeKey(ACCESS_TO_REFRESH + tokenValue); RedisConnection conn = getConnection(); try { byte[] access = conn.get(accessKey); byte[] auth = conn.get(authKey); conn.openPipeline(); conn.del(accessKey); conn.del(accessToRefreshKey); // Don't remove the refresh token - it's up to the caller to do that conn.del(authKey); conn.closePipeline(); OAuth2Authentication authentication = deserializeAuthentication(auth); if (authentication != null) { String key = authenticationKeyGenerator.extractKey(authentication); byte[] relationIdTokenKey = getRelationIdTokenKey(authentication); byte[] authToAccessKey = serializeKey(AUTH_TO_ACCESS + key); byte[] unameKey = serializeKey(SecurityConstants.REDIS_UNAME_TO_ACCESS + getApprovalKey(authentication)); byte[] clientId = serializeKey(SecurityConstants.REDIS_CLIENT_ID_TO_ACCESS + authentication.getOAuth2Request().getClientId()); conn.openPipeline(); conn.del(authToAccessKey); conn.lRem(unameKey, 1, access); conn.lRem(clientId, 1, access); conn.del(serialize(ACCESS + key)); if(relationIdTokenKey!=null){ conn.del(relationIdTokenKey); } conn.closePipeline(); } } catch (Exception e){ e.printStackTrace(); log.error("removeAccessToken 失败:{}",e.getMessage()); }finally { conn.close(); } } @Override public void storeRefreshToken(OAuth2RefreshToken refreshToken, OAuth2Authentication authentication) { RedisConnection conn = getConnection(); try { byte[] refreshKey = serializeKey(REFRESH + refreshToken.getValue()); byte[] refreshAuthKey = serializeKey(REFRESH_AUTH + refreshToken.getValue()); byte[] serializedRefreshToken = serialize(refreshToken); conn.openPipeline(); if (springDataRedis_2_0) { try { this.redisConnectionSet_2_0.invoke(conn, refreshKey, serializedRefreshToken); this.redisConnectionSet_2_0.invoke(conn, refreshAuthKey, serialize(authentication)); } catch (Exception ex) { throw new RuntimeException(ex); } } else { conn.set(refreshKey, serializedRefreshToken); conn.set(refreshAuthKey, serialize(authentication)); } expireRefreshToken(refreshToken, conn, refreshKey, refreshAuthKey); conn.closePipeline(); } catch (Exception e){ e.printStackTrace(); log.error("storeRefreshToken 失败:{}",e.getMessage()); }finally { conn.close(); } } private void expireRefreshToken(OAuth2RefreshToken refreshToken, RedisConnection conn, byte[] refreshKey, byte[] refreshAuthKey) { if (refreshToken instanceof ExpiringOAuth2RefreshToken) { ExpiringOAuth2RefreshToken expiringRefreshToken = (ExpiringOAuth2RefreshToken) refreshToken; Date expiration = expiringRefreshToken.getExpiration(); if (expiration != null) { int seconds = Long.valueOf((expiration.getTime() - System.currentTimeMillis()) / 1000L) .intValue(); conn.expire(refreshKey, seconds); conn.expire(refreshAuthKey, seconds); } } } @Override public OAuth2RefreshToken readRefreshToken(String tokenValue) { byte[] key = serializeKey(REFRESH + tokenValue); byte[] bytes; RedisConnection conn = getConnection(); try { bytes = conn.get(key); } finally { conn.close(); } return deserializeRefreshToken(bytes); } @Override public void removeRefreshToken(OAuth2RefreshToken refreshToken) { removeRefreshToken(refreshToken.getValue()); } public void removeRefreshToken(String tokenValue) { RedisConnection conn = getConnection(); try { byte[] refreshKey = serializeKey(REFRESH + tokenValue); byte[] refreshAuthKey = serializeKey(REFRESH_AUTH + tokenValue); byte[] refresh2AccessKey = serializeKey(REFRESH_TO_ACCESS + tokenValue); byte[] access2RefreshKey = serializeKey(ACCESS_TO_REFRESH + tokenValue); conn.openPipeline(); conn.del(refreshKey); conn.del(refreshAuthKey); conn.del(refresh2AccessKey); conn.del(access2RefreshKey); conn.closePipeline(); } catch (Exception e){ e.printStackTrace(); log.error("removeRefreshToken 失败:{}",e.getMessage()); }finally { conn.close(); } } @Override public void removeAccessTokenUsingRefreshToken(OAuth2RefreshToken refreshToken) { removeAccessTokenUsingRefreshToken(refreshToken.getValue()); } private void removeAccessTokenUsingRefreshToken(String refreshToken) { byte[] key = serializeKey(REFRESH_TO_ACCESS + refreshToken); RedisConnection conn = getConnection(); byte[] bytes = null; try { bytes = conn.get(key); conn.del(key); } finally { conn.close(); } if (bytes == null) { return; } String accessToken = deserializeString(bytes); if (accessToken != null) { removeAccessToken(accessToken); } } @Override public Collection<OAuth2AccessToken> findTokensByClientIdAndUserName(String clientId, String userName) { byte[] approvalKey = serializeKey(SecurityConstants.REDIS_UNAME_TO_ACCESS + getApprovalKey(clientId, userName)); List<byte[]> byteList; RedisConnection conn = getConnection(); try { byteList = conn.lRange(approvalKey, 0, -1); } finally { conn.close(); } return getTokenCollections(byteList); } @Override public Collection<OAuth2AccessToken> findTokensByClientId(String clientId) { byte[] key = serializeKey(SecurityConstants.REDIS_CLIENT_ID_TO_ACCESS + clientId); List<byte[]> byteList; RedisConnection conn = getConnection(); try { byteList = conn.lRange(key, 0, -1); } finally { conn.close(); } return getTokenCollections(byteList); } private Collection<OAuth2AccessToken> getTokenCollections(List<byte[]> byteList) { if (byteList == null || byteList.size() == 0) { return Collections.emptySet(); } List<OAuth2AccessToken> accessTokens = new ArrayList<>(byteList.size()); for (byte[] bytes : byteList) { OAuth2AccessToken accessToken = deserializeAccessToken(bytes); accessTokens.add(accessToken); } return Collections.unmodifiableCollection(accessTokens); } }

五、token续期

方式:后端自动续期,APP前端不用处理。

在gateway服务中,每次校验token时,如果token离过期时间小于24小时,则自动续期。

5.1 gateway服务代码

public class CustomAuthenticationManager implements ReactiveAuthenticationManager { private TokenStore tokenStore; public CustomAuthenticationManager(TokenStore tokenStore) { this.tokenStore = tokenStore; } @Override public Mono<Authentication> authenticate(Authentication authentication) { return Mono.justOrEmpty(authentication) .filter(a -> a instanceof BearerTokenAuthenticationToken) .cast(BearerTokenAuthenticationToken.class) .map(BearerTokenAuthenticationToken::getToken) .flatMap((accessTokenValue -> { OAuth2AccessToken accessToken = tokenStore.readAccessToken(accessTokenValue); if (accessToken == null) { throw new AlanChenException(ResultCode.UNAUTHORIZED,"登录状态失效"); } else if (accessToken.isExpired()) { throw new AlanChenException(ResultCode.UNAUTHORIZED,"登录状态失效"); } else { OAuth2RefreshToken refreshToken= tokenStore.readRefreshToken(accessToken.getRefreshToken().getValue()); if(null == refreshToken) { throw new AlanChenException(ResultCode.JWT_OFFLINE,"账号在其他设备登录了"); } } // token续期代码在readAuthentication方法里 OAuth2Authentication result = tokenStore.readAuthentication(accessToken); if (result == null) { throw new AlanChenException(ResultCode.FORBIDDEN,"没有权限"); } return Mono.just(result); })) .cast(Authentication.class); } }

注意:gateway这里取token不是直接用的App前端传过来的token,而是通过Authentication来取的,这有这样后端token续期了才会有效。

5.2 CustomRedisTokenStore代码

@Slf4j public class CustomRedisTokenStore implements TokenStore { //其他代码省略 @Override public OAuth2Authentication readAuthentication(OAuth2AccessToken token) { OAuth2Authentication auth2Authentication = readAuthentication(token.getValue()); if (auth2Authentication != null) { //获取过期时长 int validitySeconds = 2592000; //2592000 int expiresRatio = validitySeconds - token.getExpiresIn(); //判断是否需要续签,当前剩余时间小于过期时长的24小时则续签 if (expiresRatio >= 24 * 3600) { //更新AccessToken过期时间 DefaultOAuth2AccessToken oAuth2AccessToken = (DefaultOAuth2AccessToken) token; Date expiration = new Date(System.currentTimeMillis() + (validitySeconds * 1000L)); oAuth2AccessToken.setExpiration(expiration); storeAccessToken(oAuth2AccessToken, auth2Authentication, true); OAuth2RefreshToken refreshToken = new DefaultExpiringOAuth2RefreshToken(token.getRefreshToken().getValue(), expiration); storeRefreshToken(refreshToken, auth2Authentication); } } return auth2Authentication; } }

发布人:39e9****    IP:120.230.91.***     举报/删稿
展会推荐
让朕来说2句
评论
收藏
点赞
转发