From 60c4b7a2bba066843565b18934954bb6113d2190 Mon Sep 17 00:00:00 2001 From: zlt2000 Date: Thu, 28 Jan 2021 21:09:01 +0800 Subject: [PATCH] =?UTF-8?q?=E7=99=BB=E5=BD=95=E5=90=8C=E5=BA=94=E7=94=A8?= =?UTF-8?q?=E5=90=8C=E8=B4=A6=E5=8F=B7=E4=BA=92=E8=B8=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth/config/TokenGranterConfig.java | 10 +- .../service/impl/CustomTokenServices.java | 128 ++++++++++++++++++ zlt-uaa/src/main/resources/application.yml | 5 +- 3 files changed, 141 insertions(+), 2 deletions(-) create mode 100644 zlt-uaa/src/main/java/com/central/oauth/service/impl/CustomTokenServices.java diff --git a/zlt-uaa/src/main/java/com/central/oauth/config/TokenGranterConfig.java b/zlt-uaa/src/main/java/com/central/oauth/config/TokenGranterConfig.java index e020224..b6adc82 100644 --- a/zlt-uaa/src/main/java/com/central/oauth/config/TokenGranterConfig.java +++ b/zlt-uaa/src/main/java/com/central/oauth/config/TokenGranterConfig.java @@ -4,7 +4,9 @@ import com.central.oauth.granter.MobilePwdGranter; import com.central.oauth.granter.OpenIdGranter; import com.central.oauth.granter.PwdImgCodeGranter; import com.central.oauth.service.IValidateCodeService; +import com.central.oauth.service.impl.CustomTokenServices; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; @@ -67,6 +69,12 @@ public class TokenGranterConfig { private TokenGranter tokenGranter; + /** + * 是否登录同应用同账号互踢 + */ + @Value("${zlt.uaa.isSingleLogin:false}") + private boolean isSingleLogin; + /** * 授权模式 */ @@ -149,7 +157,7 @@ public class TokenGranterConfig { } private DefaultTokenServices createDefaultTokenServices() { - DefaultTokenServices tokenServices = new DefaultTokenServices(); + DefaultTokenServices tokenServices = new CustomTokenServices(isSingleLogin); tokenServices.setTokenStore(tokenStore); tokenServices.setSupportRefreshToken(true); tokenServices.setReuseRefreshToken(reuseRefreshToken); diff --git a/zlt-uaa/src/main/java/com/central/oauth/service/impl/CustomTokenServices.java b/zlt-uaa/src/main/java/com/central/oauth/service/impl/CustomTokenServices.java new file mode 100644 index 0000000..aef5351 --- /dev/null +++ b/zlt-uaa/src/main/java/com/central/oauth/service/impl/CustomTokenServices.java @@ -0,0 +1,128 @@ +package com.central.oauth.service.impl; + +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.oauth2.common.*; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.token.DefaultTokenServices; +import org.springframework.security.oauth2.provider.token.TokenEnhancer; +import org.springframework.security.oauth2.provider.token.TokenStore; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; +import java.util.UUID; + +/** + * 重写 DefaultTokenServices,实现登录同应用同账号互踢 + * + * @author zlt + * @date 2021/1/28 + *

+ * Blog: https://zlt2000.gitee.io + * Github: https://github.com/zlt2000 + */ +public class CustomTokenServices extends DefaultTokenServices { + private TokenStore tokenStore; + private TokenEnhancer accessTokenEnhancer; + + /** + * 是否登录同应用同账号互踢 + */ + private boolean isSingleLogin; + + public CustomTokenServices(boolean isSingleLogin) { + this.isSingleLogin = isSingleLogin; + } + + @Override + @Transactional + public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException { + OAuth2AccessToken existingAccessToken = tokenStore.getAccessToken(authentication); + OAuth2RefreshToken refreshToken = null; + if (existingAccessToken != null) { + if (isSingleLogin) { + if (existingAccessToken.getRefreshToken() != null) { + tokenStore.removeRefreshToken(existingAccessToken.getRefreshToken()); + } + tokenStore.removeAccessToken(existingAccessToken); + } else if (existingAccessToken.isExpired()) { + if (existingAccessToken.getRefreshToken() != null) { + refreshToken = existingAccessToken.getRefreshToken(); + // The token store could remove the refresh token when the + // access token is removed, but we want to + // be sure... + tokenStore.removeRefreshToken(refreshToken); + } + tokenStore.removeAccessToken(existingAccessToken); + } + else { + // Re-store the access token in case the authentication has changed + tokenStore.storeAccessToken(existingAccessToken, authentication); + return existingAccessToken; + } + } + + // Only create a new refresh token if there wasn't an existing one + // associated with an expired access token. + // Clients might be holding existing refresh tokens, so we re-use it in + // the case that the old access token + // expired. + if (refreshToken == null) { + refreshToken = createRefreshToken(authentication); + } + // But the refresh token itself might need to be re-issued if it has + // expired. + else if (refreshToken instanceof ExpiringOAuth2RefreshToken) { + ExpiringOAuth2RefreshToken expiring = (ExpiringOAuth2RefreshToken) refreshToken; + if (System.currentTimeMillis() > expiring.getExpiration().getTime()) { + refreshToken = createRefreshToken(authentication); + } + } + + OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken); + tokenStore.storeAccessToken(accessToken, authentication); + // In case it was modified + refreshToken = accessToken.getRefreshToken(); + if (refreshToken != null) { + tokenStore.storeRefreshToken(refreshToken, authentication); + } + return accessToken; + + } + + private OAuth2RefreshToken createRefreshToken(OAuth2Authentication authentication) { + if (!isSupportRefreshToken(authentication.getOAuth2Request())) { + return null; + } + int validitySeconds = getRefreshTokenValiditySeconds(authentication.getOAuth2Request()); + String value = UUID.randomUUID().toString(); + if (validitySeconds > 0) { + return new DefaultExpiringOAuth2RefreshToken(value, new Date(System.currentTimeMillis() + + (validitySeconds * 1000L))); + } + return new DefaultOAuth2RefreshToken(value); + } + + 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; + } + + @Override + public void setTokenStore(TokenStore tokenStore) { + this.tokenStore = tokenStore; + super.setTokenStore(tokenStore); + } + + @Override + public void setTokenEnhancer(TokenEnhancer accessTokenEnhancer) { + this.accessTokenEnhancer = accessTokenEnhancer; + super.setTokenEnhancer(accessTokenEnhancer); + } +} diff --git a/zlt-uaa/src/main/resources/application.yml b/zlt-uaa/src/main/resources/application.yml index dd7b100..b3d0ae6 100644 --- a/zlt-uaa/src/main/resources/application.yml +++ b/zlt-uaa/src/main/resources/application.yml @@ -32,4 +32,7 @@ zlt: tenant: enable: true ignoreTables: - - oauth_client_details \ No newline at end of file + - oauth_client_details + #是否登录同应用同账号互踢 + uaa: + isSingleLogin: false \ No newline at end of file -- GitLab