{"id":5833,"date":"2018-09-21T07:02:48","date_gmt":"2018-09-21T14:02:48","guid":{"rendered":"https:\/\/www.couchbase.com\/blog\/?p=5833"},"modified":"2023-06-15T09:02:49","modified_gmt":"2023-06-15T16:02:49","slug":"custom-token-store-spring-securtiy-oauth2","status":"publish","type":"post","link":"https:\/\/www.couchbase.com\/blog\/ko\/custom-token-store-spring-securtiy-oauth2\/","title":{"rendered":"Spring\uc6a9 \uc0ac\uc6a9\uc790 \uc815\uc758 \ud1a0\ud070 \uc800\uc7a5\uc18c\ub97c \ub9cc\ub4dc\ub294 \ubc29\ubc95 - \ubcf4\uc548-Oauth2 | OAuth \ud30c\ud2b8 2"},"content":{"rendered":"<p>\uc774\uc804 \ube14\ub85c\uadf8 \uac8c\uc2dc\ubb3c\uc5d0\uc11c\ub294 <a href=\"https:\/\/www.couchbase.com\/blog\/ko\/spring-security-oauth2_authentication\/\">\uac04\ub2e8\ud55c OAuth2 \uc778\uc99d \uad6c\uc131 \ubc29\ubc95<\/a>. \ud558\uc9c0\ub9cc \uc800\ud76c \uad6c\ud604\uc5d0\ub294 \uc778\uba54\ubaa8\ub9ac \ud1a0\ud070 \uc800\uc7a5\uc18c\ub97c \uc0ac\uc6a9\ud558\uace0 \uc788\ub2e4\ub294 \ud070 \uacb0\ud568\uc774 \uc788\uc2b5\ub2c8\ub2e4.<\/p>\n<p>\uc778\uba54\ubaa8\ub9ac \ud1a0\ud070 \uc800\uc7a5\uc18c\ub294 \ub178\ub4dc \uac04\uc5d0 \uc27d\uac8c \uacf5\uc720\ud560 \uc218 \uc5c6\uace0 \uc11c\ubc84\uac00 \ub2e4\uc2dc \uc2dc\uc791\ub420 \uacbd\uc6b0 \uc800\uc7a5\uc18c\uc758 \ubaa8\ub4e0 \uc561\uc138\uc2a4 \ud1a0\ud070\uc744 \uc783\uac8c \ub418\ubbc0\ub85c \uac1c\ubc1c \uc911\uc774\uac70\ub098 \uc560\ud50c\ub9ac\ucf00\uc774\uc158\uc5d0 \ub2e8\uc77c \uc11c\ubc84\uac00 \uc788\ub294 \uacbd\uc6b0\uc5d0\ub9cc \uc0ac\uc6a9\ud574\uc57c \ud569\ub2c8\ub2e4.<\/p>\n<p><strong>\uc2a4\ud504\ub9c1 \ubcf4\uc548 \uc778\uc99d 2<\/strong> \uc5d0\ub294 \uc774\ubbf8 JDBC \ubc0f JWT\uc5d0 \ub300\ud55c \uae30\ubcf8 \uc9c0\uc6d0 \uae30\ub2a5\uc774 \uc788\uc2b5\ub2c8\ub2e4. \uadf8\ub7ec\ub098 \ud1a0\ud070\uc744 \ub2e4\ub978 \uacf3\uc5d0 \uc800\uc7a5\ud574\uc57c \ud558\ub294 \uacbd\uc6b0\uc5d0\ub294 \uc790\uccb4 \uc2a4\ud504\ub9c1 \ubcf4\uc548 \ud1a0\ud070 \uc800\uc7a5\uc18c\ub97c \ub9cc\ub4e4\uc5b4\uc57c \ud569\ub2c8\ub2e4. \uc548\ud0c0\uae5d\uac8c\ub3c4 \uc774\ub7ec\ud55c \uc791\uc5c5\uc744 \uad6c\ud604\ud558\ub294 \uac83\uc740 \uc0ac\uc18c\ud55c \uc791\uc5c5\uc774 \uc544\ub2c8\uba70 \ub2e4\uc74c \ub808\uc2dc\ud53c\ub97c \ud1b5\ud574 \uba87 \uc2dc\uac04\uc758 \uc791\uc5c5\uc744 \uc808\uc57d\ud560 \uc218 \uc788\uae30\ub97c \ubc14\ub78d\ub2c8\ub2e4.<\/p>\n<p>\uc561\uc138\uc2a4 \ud1a0\ud070\uacfc \uc0c8\ub85c \uace0\uce68 \ud1a0\ud070\uc744 \uc800\uc7a5\ud558\ub294 \ub450 \uac1c\uc758 \uc5d4\ud2f0\ud2f0\uc640 \uac01\uac01\uc758 \ub9ac\ud3ec\uc9c0\ud1a0\ub9ac\ub97c \ub9cc\ub4dc\ub294 \uac83\ubd80\ud130 \uc2dc\uc791\ud558\uaca0\uc2b5\ub2c8\ub2e4:<\/p>\n<pre class=\"lang:java decode:true\">lombok.Data\ub97c \uac00\uc838\uc635\ub2c8\ub2e4;\r\norg.springframework.data.annotation.Id\ub97c \uac00\uc838\uc635\ub2c8\ub2e4;\r\norg.springframework.data.couchbase.core.mapping.Document\ub97c \uac00\uc838\uc635\ub2c8\ub2e4;\r\norg.springframework.security.oauth2.common.OAuth2AccessToken\uc744 \uac00\uc838\uc635\ub2c8\ub2e4;\r\norg.springframework.security.oauth2.provider.OAuth2Authentication\uc744 \uac00\uc838\uc635\ub2c8\ub2e4;\r\n\r\n\ub3c4\ud050\uba3c\ud2b8\r\n@Data\r\n\uacf5\uc6a9 \ud074\ub798\uc2a4 CouchbaseAccessToken {\r\n\r\n    @Id\r\n    private String id;\r\n    private String tokenId;\r\n    \ube44\uacf5\uac1c OAuth2AccessToken \ud1a0\ud070;\r\n    private String authenticationId;\r\n    private String \uc0ac\uc6a9\uc790 \uc774\ub984\r\n    private String clientId;\r\n    private String \uc778\uc99d;\r\n    private String refreshToken;\r\n\r\n\r\n    public OAuth2Authentication getAuthentication() {\r\n        \ubc18\ud658 SerializableObjectConverter.deserialize(authentication);\r\n    }\r\n\r\n    public void setAuthentication(OAuth2Authentication authentication) {\r\n        this.authentication = SerializableObjectConverter.serialize(authentication);\r\n    }\r\n\r\n}\r\n<\/pre>\n<p>&nbsp;<\/p>\n<pre class=\"lang:java decode:true\">@N1qlPrimaryIndexed\r\nViewIndexed(designDoc = \"couchbaseAccessToken\")\r\n\uacf5\uc6a9 \uc778\ud130\ud398\uc774\uc2a4 CouchbaseAccessTokenRepository extends CouchbasePagingAndSortingRepository {\r\n\r\n    List findByClientId(String clientId)\r\n\r\n    List findByClientIdAndUsername(String clientId, String username);\r\n\r\n    Optional findByTokenId(String tokenId);\r\n\r\n    Optional findByRefreshToken(String refreshToken);\r\n\r\n    Optional findByAuthenticationId(String authenticationId);\r\n\r\n}\r\n<\/pre>\n<p>&nbsp;<\/p>\n<pre class=\"lang:java decode:true\">lombok.Data\ub97c \uac00\uc838\uc635\ub2c8\ub2e4;\r\norg.springframework.data.annotation.Id\ub97c \uac00\uc838\uc635\ub2c8\ub2e4;\r\norg.springframework.data.couchbase.core.mapping.Document\ub97c \uac00\uc838\uc635\ub2c8\ub2e4;\r\norg.springframework.security.oauth2.common.OAuth2RefreshToken\uc744 \uac00\uc838\uc635\ub2c8\ub2e4;\r\norg.springframework.security.oauth2.provider.OAuth2Authentication\uc744 \uac00\uc838\uc635\ub2c8\ub2e4;\r\n\r\n\ub3c4\ud050\uba3c\ud2b8\r\n@Data\r\n\uacf5\uc6a9 \ud074\ub798\uc2a4 CouchbaseRefreshToken {\r\n\r\n    @Id\r\n    private String id;\r\n    private String tokenId;\r\n    private OAuth2RefreshToken \ud1a0\ud070;\r\n    private String \uc778\uc99d;\r\n\r\n    public OAuth2Authentication getAuthentication() {\r\n        return SerializableObjectConverter.deserialize(authentication);\r\n    }\r\n\r\n    public void setAuthentication(OAuth2Authentication authentication) {\r\n        this.authentication = SerializableObjectConverter.serialize(authentication);\r\n    }\r\n}\r\n<\/pre>\n<p>&nbsp;<\/p>\n<pre class=\"lang:java decode:true\">org.springframework.data.couchbase.core.query.N1qlPrimaryIndexed\ub97c \uac00\uc838\uc635\ub2c8\ub2e4;\r\norg.springframework.data.couchbase.core.query.ViewIndexed\ub97c \uac00\uc838\uc635\ub2c8\ub2e4;\r\norg.springframework.data.couchbase.repository.CouchbasePagingAndSortingRepository\ub97c \uac00\uc838\uc635\ub2c8\ub2e4;\r\n\r\nimport java.util.List;\r\nimport java.util.Optional;\r\n\r\n@N1qlPrimaryIndexed\r\nViewIndexed(designDoc = \"couchbaseAccessToken\")\r\n\uacf5\uc6a9 \uc778\ud130\ud398\uc774\uc2a4 CouchbaseAccessTokenRepository extends CouchbasePagingAndSortingRepository {\r\n\r\n    List findByClientId(String clientId)\r\n\r\n    List findByClientIdAndUsername(String clientId, String username);\r\n\r\n    Optional findByTokenId(String tokenId);\r\n\r\n    Optional findByRefreshToken(String refreshToken);\r\n\r\n    Optional findByAuthenticationId(String authenticationId);\r\n\r\n}\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>\ucc38\uace0 <em><strong>OAuth2\uc778\uc99d<\/strong><\/em> \ub294 \uc778\ud130\ud398\uc774\uc2a4\uc774\ubbc0\ub85c \uac1d\uccb4\ub97c \uc9c1\ub82c\ud654\ud558\uc5ec \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0 \uc800\uc7a5\ud558\ub294 \uac83 \uc678\uc5d0 \ub2e4\ub978 \uc635\uc158\uc774 \uc5c6\uc2b5\ub2c8\ub2e4. \ub2e4\uc74c\uc740 \uc9c1\ub82c\ud654\/\uc5ed\uc9c1\ub82c\ud654\ub97c \ub2f4\ub2f9\ud558\ub294 \ud074\ub798\uc2a4\uc785\ub2c8\ub2e4:<\/p>\n<pre class=\"lang:java decode:true\">\uacf5\uc6a9 \ud074\ub798\uc2a4 SerializableObjectConverter {\r\n\r\n    public static String serialize(OAuth2Authentication \uac1d\uccb4) {\r\n        try {\r\n            byte[] bytes = SerializationUtils.serialize(object);\r\n            return Base64.encodeBase64String(bytes);\r\n        } catch(Exception e) {\r\n            e.printStackTrace();\r\n            throw e;\r\n        }\r\n    }\r\n\r\n    public static OAuth2Authentication \uc5ed\uc9c1\ub82c\ud654(String encodedObject) {\r\n        try {\r\n            byte[] bytes = Base64.decodeBase64(encodedObject);\r\n            return (OAuth2Authentication) SerializationUtils.deserialize(bytes);\r\n        } catch(Exception e) {\r\n            e.printStackTrace();\r\n            throw e;\r\n        }\r\n    }\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>\uc774\uc81c \ub4dc\ub514\uc5b4 \uc0ac\uc6a9\uc790 \uc815\uc758 <span style=\"font-weight: 400\">\uc2a4\ud504\ub9c1 \uc624\uc544\uc2dc\uc2a42<\/span> \ud1a0\ud070 \uc800\uc7a5\uc18c\uc785\ub2c8\ub2e4. \uc774\ub97c \uc704\ud574 \ud544\uc694\ud55c \uac83\uc740 \ud1a0\ud070 \uc800\uc7a5\uc18c\uc758 \uae34 \uba54\uc11c\ub4dc \ubaa9\ub85d\uc778 <em><strong>org.springframework.security.oauth2.provider.token.TokenStore<\/strong><\/em>:<\/p>\n<pre class=\"lang:java decode:true\">import org.springframework.security.oauth2.common.OAuth2AccessToken;\r\nimport org.springframework.security.oauth2.common.OAuth2RefreshToken;\r\nimport org.springframework.security.oauth2.provider.OAuth2Authentication;\r\nimport org.springframework.security.oauth2.provider.token.AuthenticationKeyGenerator;\r\nimport org.springframework.security.oauth2.provider.token.DefaultAuthenticationKeyGenerator;\r\nimport org.springframework.security.oauth2.provider.token.TokenStore;\r\n\r\nimport java.io.UnsupportedEncodingException;\r\nimport java.math.BigInteger;\r\nimport java.security.MessageDigest;\r\nimport java.security.NoSuchAlgorithmException;\r\nimport java.util.*;\r\n\r\npublic class CouchbaseTokenStore implements TokenStore {\r\n\r\n    private CouchbaseAccessTokenRepository cbAccessTokenRepository;\r\n\r\n    private CouchbaseRefreshTokenRepository cbRefreshTokenRepository;\r\n\r\n    public CouchbaseTokenStore(CouchbaseAccessTokenRepository cbAccessTokenRepository, CouchbaseRefreshTokenRepository cbRefreshTokenRepository){\r\n        this.cbAccessTokenRepository = cbAccessTokenRepository;\r\n        this.cbRefreshTokenRepository = cbRefreshTokenRepository;\r\n    }\r\n\r\n    private AuthenticationKeyGenerator authenticationKeyGenerator = new DefaultAuthenticationKeyGenerator();\r\n\r\n    @Override\r\n    public OAuth2Authentication readAuthentication(OAuth2AccessToken accessToken) {\r\n        return readAuthentication(accessToken.getValue());\r\n    }\r\n\r\n    @Override\r\n    public OAuth2Authentication readAuthentication(String token) {\r\n        Optional&lt;CouchbaseAccessToken&gt; accessToken = cbAccessTokenRepository.findByTokenId(extractTokenKey(token));\r\n        if (accessToken.isPresent()) {\r\n            return accessToken.get().getAuthentication();\r\n        }\r\n        return null;\r\n    }\r\n\r\n    @Override\r\n    public void storeAccessToken(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {\r\n        String refreshToken = null;\r\n        if (accessToken.getRefreshToken() != null) {\r\n            refreshToken = accessToken.getRefreshToken().getValue();\r\n        }\r\n\r\n        if (readAccessToken(accessToken.getValue()) != null) {\r\n            this.removeAccessToken(accessToken);\r\n        }\r\n\r\n        CouchbaseAccessToken cat =  new CouchbaseAccessToken();\r\n        cat.setId(UUID.randomUUID().toString()+UUID.randomUUID().toString());\r\n        cat.setTokenId(extractTokenKey(accessToken.getValue()));\r\n        cat.setToken(accessToken);\r\n        cat.setAuthenticationId(authenticationKeyGenerator.extractKey(authentication));\r\n        cat.setUsername(authentication.isClientOnly() ? null : authentication.getName());\r\n        cat.setClientId(authentication.getOAuth2Request().getClientId());\r\n        cat.setAuthentication(authentication);\r\n        cat.setRefreshToken(extractTokenKey(refreshToken));\r\n\r\n        cbAccessTokenRepository.save(cat);\r\n    }\r\n\r\n    @Override\r\n    public OAuth2AccessToken readAccessToken(String tokenValue) {\r\n        Optional&lt;CouchbaseAccessToken&gt; accessToken = cbAccessTokenRepository.findByTokenId(extractTokenKey(tokenValue));\r\n        if (accessToken.isPresent()) {\r\n            return accessToken.get().getToken();\r\n        }\r\n        return null;\r\n    }\r\n\r\n    @Override\r\n    public void removeAccessToken(OAuth2AccessToken oAuth2AccessToken) {\r\n        Optional&lt;CouchbaseAccessToken&gt; accessToken = cbAccessTokenRepository.findByTokenId(extractTokenKey(oAuth2AccessToken.getValue()));\r\n        if (accessToken.isPresent()) {\r\n            cbAccessTokenRepository.delete(accessToken.get());\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public void storeRefreshToken(OAuth2RefreshToken refreshToken, OAuth2Authentication authentication) {\r\n        CouchbaseRefreshToken crt = new CouchbaseRefreshToken();\r\n        crt.setId(UUID.randomUUID().toString()+UUID.randomUUID().toString());\r\n        crt.setTokenId(extractTokenKey(refreshToken.getValue()));\r\n        crt.setToken(refreshToken);\r\n        crt.setAuthentication(authentication);\r\n        cbRefreshTokenRepository.save(crt);\r\n    }\r\n\r\n    @Override\r\n    public OAuth2RefreshToken readRefreshToken(String tokenValue) {\r\n        Optional&lt;CouchbaseRefreshToken&gt; refreshToken = cbRefreshTokenRepository.findByTokenId(extractTokenKey(tokenValue));\r\n        return refreshToken.isPresent()? refreshToken.get().getToken() :null;\r\n    }\r\n\r\n    @Override\r\n    public OAuth2Authentication readAuthenticationForRefreshToken(OAuth2RefreshToken refreshToken) {\r\n        Optional&lt;CouchbaseRefreshToken&gt; rtk = cbRefreshTokenRepository.findByTokenId(extractTokenKey(refreshToken.getValue()));\r\n        return rtk.isPresent()? rtk.get().getAuthentication() :null;\r\n    }\r\n\r\n    @Override\r\n    public void removeRefreshToken(OAuth2RefreshToken refreshToken) {\r\n        Optional&lt;CouchbaseRefreshToken&gt; rtk = cbRefreshTokenRepository.findByTokenId(extractTokenKey(refreshToken.getValue()));\r\n        if (rtk.isPresent()) {\r\n            cbRefreshTokenRepository.delete(rtk.get());\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public void removeAccessTokenUsingRefreshToken(OAuth2RefreshToken refreshToken) {\r\n        Optional&lt;CouchbaseAccessToken&gt; token = cbAccessTokenRepository.findByRefreshToken(extractTokenKey(refreshToken.getValue()));\r\n        if(token.isPresent()){\r\n            cbAccessTokenRepository.delete(token.get());\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) {\r\n        OAuth2AccessToken accessToken = null;\r\n        String authenticationId = authenticationKeyGenerator.extractKey(authentication);\r\n        Optional&lt;CouchbaseAccessToken&gt; token = cbAccessTokenRepository.findByAuthenticationId(authenticationId);\r\n\r\n        if(token.isPresent()) {\r\n            accessToken = token.get().getToken();\r\n            if(accessToken != null &amp;&amp; !authenticationId.equals(this.authenticationKeyGenerator.extractKey(this.readAuthentication(accessToken)))) {\r\n                this.removeAccessToken(accessToken);\r\n                this.storeAccessToken(accessToken, authentication);\r\n            }\r\n        }\r\n        return accessToken;\r\n    }\r\n\r\n    @Override\r\n    public Collection&lt;OAuth2AccessToken&gt; findTokensByClientIdAndUserName(String clientId, String userName) {\r\n        Collection&lt;OAuth2AccessToken&gt; tokens = new ArrayList&lt;OAuth2AccessToken&gt;();\r\n        List&lt;CouchbaseAccessToken&gt; result = cbAccessTokenRepository.findByClientIdAndUsername(clientId, userName);\r\n        result.forEach(e-&gt; tokens.add(e.getToken()));\r\n        return tokens;\r\n    }\r\n\r\n    @Override\r\n    public Collection&lt;OAuth2AccessToken&gt; findTokensByClientId(String clientId) {\r\n        Collection&lt;OAuth2AccessToken&gt; tokens = new ArrayList&lt;OAuth2AccessToken&gt;();\r\n        List&lt;CouchbaseAccessToken&gt; result = cbAccessTokenRepository.findByClientId(clientId);\r\n        result.forEach(e-&gt; tokens.add(e.getToken()));\r\n        return tokens;\r\n    }\r\n\r\n    private String extractTokenKey(String value) {\r\n        if(value == null) {\r\n            return null;\r\n        } else {\r\n            MessageDigest digest;\r\n            try {\r\n                digest = MessageDigest.getInstance(\"MD5\");\r\n            } catch (NoSuchAlgorithmException var5) {\r\n                throw new IllegalStateException(\"MD5 algorithm not available.  Fatal (should be in the JDK).\");\r\n            }\r\n\r\n            try {\r\n                byte[] e = digest.digest(value.getBytes(\"UTF-8\"));\r\n                return String.format(\"%032x\", new Object[]{new BigInteger(1, e)});\r\n            } catch (UnsupportedEncodingException var4) {\r\n                throw new IllegalStateException(\"UTF-8 encoding not available.  Fatal (should be in the JDK).\");\r\n            }\r\n        }\r\n    }\r\n}\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>\ub9c8\uc9c0\ub9c9\uc73c\ub85c <em><strong>SecurityConfig<\/strong><\/em> \ud074\ub798\uc2a4\uc5d0\uc11c \uc774\uc804 \uae00\uc5d0\uc11c \uc0dd\uc131\ud55c \uc778\uc2a4\ud134\uc2a4\ub97c \ubc18\ud658\ud569\ub2c8\ub2e4. \uc774\uc81c \uc778\uc2a4\ud134\uc2a4\ub97c \ubc18\ud658\ud569\ub2c8\ub2e4. <em><strong>\uce74\uc6b0\uce58\ubca0\uc774\uc2a4\ud1a0\ud070\uc2a4\ud1a0\uc5b4<\/strong><\/em> \ub300\uc2e0 <em><strong>\uc778\uba54\ubaa8\ub9ac\ud1a0\ud070\uc2a4\ud1a0\uc5b4<\/strong><\/em>:<\/p>\n<pre class=\"lang:default decode:true\">    @Autowired\r\n    \ube44\uacf5\uac1c CouchbaseAccessTokenRepository couchbaseAccessTokenRepository;\r\n\r\n    \uc790\ub3d9\ud654\ub41c\r\n    \ube44\uacf5\uac1c CouchbaseRefresh\ud1a0\ud070\uc800\uc7a5\uc18c couchbaseRefresh\ud1a0\ud070\uc800\uc7a5\uc18c;\r\n\r\n\r\n    @Bean\r\n    public TokenStore tokenStore() {\r\n        return new CouchbaseTokenStore(couchbaseAccessTokenRepository, couchbaseRefreshTokenRepository);\r\n    }\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>\ub2e4\uc74c\uc740 \uc804\uccb4 \ubc84\uc804\uc758 <em><strong>SecurityConfig<\/strong><\/em> \ud074\ub798\uc2a4\uc785\ub2c8\ub2e4:<\/p>\n<pre class=\"lang:java decode:true\">@Configuration\r\n@EnableWebMvc\r\npublic \ud074\ub798\uc2a4 SecurityConfig extends WebSecurityConfigurerAdapter {\r\n\r\n    @Autowired\r\n    \ube44\uacf5\uac1c \uc0ac\uc6a9\uc790 \uc815\uc758 \uc138\ubd80 \uc11c\ube44\uc2a4 \uc0ac\uc6a9\uc790 \uc815\uc758 \uc138\ubd80 \uc11c\ube44\uc2a4;\r\n\r\n    @Autowired\r\n    \ube44\uacf5\uac1c CouchbaseAccess\ud1a0\ud070\uc800\uc7a5\uc18c couchbaseAccess\ud1a0\ud070\uc800\uc7a5\uc18c;\r\n\r\n    \uc790\ub3d9\ud654\ub41c\r\n    \ube44\uacf5\uac1c CouchbaseRefresh\ud1a0\ud070\uc800\uc7a5\uc18c couchbaseRefresh\ud1a0\ud070\uc800\uc7a5\uc18c;\r\n\r\n\r\n    @Autowired\r\n    public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {\r\n        auth.userDetailsService(customUserDetailsService)\r\n                .passwordEncoder(encoder());\r\n    }\r\n\r\n    @Override\r\n    public void configure( WebSecurity web ) throws Exception {\r\n        web.ignoring().antMatchers( HttpMethod.OPTIONS, \"\/**\" );\r\n    }\r\n\r\n    @Override\r\n    protected void configure(HttpSecurity http) throws Exception {\r\n        http\r\n                .csrf().disable()\r\n                .authorizeRequests()\r\n                .antMatchers(\"\/oauth\/token\").permitAll()\r\n                .antMatchers(\"\/api-docs\/**\").permitAll()\r\n                .anyRequest().authenticated()\r\n                .and().anonymous().disable();\r\n    }\r\n\r\n    @Bean\r\n    public TokenStore tokenStore() {\r\n        return new CouchbaseTokenStore(couchbaseAccessTokenRepository, couchbaseRefreshTokenRepository);\r\n    }\r\n\r\n    @Bean\r\n    public PasswordEncoder encoder(){{\r\n        NoOpPasswordEncoder.getInstance()\ub97c \ubc18\ud658\ud569\ub2c8\ub2e4;\r\n    }\r\n\r\n    @Bean\r\n    public FilterRegistrationBean corsFilter() {\r\n        UrlBasedCorsConfigurationSource source = \uc0c8\ub85c\uc6b4 UrlBasedCorsConfigurationSource();\r\n        CorsConfiguration config = \uc0c8\ub85c\uc6b4 CorsConfiguration();\r\n        config.setAllowCredentials(true);\r\n        config.addAllowedOrigin(\"*\");\r\n        config.addAllowedHeader(\"*\");\r\n        config.addAllowedMethod(\"*\");\r\n        source.registerCorsConfiguration(\"\/**\", config);\r\n        FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));\r\n        bean.setOrder(0);\r\n        bean\uc744 \ubc18\ud658\ud569\ub2c8\ub2e4;\r\n    }\r\n\r\n    @Bean\r\n    @Override\r\n    public AuthenticationManager authenticationManagerBean() throws Exception {\r\n        super.authenticationManagerBean() \ub97c \ubc18\ud658\ud569\ub2c8\ub2e4;\r\n    }\r\n}<\/pre>\n<p>&nbsp;<\/p>\n<p>\uc798\ud588\uc5b4\uc694! \uadf8\uac8c \uc6b0\ub9ac\uac00 \ud574\uc57c \ud560 \uc804\ubd80\uc785\ub2c8\ub2e4.<\/p>\n<p>\uc561\uc138\uc2a4 \ud1a0\ud070\uc740 \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0\uc11c \ub2e4\uc74c\uacfc \uac19\uc774 \ud45c\uc2dc\ub429\ub2c8\ub2e4:<\/p>\n<pre class=\"lang:plsql decode:true\">test\uc5d0\uc11c * SELECT\r\nwhere _class = 'com.bc.quicktask.standalone.model.CouchbaseAccessToken'<\/pre>\n<pre class=\"lang:js decode:true\">[\r\n  {\r\n    \"your_bucket_name\": {\r\n      \"_class\": \"com.bc.quicktask.standalone.model.CouchbaseAccessToken\",\r\n      \"\uc778\uc99d\": \"rO0ABXNyAEFvcmcuc3ByaW5nZnJhbWV3b3JrLnNlY3VyaXR5Lm9hdXRoMi5wcm92aWRlci5PQXV0aDJBdXRoZW50aWNhdGlvbr1ACwIWYlITAgACTAANc3RvcmVkUmVxdWVzdHQAPExvcmcvc3ByaW5nZnJhbWV3b3JrL3NlY3VyaXR5L29hdXRoMi9wcm92aWRlci9PQXV0aDJSZXF1ZXN0O0wAEnVzZXJBdXRoZW50aWNhdGlvbnQAMkxvcmcvc3ByaW5nZnJhbWV3b3JrL3NlY3VyaXR5L2NvcmUvQXV0aGVudGljYXRpb247eHIAR29yZy5zcHJpbmdmcmFtZXdvcmsuc2VjdXJpdHkuYXV0aGVudGljYXRpb24uQWJzdHJhY3RBdXRoZW50aWNhdGlvblRva2Vu06oofm5HZA4CAANaAA1hdXRoZW50aWNhdGVkTAALYXV0aG9yaXRpZXN0ABZMamF2YS91dGlsL0NvbGxlY3Rpb247TAAHZGV0YWlsc3QAEkxqYXZhL2xhbmcvT2JqZWN0O3hwAHNyACZqYXZhLnV0aWwuQ29sbGVjdGlvbnMkVW5tb2RpZmlhYmxlTGlzdPwPJTG17I4QAgABTAAEbGlzdHQAEExqYXZhL3V0aWwvTGlzdDt4cgAsamF2YS51dGlsLkNvbGxlY3Rpb25zJFVubW9kaWZpYWJsZUNvbGxlY3Rpb24ZQgCAy173HgIAAUwAAWNxAH4ABHhwc3IAE2phdmEudXRpbC5BcnJheUxpc3R4gdIdmcdhnQMAAUkABHNpemV4cAAAAAB3BAAAAAB4cQB+AAxwc3IAOm9yZy5zcHJpbmdmcmFtZXdvcmsuc2VjdXJpdHkub2F1dGgyLnByb3ZpZGVyLk9BdXRoMlJlcXVlc3QAAAAAAAAAAQIAB1oACGFwcHJvdmVkTAALYXV0aG9yaXRpZXNxAH4ABEwACmV4dGVuc2lvbnN0AA9MamF2YS91dGlsL01hcDtMAAtyZWRpcmVjdFV\r\n      \"authenticationId\": \"202d6940ebd428bbe2098530c8de3958\",\r\n      \"clientId\": \"myclient\",\r\n      \"refreshToken\": \"7613ffc6480ae83beb8f0988ef9ecfcf\",\r\n      \"\ud1a0\ud070\": {\r\n        \"_class\": \"org.springframework.security.oauth2.common.DefaultOAuth2AccessToken\",\r\n        \"\ucd94\uac00 \uc815\ubcf4\": {},\r\n        \"\ub9cc\ub8cc\": 1537420023432,\r\n        \"refreshToken\": {\r\n          \"_class\": \"org.springframework.security.oauth2.common.DefaultExpiringOAuth2RefreshToken\",\r\n          \"\ub9cc\ub8cc\": 1537420023421,\r\n          \"value\": \"c44db735-403e-49d6-8a22-cfe0e29f21d3\"\r\n        },\r\n        \"scope\": [\r\n          \"trust\",\r\n          \"read\",\r\n          \"write\"\r\n        ],\r\n        \"\ud1a0\ud070 \uc720\ud615\": \"\ubb34\uae30\uba85\",\r\n        \"value\": \"7759804c-e1c6-4d63-9520-8737f5b46dbf\"\r\n      },\r\n      \"tokenId\": \"12b6bd9de380e1dfab348f4c15abb805\",\r\n      \"username\": \"myuser\"\r\n    }\r\n  }\r\n]<\/pre>\n<p>&nbsp;<\/p>\n<p>\ub098\ub294 \uc0ac\uc6a9\ud588\uc2b5\ub2c8\ub2e4\u00a0<a href=\"https:\/\/github.com\/caelwinner\">caelwinner's<\/a> \ud504\ub85c\uc81d\ud2b8\uc5d0 \ub300\ud574 \ud2b9\ubcc4\ud788 \uac10\uc0ac\uc758 \ub9d0\uc500\uc744 \uc804\ud569\ub2c8\ub2e4.<\/p>\n<p>\uc9c8\ubb38\uc774 \uc788\uc73c\uc2dc\uba74 \uc5b8\uc81c\ub4e0\uc9c0 \ub2e4\uc74c \uc8fc\uc18c\ub85c \ud2b8\uc717\ud574 \uc8fc\uc138\uc694. <a href=\"https:\/\/twitter.com\/deniswsrosa\">@deniswsrosa<\/a><\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>","protected":false},"excerpt":{"rendered":"<p>In the previous blog post, we discussed how to configure a simple OAuth2 authentication. However, our implementation has a major flaw in it: we are using an in-memory token store. In-Memory token stores should be used only during development or [&hellip;]<\/p>","protected":false},"author":8754,"featured_media":5834,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[1815,1818,1813],"tags":[2283],"ppma_author":[9059],"class_list":["post-5833","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-best-practices-and-tutorials","category-java","category-security","tag-oauth2"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.8 (Yoast SEO v25.8) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Custom Token Store for Spring-Security-Oauth2 w\/ JDBC + JWT<\/title>\n<meta name=\"description\" content=\"Spring-security-oauth2 already has built-in support for JDBC and JWT, but you must create your own spring security token store. Find out how to do it here.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.couchbase.com\/blog\/ko\/custom-token-store-spring-securtiy-oauth2\/\" \/>\n<meta property=\"og:locale\" content=\"ko_KR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"How to Create a Custom Token Store for Spring-Security-Oauth2 | OAuth Part 2\" \/>\n<meta property=\"og:description\" content=\"Spring-security-oauth2 already has built-in support for JDBC and JWT, but you must create your own spring security token store. Find out how to do it here.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.couchbase.com\/blog\/ko\/custom-token-store-spring-securtiy-oauth2\/\" \/>\n<meta property=\"og:site_name\" content=\"The Couchbase Blog\" \/>\n<meta property=\"article:published_time\" content=\"2018-09-21T14:02:48+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2023-06-15T16:02:49+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2018\/09\/oauth-part2.png\" \/>\n\t<meta property=\"og:image:width\" content=\"728\" \/>\n\t<meta property=\"og:image:height\" content=\"210\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Denis Rosa, Developer Advocate, Couchbase\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@deniswsrosa\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Denis Rosa, Developer Advocate, Couchbase\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"5\ubd84\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/custom-token-store-spring-securtiy-oauth2\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/custom-token-store-spring-securtiy-oauth2\/\"},\"author\":{\"name\":\"Denis Rosa, Developer Advocate, Couchbase\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/fe3c5273e805e72a5294611a48f62257\"},\"headline\":\"How to Create a Custom Token Store for Spring-Security-Oauth2 | OAuth Part 2\",\"datePublished\":\"2018-09-21T14:02:48+00:00\",\"dateModified\":\"2023-06-15T16:02:49+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/custom-token-store-spring-securtiy-oauth2\/\"},\"wordCount\":316,\"commentCount\":1,\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/custom-token-store-spring-securtiy-oauth2\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/09\/oauth-part2.png\",\"keywords\":[\"oauth2\"],\"articleSection\":[\"Best Practices and Tutorials\",\"Java\",\"Security\"],\"inLanguage\":\"ko-KR\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/custom-token-store-spring-securtiy-oauth2\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/custom-token-store-spring-securtiy-oauth2\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/custom-token-store-spring-securtiy-oauth2\/\",\"name\":\"Custom Token Store for Spring-Security-Oauth2 w\/ JDBC + JWT\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/custom-token-store-spring-securtiy-oauth2\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/custom-token-store-spring-securtiy-oauth2\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/09\/oauth-part2.png\",\"datePublished\":\"2018-09-21T14:02:48+00:00\",\"dateModified\":\"2023-06-15T16:02:49+00:00\",\"description\":\"Spring-security-oauth2 already has built-in support for JDBC and JWT, but you must create your own spring security token store. Find out how to do it here.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/custom-token-store-spring-securtiy-oauth2\/#breadcrumb\"},\"inLanguage\":\"ko-KR\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/custom-token-store-spring-securtiy-oauth2\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"ko-KR\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/custom-token-store-spring-securtiy-oauth2\/#primaryimage\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/09\/oauth-part2.png\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/09\/oauth-part2.png\",\"width\":728,\"height\":210},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/custom-token-store-spring-securtiy-oauth2\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.couchbase.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"How to Create a Custom Token Store for Spring-Security-Oauth2 | OAuth Part 2\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#website\",\"url\":\"https:\/\/www.couchbase.com\/blog\/\",\"name\":\"The Couchbase Blog\",\"description\":\"Couchbase, the NoSQL Database\",\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.couchbase.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"ko-KR\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\",\"name\":\"The Couchbase Blog\",\"url\":\"https:\/\/www.couchbase.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"ko-KR\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png\",\"width\":218,\"height\":34,\"caption\":\"The Couchbase Blog\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/fe3c5273e805e72a5294611a48f62257\",\"name\":\"Denis Rosa, Developer Advocate, Couchbase\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"ko-KR\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/be0716f6199cfb09417c92cf7a8fa8d6\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/f8d1f5c13115122cab89d0f229b904480bfe20d3dfbb093fe9734cda5235d419?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/f8d1f5c13115122cab89d0f229b904480bfe20d3dfbb093fe9734cda5235d419?s=96&d=mm&r=g\",\"caption\":\"Denis Rosa, Developer Advocate, Couchbase\"},\"description\":\"Denis Rosa is a Developer Advocate for Couchbase and lives in Munich - Germany. He has a solid experience as a software engineer and speaks fluently Java, Python, Scala and Javascript. Denis likes to write about search, Big Data, AI, Microservices and everything else that would help developers to make a beautiful, faster, stable and scalable app.\",\"sameAs\":[\"https:\/\/x.com\/deniswsrosa\"],\"url\":\"https:\/\/www.couchbase.com\/blog\/ko\/author\/denis-rosa\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Custom Token Store for Spring-Security-Oauth2 w\/ JDBC + JWT","description":"Spring-security-oauth2\ub294 \uc774\ubbf8 JDBC \ubc0f JWT\ub97c \uae30\ubcf8\uc801\uc73c\ub85c \uc9c0\uc6d0\ud558\uc9c0\ub9cc \uc790\uccb4 \uc2a4\ud504\ub9c1 \ubcf4\uc548 \ud1a0\ud070 \uc800\uc7a5\uc18c\ub97c \ub9cc\ub4e4\uc5b4\uc57c \ud569\ub2c8\ub2e4. \uc5ec\uae30\uc5d0\uc11c \ubc29\ubc95\uc744 \uc54c\uc544\ubcf4\uc138\uc694.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.couchbase.com\/blog\/ko\/custom-token-store-spring-securtiy-oauth2\/","og_locale":"ko_KR","og_type":"article","og_title":"How to Create a Custom Token Store for Spring-Security-Oauth2 | OAuth Part 2","og_description":"Spring-security-oauth2 already has built-in support for JDBC and JWT, but you must create your own spring security token store. Find out how to do it here.","og_url":"https:\/\/www.couchbase.com\/blog\/ko\/custom-token-store-spring-securtiy-oauth2\/","og_site_name":"The Couchbase Blog","article_published_time":"2018-09-21T14:02:48+00:00","article_modified_time":"2023-06-15T16:02:49+00:00","og_image":[{"width":728,"height":210,"url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2018\/09\/oauth-part2.png","type":"image\/png"}],"author":"Denis Rosa, Developer Advocate, Couchbase","twitter_card":"summary_large_image","twitter_creator":"@deniswsrosa","twitter_misc":{"Written by":"Denis Rosa, Developer Advocate, Couchbase","Est. reading time":"5\ubd84"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.couchbase.com\/blog\/custom-token-store-spring-securtiy-oauth2\/#article","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/custom-token-store-spring-securtiy-oauth2\/"},"author":{"name":"Denis Rosa, Developer Advocate, Couchbase","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/fe3c5273e805e72a5294611a48f62257"},"headline":"How to Create a Custom Token Store for Spring-Security-Oauth2 | OAuth Part 2","datePublished":"2018-09-21T14:02:48+00:00","dateModified":"2023-06-15T16:02:49+00:00","mainEntityOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/custom-token-store-spring-securtiy-oauth2\/"},"wordCount":316,"commentCount":1,"publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/custom-token-store-spring-securtiy-oauth2\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/09\/oauth-part2.png","keywords":["oauth2"],"articleSection":["Best Practices and Tutorials","Java","Security"],"inLanguage":"ko-KR","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.couchbase.com\/blog\/custom-token-store-spring-securtiy-oauth2\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.couchbase.com\/blog\/custom-token-store-spring-securtiy-oauth2\/","url":"https:\/\/www.couchbase.com\/blog\/custom-token-store-spring-securtiy-oauth2\/","name":"Custom Token Store for Spring-Security-Oauth2 w\/ JDBC + JWT","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/custom-token-store-spring-securtiy-oauth2\/#primaryimage"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/custom-token-store-spring-securtiy-oauth2\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/09\/oauth-part2.png","datePublished":"2018-09-21T14:02:48+00:00","dateModified":"2023-06-15T16:02:49+00:00","description":"Spring-security-oauth2\ub294 \uc774\ubbf8 JDBC \ubc0f JWT\ub97c \uae30\ubcf8\uc801\uc73c\ub85c \uc9c0\uc6d0\ud558\uc9c0\ub9cc \uc790\uccb4 \uc2a4\ud504\ub9c1 \ubcf4\uc548 \ud1a0\ud070 \uc800\uc7a5\uc18c\ub97c \ub9cc\ub4e4\uc5b4\uc57c \ud569\ub2c8\ub2e4. \uc5ec\uae30\uc5d0\uc11c \ubc29\ubc95\uc744 \uc54c\uc544\ubcf4\uc138\uc694.","breadcrumb":{"@id":"https:\/\/www.couchbase.com\/blog\/custom-token-store-spring-securtiy-oauth2\/#breadcrumb"},"inLanguage":"ko-KR","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.couchbase.com\/blog\/custom-token-store-spring-securtiy-oauth2\/"]}]},{"@type":"ImageObject","inLanguage":"ko-KR","@id":"https:\/\/www.couchbase.com\/blog\/custom-token-store-spring-securtiy-oauth2\/#primaryimage","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/09\/oauth-part2.png","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/09\/oauth-part2.png","width":728,"height":210},{"@type":"BreadcrumbList","@id":"https:\/\/www.couchbase.com\/blog\/custom-token-store-spring-securtiy-oauth2\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.couchbase.com\/blog\/"},{"@type":"ListItem","position":2,"name":"How to Create a Custom Token Store for Spring-Security-Oauth2 | OAuth Part 2"}]},{"@type":"WebSite","@id":"https:\/\/www.couchbase.com\/blog\/#website","url":"https:\/\/www.couchbase.com\/blog\/","name":"\uce74\uc6b0\uce58\ubca0\uc774\uc2a4 \ube14\ub85c\uadf8","description":"NoSQL \ub370\uc774\ud130\ubca0\uc774\uc2a4, Couchbase","publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.couchbase.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"ko-KR"},{"@type":"Organization","@id":"https:\/\/www.couchbase.com\/blog\/#organization","name":"\uce74\uc6b0\uce58\ubca0\uc774\uc2a4 \ube14\ub85c\uadf8","url":"https:\/\/www.couchbase.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"ko-KR","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png","width":218,"height":34,"caption":"The Couchbase Blog"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/fe3c5273e805e72a5294611a48f62257","name":"\ub370\ub2c8\uc2a4 \ub85c\uc0ac, \uac1c\ubc1c\uc790 \uc639\ud638\uc790, \uce74\uc6b0\uce58\ubca0\uc774\uc2a4","image":{"@type":"ImageObject","inLanguage":"ko-KR","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/be0716f6199cfb09417c92cf7a8fa8d6","url":"https:\/\/secure.gravatar.com\/avatar\/f8d1f5c13115122cab89d0f229b904480bfe20d3dfbb093fe9734cda5235d419?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/f8d1f5c13115122cab89d0f229b904480bfe20d3dfbb093fe9734cda5235d419?s=96&d=mm&r=g","caption":"Denis Rosa, Developer Advocate, Couchbase"},"description":"\ub370\ub2c8\uc2a4 \ub85c\uc0ac\ub294 \ub3c5\uc77c \ubb8c\ud5e8\uc5d0 \uac70\uc8fc\ud558\uace0 \uc788\ub294 \uce74\uc6b0\uce58\ubca0\uc774\uc2a4\uc758 \uac1c\ubc1c\uc790 \uc639\ud638\uc790\uc785\ub2c8\ub2e4. \uadf8\ub294 \uc18c\ud504\ud2b8\uc6e8\uc5b4 \uc5d4\uc9c0\ub2c8\uc5b4\ub85c\uc11c \ud0c4\ud0c4\ud55c \uacbd\ub825\uc744 \uc313\uc558\uc73c\uba70 Java, Python, Scala, Javascript\ub97c \uc720\ucc3d\ud558\uac8c \uad6c\uc0ac\ud569\ub2c8\ub2e4. Denis\ub294 \uac80\uc0c9, \ube45 \ub370\uc774\ud130, AI, \ub9c8\uc774\ud06c\ub85c\uc11c\ube44\uc2a4 \ubc0f \uac1c\ubc1c\uc790\uac00 \uc544\ub984\ub2f5\uace0 \ube60\ub974\uace0 \uc548\uc815\uc801\uc774\uba70 \ud655\uc7a5 \uac00\ub2a5\ud55c \uc571\uc744 \ub9cc\ub4dc\ub294 \ub370 \ub3c4\uc6c0\uc774 \ub418\ub294 \ubaa8\ub4e0 \uac83\uc5d0 \ub300\ud574 \uae00\uc744 \uc4f0\ub294 \uac83\uc744 \uc88b\uc544\ud569\ub2c8\ub2e4.","sameAs":["https:\/\/x.com\/deniswsrosa"],"url":"https:\/\/www.couchbase.com\/blog\/ko\/author\/denis-rosa\/"}]}},"authors":[{"term_id":9059,"user_id":8754,"is_guest":0,"slug":"denis-rosa","display_name":"Denis Rosa, Developer Advocate, Couchbase","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/f8d1f5c13115122cab89d0f229b904480bfe20d3dfbb093fe9734cda5235d419?s=96&d=mm&r=g","author_category":"","last_name":"Rosa, Developer Advocate, Couchbase","first_name":"Denis","job_title":"","user_url":"","description":"\ub370\ub2c8\uc2a4 \ub85c\uc0ac\ub294 \ub3c5\uc77c \ubb8c\ud5e8\uc5d0 \uac70\uc8fc\ud558\uace0 \uc788\ub294 \uce74\uc6b0\uce58\ubca0\uc774\uc2a4\uc758 \uac1c\ubc1c\uc790 \uc639\ud638\uc790\uc785\ub2c8\ub2e4. \uadf8\ub294 \uc18c\ud504\ud2b8\uc6e8\uc5b4 \uc5d4\uc9c0\ub2c8\uc5b4\ub85c\uc11c \ud0c4\ud0c4\ud55c \uacbd\ub825\uc744 \uc313\uc558\uc73c\uba70 Java, Python, Scala, Javascript\ub97c \uc720\ucc3d\ud558\uac8c \uad6c\uc0ac\ud569\ub2c8\ub2e4. Denis\ub294 \uac80\uc0c9, \ube45 \ub370\uc774\ud130, AI, \ub9c8\uc774\ud06c\ub85c\uc11c\ube44\uc2a4 \ubc0f \uac1c\ubc1c\uc790\uac00 \uc544\ub984\ub2f5\uace0 \ube60\ub974\uace0 \uc548\uc815\uc801\uc774\uba70 \ud655\uc7a5 \uac00\ub2a5\ud55c \uc571\uc744 \ub9cc\ub4dc\ub294 \ub370 \ub3c4\uc6c0\uc774 \ub418\ub294 \ubaa8\ub4e0 \uac83\uc5d0 \ub300\ud574 \uae00\uc744 \uc4f0\ub294 \uac83\uc744 \uc88b\uc544\ud569\ub2c8\ub2e4."}],"_links":{"self":[{"href":"https:\/\/www.couchbase.com\/blog\/ko\/wp-json\/wp\/v2\/posts\/5833","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.couchbase.com\/blog\/ko\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.couchbase.com\/blog\/ko\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/ko\/wp-json\/wp\/v2\/users\/8754"}],"replies":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/ko\/wp-json\/wp\/v2\/comments?post=5833"}],"version-history":[{"count":0,"href":"https:\/\/www.couchbase.com\/blog\/ko\/wp-json\/wp\/v2\/posts\/5833\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/ko\/wp-json\/wp\/v2\/media\/5834"}],"wp:attachment":[{"href":"https:\/\/www.couchbase.com\/blog\/ko\/wp-json\/wp\/v2\/media?parent=5833"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/ko\/wp-json\/wp\/v2\/categories?post=5833"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/ko\/wp-json\/wp\/v2\/tags?post=5833"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/ko\/wp-json\/wp\/v2\/ppma_author?post=5833"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}