{"id":1422,"date":"2023-03-20T21:49:45","date_gmt":"2023-03-20T13:49:45","guid":{"rendered":"https:\/\/www.appblog.cn\/?p=1422"},"modified":"2023-04-28T21:04:32","modified_gmt":"2023-04-28T13:04:32","slug":"spring-security-oauth2-redis-storage-token-refresh-token-never-expires-problem-resolution","status":"publish","type":"post","link":"https:\/\/www.appblog.cn\/index.php\/2023\/03\/20\/spring-security-oauth2-redis-storage-token-refresh-token-never-expires-problem-resolution\/","title":{"rendered":"Spring Security OAuth2 Redis\u5b58\u50a8token refresh_token\u6c38\u4e0d\u8fc7\u671f\u95ee\u9898\u89e3\u51b3"},"content":{"rendered":"<p><code>OAuth2AccessToken<\/code>\u63a5\u53e3\u7684\u9ed8\u8ba4\u5b9e\u73b0\u662f<code>DefaultOAuth2AccessToken<\/code>\u7c7b\uff08\u81ea\u5e26\u8fc7\u671f\u65f6\u95f4\u5c5e\u6027\uff09<br \/>\n<code>OAuth2RefreshToken<\/code>\u63a5\u53e3\u7684\u9ed8\u8ba4\u5b9e\u73b0\u662f<code>DefaultOAuth2RefreshToken<\/code>\u7c7b\uff08\u4e0d\u5e26\u8fc7\u671f\u65f6\u95f4\u5c5e\u6027\uff09<br \/>\n<code>ExpiringOAuth2RefreshToken<\/code>\u63a5\u53e3\u7236\u63a5\u53e3\u662f<code>OAuth2RefreshToken<\/code>\uff0c<code>ExpiringOAuth2RefreshToken<\/code>\u7684\u9ed8\u8ba4\u5b9e\u73b0\u662f<code>DefaultExpiringOAuth2RefreshToken<\/code>\uff08\u81ea\u5e26\u8fc7\u671f\u65f6\u95f4\u5c5e\u6027\uff09<\/p>\n<h2>\u81ea\u5b9a\u4e49\u65b9\u5f0f\u751f\u6210access_token\u548crefresh_token<\/h2>\n<p><!-- more --><\/p>\n<pre><code class=\"language-java\">\/**\n * @Description: \u7528\u6237\u81ea\u5b9a\u4e49token\u4ee4\u724c\uff0c\u5305\u62ecaccess_token\u548crefresh_token\n * @Package: cn.appblog.security.oauth2.enhancer.UserTokenEnhancer\n * @Version: 1.0\n *\/\npublic class UserTokenEnhancer implements TokenEnhancer {\n    \/**\n     * \u91cd\u65b0\u5b9a\u4e49\u4ee4\u724ctoken\n     *\/\n    @Override\n    public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {\n       if(accessToken instanceof DefaultOAuth2AccessToken){\n           DefaultOAuth2AccessToken token = (DefaultOAuth2AccessToken) accessToken;\n           token.setValue(getToken());\n           OAuth2RefreshToken refreshToken = token.getRefreshToken();\n           if(refreshToken instanceof OAuth2RefreshToken){\n               token.setRefreshToken(new OAuth2RefreshToken(getToken()));\n           }\n           Map&lt;String, Object&gt; additionalInformation = Maps.newHashMap();\n           additionalInformation.put(&quot;client_id&quot;, authentication.getOAuth2Request().getClientId());\n           \/\/\u6dfb\u52a0\u989d\u5916\u914d\u7f6e\u4fe1\u606f\n           token.setAdditionalInformation(additionalInformation);\n           return token;\n       }\n        return accessToken;\n    }\n\n    \/**\n     * \u751f\u6210\u81ea\u5b9a\u4e49token\n     *\/\n    private String getToken() {\n        return StringUtils.join(UUID.randomUUID().toString().replace(&quot;-&quot;, &quot;&quot;));\n    }\n}<\/code><\/pre>\n<p>\u5728\u5b9e\u9645\u7684\u6d4b\u8bd5\u73af\u5883\u4e2d\u6211\u53ef\u4ee5\u62ff\u5230access_token\u7684\u8fc7\u671f\u65f6\u95f4\uff0c\u5e76\u4e14\u5728redis\u7684\u5ba2\u6237\u7aef\u67e5\u770baccess_token\u76f8\u5173\u952e\u503c\u5bf9\u90fd\u662f\u8ddf\u6211\u8bbe\u7f6e\u7684\u8fc7\u671f\u65f6\u95f4\u662f\u4e00\u81f4\u7684\uff0c\u4f46\u662frefresh_token\u8bbe\u7f6e\u7684\u8fc7\u671f<br \/>\n\u65f6\u95f4\u4e00\u76f4\u4e0d\u8d77\u4f5c\u7528\uff0cTTL\u663e\u793a-1\uff0c\u4e5f\u5c31\u662f\u4e00\u76f4\u6709\u6548\uff0c\u611f\u89c9\u5c31\u5f88\u5947\u602a\uff0c\u6240\u4ee5\u7ffb\u770bTokenStore\u7684\u5b9e\u73b0\u7c7bRedisTokenStore<\/p>\n<p>\u751f\u6210access_token\u952e\u503c\u5bf9\u7684\u4ee3\u7801\u5982\u4e0b\uff1a<\/p>\n<pre><code class=\"language-java\">public void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {\n   \/\/\u5e8f\u5217\u5316\u76f8\u5173key\n    byte[] serializedAccessToken = this.serialize((Object)token);\n    byte[] serializedAuth = this.serialize((Object)authentication);\n    byte[] accessKey = this.serializeKey(&quot;access:&quot; + token.getValue());\n    byte[] authKey = this.serializeKey(&quot;auth:&quot; + token.getValue());\n    byte[] authToAccessKey = this.serializeKey(&quot;auth_to_access:&quot; + this.authenticationKeyGenerator.extractKey(authentication));\n    byte[] approvalKey = this.serializeKey(&quot;uname_to_access:&quot; + getApprovalKey(authentication));\n    byte[] clientId = this.serializeKey(&quot;client_id_to_access:&quot; + authentication.getOAuth2Request().getClientId());\n    RedisConnection conn = this.getConnection();\n\n    try {\n        conn.openPipeline();\n        if (springDataRedis_2_0) {\n            try {\n                \/\/\u5b58\u50a8\u952e\u503c\u5bf9\n                this.redisConnectionSet_2_0.invoke(conn, accessKey, serializedAccessToken);\n                this.redisConnectionSet_2_0.invoke(conn, authKey, serializedAuth);\n                this.redisConnectionSet_2_0.invoke(conn, authToAccessKey, serializedAccessToken);\n            } catch (Exception var24) {\n                throw new RuntimeException(var24);\n            }\n        } else {\n            conn.set(accessKey, serializedAccessToken);\n            conn.set(authKey, serializedAuth);\n            conn.set(authToAccessKey, serializedAccessToken);\n        }\n\n        if (!authentication.isClientOnly()) {\n            conn.sAdd(approvalKey, new byte[][]{serializedAccessToken});\n        }\n\n        conn.sAdd(clientId, new byte[][]{serializedAccessToken});\n        \/\/\u8bbe\u7f6eaccess_token\u8fc7\u671f\u65f6\u95f4\n        if (token.getExpiration() != null) {\n            int seconds = token.getExpiresIn();\n            conn.expire(accessKey, (long)seconds);\n            conn.expire(authKey, (long)seconds);\n            conn.expire(authToAccessKey, (long)seconds);\n            conn.expire(clientId, (long)seconds);\n            conn.expire(approvalKey, (long)seconds);\n        }\n\n        OAuth2RefreshToken refreshToken = token.getRefreshToken();\n        if (refreshToken != null &amp;&amp; refreshToken.getValue() != null) {\n            byte[] refresh = this.serialize(token.getRefreshToken().getValue());\n            byte[] auth = this.serialize(token.getValue());\n            byte[] refreshToAccessKey = this.serializeKey(&quot;refresh_to_access:&quot; + token.getRefreshToken().getValue());\n            byte[] accessToRefreshKey = this.serializeKey(&quot;access_to_refresh:&quot; + token.getValue());\n            if (springDataRedis_2_0) {\n                try {\n                    this.redisConnectionSet_2_0.invoke(conn, refreshToAccessKey, auth);\n                    this.redisConnectionSet_2_0.invoke(conn, accessToRefreshKey, refresh);\n                } catch (Exception var23) {\n                    throw new RuntimeException(var23);\n                }\n            } else {\n                conn.set(refreshToAccessKey, auth);\n                conn.set(accessToRefreshKey, refresh);\n            }\n            \/\/\u5224\u65adrefresh_token\u5bf9\u8c61\u662f\u5426\u662fExpiringOAuth2RefreshToken\u7684\u5b9e\u4f8b\u5bf9\u8c61\uff0c\u8fd9\u4e00\u6bb5\u662f\u8bbe\u7f6erefresh_token\u7684\u5173\u952e\uff0c\u5982\u679c\u662f\u5c31\u4f1a\u8fdb\u884c\u8fc7\u671f\u65f6\u95f4\n            \/\/\u8bbe\u7f6e\uff0c\u5426\u5219\u751f\u6210\u7684refresh_token\u76f8\u5173\u7684\u952e\u503c\u5bf9\u6c38\u8fdc\u6709\u6548          \n            if (refreshToken instanceof ExpiringOAuth2RefreshToken) {\n                ExpiringOAuth2RefreshToken expiringRefreshToken = (ExpiringOAuth2RefreshToken)refreshToken;\n                Date expiration = expiringRefreshToken.getExpiration();\n                if (expiration != null) {\n                    int seconds = Long.valueOf((expiration.getTime() - System.currentTimeMillis()) \/ 1000L).intValue();\n                    conn.expire(refreshToAccessKey, (long)seconds);\n                    conn.expire(accessToRefreshKey, (long)seconds);\n                }\n            }\n        }\n\n        conn.closePipeline();\n    } finally {\n        conn.close();\n    }\n}<\/code><\/pre>\n<p>\u4e0a\u9762\u7684<code>refreshToken instanceof ExpiringOAuth2RefreshToken<\/code>\u8fd9\u6bb5\u4ee3\u7801\u662f\u6765\u5224\u65ad\u5237\u65b0token\u662f\u5426\u662f\u5e26\u6709\u6709\u6548\u671f\u65f6\u95f4\u7684<code>ExpiringOAuth2RefreshToken<\/code>\u5b9e\u4f8b\u5bf9\u8c61\uff0c\u6211\u4eec\u53ef\u4ee5<br \/>\n\u770b\u5230\u4e0a\u9762\u81ea\u5b9a\u4e49\u7684\u751f\u6210<code>refresh_token<\/code>\u7684\u5b9e\u4f8b\u5bf9\u8c61\u662f<code>OAuth2RefreshToken<\/code>\u7c7b\u578b\uff0c\u53ea\u5e26\u6709\u4e00\u4e2a<code>refresh_token<\/code>\u503c\uff0c\u800c\u6ca1\u6709\u6709\u6548\u65f6\u95f4\u7684\u5b57\u6bb5\u503c\uff0c\u6211\u4eec\u770b\u4e0b<code>ExpiringOAuth2RefreshToken<\/code>\u7c7b\u7684\u6e90\u7801\uff1a<\/p>\n<pre><code class=\"language-java\">package org.springframework.security.oauth2.common;\n\nimport java.util.Date;\n\npublic interface ExpiringOAuth2RefreshToken extends OAuth2RefreshToken {\n    Date getExpiration();\n}<\/code><\/pre>\n<p>\u53ef\u4ee5\u770b\u5230ExpiringOAuth2RefreshToken\u662fOAuth2RefreshToken\u7684\u5b50\u7c7b\uff0c\u6240\u4ee5\u6211\u4eec\u53ef\u4ee5\u5c06\u751f\u6210\u7684refresh_token\u5b9e\u4f8b\u5bf9\u8c61\u66f4\u6539\u4e3aExpiringOAuth2RefreshToken\u5bf9\u8c61\uff0c\u4ee3\u7801\u5982\u4e0b\uff1a<\/p>\n<pre><code class=\"language-java\">\/**\n * \u91cd\u65b0\u5b9a\u4e49\u4ee4\u724ctoken\n *\/\n@Override\npublic OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {\n    if (accessToken instanceof DefaultOAuth2AccessToken) {\n        DefaultOAuth2AccessToken token = (DefaultOAuth2AccessToken) accessToken;\n        token.setValue(getToken());\n        \/\/\u4f7f\u7528DefaultExpiringOAuth2RefreshToken\u7c7b\u751f\u6210refresh_token\uff0c\u81ea\u5e26\u8fc7\u671f\u65f6\u95f4\uff0c\u5426\u5219\u4e0d\u751f\u6548\uff0crefresh_token\u4e00\u76f4\u6709\u6548\n        DefaultExpiringOAuth2RefreshToken refreshToken = (DefaultExpiringOAuth2RefreshToken) token.getRefreshToken();\n        \/\/OAuth2RefreshToken refreshToken = token.getRefreshToken();\n        if (refreshToken instanceof DefaultExpiringOAuth2RefreshToken) {\n            token.setRefreshToken(new DefaultExpiringOAuth2RefreshToken(getToken(), refreshToken.getExpiration()));\n        }\n        Map&lt;String, Object&gt; additionalInformation = Maps.newHashMap();\n        additionalInformation.put(&quot;client_id&quot;, authentication.getOAuth2Request().getClientId());\n        \/\/\u6dfb\u52a0\u989d\u5916\u914d\u7f6e\u4fe1\u606f\n        token.setAdditionalInformation(additionalInformation);\n        return token;\n    }\n    return accessToken;\n}<\/code><\/pre>\n<p>\u7ecf\u6d4b\u8bd5\u4e0a\u9762\u7684\u65b9\u6848\u5b8c\u7f8e\u89e3\u51b3\u81ea\u5b9a\u4e49token\u751f\u6210refresh_token\u6c38\u4e0d\u8fc7\u671f\u95ee\u9898<\/p>\n<p>\u672c\u6587\u8f6c\u8f7d\u53c2\u8003 <a target=\"_blank\" rel=\"noopener\" href=\"https:\/\/blog.csdn.net\/yaomingyang\/column\/info\/41645\" title=\"\u539f\u6587\">\u539f\u6587<\/a> \u5e76\u52a0\u4ee5\u8c03\u8bd5<\/p>\n","protected":false},"excerpt":{"rendered":"<p>OAuth2AccessToken\u63a5\u53e3\u7684\u9ed8\u8ba4\u5b9e\u73b0\u662fDefaultOAuth2AccessToken\u7c7b\uff08\u81ea\u5e26\u8fc7\u671f [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[354],"tags":[353],"class_list":["post-1422","post","type-post","status-publish","format-standard","hentry","category-spring-security","tag-oauth2"],"_links":{"self":[{"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/posts\/1422","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/comments?post=1422"}],"version-history":[{"count":0,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/posts\/1422\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/media?parent=1422"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/categories?post=1422"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/tags?post=1422"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}