{"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\/es\/custom-token-store-spring-securtiy-oauth2\/","title":{"rendered":"C\u00f3mo crear un almac\u00e9n de tokens personalizado para Spring-Security-Oauth2 | OAuth Parte 2"},"content":{"rendered":"<p>En la anterior entrada del blog, hablamos de <a href=\"https:\/\/www.couchbase.com\/blog\/es\/spring-security-oauth2_authentication\/\">c\u00f3mo configurar una autenticaci\u00f3n OAuth2 sencilla<\/a>. Sin embargo, nuestra implementaci\u00f3n tiene un fallo importante: estamos utilizando un almac\u00e9n de tokens en memoria.<\/p>\n<p>Los almacenes de tokens en memoria s\u00f3lo deben utilizarse durante el desarrollo o si tu aplicaci\u00f3n tiene un \u00fanico servidor, ya que no puedes compartirlos f\u00e1cilmente entre nodos y, en caso de reinicio del servidor, perder\u00e1s todos los tokens de acceso que haya en \u00e9l.<\/p>\n<p><strong>Spring-security-oauth2<\/strong> ya tiene soporte integrado para JDBC y JWT. Sin embargo, si necesitas guardar tus tokens en alg\u00fan otro lugar, tienes que crear tu propio almac\u00e9n de tokens de seguridad de Spring. Por desgracia, la implementaci\u00f3n de tal cosa no es una tarea trivial, y espero que la siguiente receta le ahorrar\u00e1 un par de horas de trabajo.<\/p>\n<p>Empecemos por crear las dos entidades responsables de almacenar tu token de acceso y refresco, y sus respectivos repositorios:<\/p>\n<pre class=\"lang:java decode:true\">import lombok.Data;\r\nimport org.springframework.data.annotation.Id;\r\nimport org.springframework.data.couchbase.core.mapping.Document;\r\nimport org.springframework.security.oauth2.common.OAuth2AccessToken;\r\nimport org.springframework.security.oauth2.provider.OAuth2Authentication;\r\n\r\n@Document\r\n@Data\r\npublic class CouchbaseAccessToken {\r\n\r\n    @Id\r\n    private String id;\r\n    private String tokenId;\r\n    private OAuth2AccessToken token;\r\n    private String authenticationId;\r\n    private String username;\r\n    private String clientId;\r\n    private String authentication;\r\n    private String refreshToken;\r\n\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}\r\n<\/pre>\n<p>&nbsp;<\/p>\n<pre class=\"lang:java decode:true\">@N1qlPrimaryIndexed\r\n@ViewIndexed(designDoc = \"couchbaseAccessToken\")\r\npublic interface CouchbaseAccessTokenRepository extends CouchbasePagingAndSortingRepository&lt;CouchbaseAccessToken, String&gt; {\r\n\r\n    List&lt;CouchbaseAccessToken&gt; findByClientId(String clientId);\r\n\r\n    List&lt;CouchbaseAccessToken&gt; findByClientIdAndUsername(String clientId, String username);\r\n\r\n    Optional&lt;CouchbaseAccessToken&gt; findByTokenId(String tokenId);\r\n\r\n    Optional&lt;CouchbaseAccessToken&gt; findByRefreshToken(String refreshToken);\r\n\r\n    Optional&lt;CouchbaseAccessToken&gt; findByAuthenticationId(String authenticationId);\r\n\r\n}\r\n<\/pre>\n<p>&nbsp;<\/p>\n<pre class=\"lang:java decode:true\">import lombok.Data;\r\nimport org.springframework.data.annotation.Id;\r\nimport org.springframework.data.couchbase.core.mapping.Document;\r\nimport org.springframework.security.oauth2.common.OAuth2RefreshToken;\r\nimport org.springframework.security.oauth2.provider.OAuth2Authentication;\r\n\r\n@Document\r\n@Data\r\npublic class CouchbaseRefreshToken {\r\n\r\n    @Id\r\n    private String id;\r\n    private String tokenId;\r\n    private OAuth2RefreshToken token;\r\n    private String authentication;\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\">import org.springframework.data.couchbase.core.query.N1qlPrimaryIndexed;\r\nimport org.springframework.data.couchbase.core.query.ViewIndexed;\r\nimport org.springframework.data.couchbase.repository.CouchbasePagingAndSortingRepository;\r\n\r\nimport java.util.List;\r\nimport java.util.Optional;\r\n\r\n@N1qlPrimaryIndexed\r\n@ViewIndexed(designDoc = \"couchbaseAccessToken\")\r\npublic interface CouchbaseAccessTokenRepository extends CouchbasePagingAndSortingRepository&lt;CouchbaseAccessToken, String&gt; {\r\n\r\n    List&lt;CouchbaseAccessToken&gt; findByClientId(String clientId);\r\n\r\n    List&lt;CouchbaseAccessToken&gt; findByClientIdAndUsername(String clientId, String username);\r\n\r\n    Optional&lt;CouchbaseAccessToken&gt; findByTokenId(String tokenId);\r\n\r\n    Optional&lt;CouchbaseAccessToken&gt; findByRefreshToken(String refreshToken);\r\n\r\n    Optional&lt;CouchbaseAccessToken&gt; findByAuthenticationId(String authenticationId);\r\n\r\n}\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>Tenga en cuenta que <em><strong>Autenticaci\u00f3n OAuth2<\/strong><\/em> es una interfaz, por lo que no tengo otra opci\u00f3n que serializar el objeto para almacenarlo en la base de datos. Aqu\u00ed est\u00e1 la clase responsable de serializarlo\/deserializarlo:<\/p>\n<pre class=\"lang:java decode:true\">public class SerializableObjectConverter {\r\n\r\n    public static String serialize(OAuth2Authentication object) {\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 deserialize(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>Ahora, por fin podemos crear nuestro <span style=\"font-weight: 400\">spring oauth2<\/span> almac\u00e9n de fichas. Para ello, todo lo que necesitamos es implementar la larga lista de m\u00e9todos del m\u00e9todo <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>Por \u00faltimo, podemos modificar ligeramente nuestro <em><strong>SecurityConfig<\/strong><\/em> que hemos creado en el art\u00edculo anterior. Ahora devolver\u00e1 una instancia de <em><strong>CouchbaseTokenStore<\/strong><\/em> en lugar de <em><strong>InMemoryTokenStore<\/strong><\/em>:<\/p>\n<pre class=\"lang:default decode:true\">    @Autowired\r\n    private CouchbaseAccessTokenRepository couchbaseAccessTokenRepository;\r\n\r\n    @Autowired\r\n    private CouchbaseRefreshTokenRepository couchbaseRefreshTokenRepository;\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>Aqu\u00ed est\u00e1 la versi\u00f3n completa del <em><strong>SecurityConfig<\/strong><\/em> clase:<\/p>\n<pre class=\"lang:java decode:true\">@Configuration\r\n@EnableWebMvc\r\npublic class SecurityConfig extends WebSecurityConfigurerAdapter {\r\n\r\n    @Autowired\r\n    private CustomUserDetailsService customUserDetailsService;\r\n\r\n    @Autowired\r\n    private CouchbaseAccessTokenRepository couchbaseAccessTokenRepository;\r\n\r\n    @Autowired\r\n    private CouchbaseRefreshTokenRepository couchbaseRefreshTokenRepository;\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        return  NoOpPasswordEncoder.getInstance();\r\n    }\r\n\r\n    @Bean\r\n    public FilterRegistrationBean corsFilter() {\r\n        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();\r\n        CorsConfiguration config = new 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        return bean;\r\n    }\r\n\r\n    @Bean\r\n    @Override\r\n    public AuthenticationManager authenticationManagerBean() throws Exception {\r\n        return super.authenticationManagerBean();\r\n    }\r\n}<\/pre>\n<p>&nbsp;<\/p>\n<p>Bien hecho. Eso es todo lo que ten\u00edamos que hacer.<\/p>\n<p>Su token de acceso tendr\u00e1 el siguiente aspecto en su base de datos:<\/p>\n<pre class=\"lang:plsql decode:true\">SELECT * from test\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      \"authentication\": \"rO0ABXNyAEFvcmcuc3ByaW5nZnJhbWV3b3JrLnNlY3VyaXR5Lm9hdXRoMi5wcm92aWRlci5PQXV0aDJBdXRoZW50aWNhdGlvbr1ACwIWYlITAgACTAANc3RvcmVkUmVxdWVzdHQAPExvcmcvc3ByaW5nZnJhbWV3b3JrL3NlY3VyaXR5L29hdXRoMi9wcm92aWRlci9PQXV0aDJSZXF1ZXN0O0wAEnVzZXJBdXRoZW50aWNhdGlvbnQAMkxvcmcvc3ByaW5nZnJhbWV3b3JrL3NlY3VyaXR5L2NvcmUvQXV0aGVudGljYXRpb247eHIAR29yZy5zcHJpbmdmcmFtZXdvcmsuc2VjdXJpdHkuYXV0aGVudGljYXRpb24uQWJzdHJhY3RBdXRoZW50aWNhdGlvblRva2Vu06oofm5HZA4CAANaAA1hdXRoZW50aWNhdGVkTAALYXV0aG9yaXRpZXN0ABZMamF2YS91dGlsL0NvbGxlY3Rpb247TAAHZGV0YWlsc3QAEkxqYXZhL2xhbmcvT2JqZWN0O3hwAHNyACZqYXZhLnV0aWwuQ29sbGVjdGlvbnMkVW5tb2RpZmlhYmxlTGlzdPwPJTG17I4QAgABTAAEbGlzdHQAEExqYXZhL3V0aWwvTGlzdDt4cgAsamF2YS51dGlsLkNvbGxlY3Rpb25zJFVubW9kaWZpYWJsZUNvbGxlY3Rpb24ZQgCAy173HgIAAUwAAWNxAH4ABHhwc3IAE2phdmEudXRpbC5BcnJheUxpc3R4gdIdmcdhnQMAAUkABHNpemV4cAAAAAB3BAAAAAB4cQB+AAxwc3IAOm9yZy5zcHJpbmdmcmFtZXdvcmsuc2VjdXJpdHkub2F1dGgyLnByb3ZpZGVyLk9BdXRoMlJlcXVlc3QAAAAAAAAAAQIAB1oACGFwcHJvdmVkTAALYXV0aG9yaXRpZXNxAH4ABEwACmV4dGVuc2lvbnN0AA9MamF2YS91dGlsL01hcDtMAAtyZWRpcmVjdFVyaXQAEkxqYXZhL2xhbmcvU3RyaW5nO0wAB3JlZnJlc2h0ADtMb3JnL3NwcmluZ2ZyYW1ld29yay9zZWN1cml0eS9vYXV0aDIvcHJvdmlkZXIvVG9rZW5SZXF1ZXN0O0wAC3Jlc291cmNlSWRzdAAPTGphdmEvdXRpbC9TZXQ7TAANcmVzcG9uc2VUeXBlc3EAfgAReHIAOG9yZy5zcHJpbmdmcmFtZXdvcmsuc2VjdXJpdHkub2F1dGgyLnByb3ZpZGVyLkJhc2VSZXF1ZXN0Nih6PqNxab0CAANMAAhjbGllbnRJZHEAfgAPTAARcmVxdWVzdFBhcmFtZXRlcnNxAH4ADkwABXNjb3BlcQB+ABF4cHQACG15Y2xpZW50c3IAJWphdmEudXRpbC5Db2xsZWN0aW9ucyRVbm1vZGlmaWFibGVNYXDxpaj+dPUHQgIAAUwAAW1xAH4ADnhwc3IAEWphdmEudXRpbC5IYXNoTWFwBQfawcMWYNEDAAJGAApsb2FkRmFjdG9ySQAJdGhyZXNob2xkeHA\/QAAAAAAABncIAAAACAAAAAN0AApncmFudF90eXBldAAIcGFzc3dvcmR0AAljbGllbnRfaWR0AAhteWNsaWVudHQACHVzZXJuYW1ldAAGbXl1c2VyeHNyACVqYXZhLnV0aWwuQ29sbGVjdGlvbnMkVW5tb2RpZmlhYmxlU2V0gB2S0Y+bgFUCAAB4cQB+AAlzcgAXamF2YS51dGlsLkxpbmtlZEhhc2hTZXTYbNdald0qHgIAAHhyABFqYXZhLnV0aWwuSGFzaFNldLpEhZWWuLc0AwAAeHB3DAAAABA\/QAAAAAAAA3QABXRydXN0dAAEcmVhZHQABXdyaXRleAFzcQB+ACJ3DAAAABA\/QAAAAAAAAHhzcQB+ABc\/QAAAAAAAAHcIAAAAEAAAAAB4cHBzcQB+ACJ3DAAAABA\/QAAAAAAAAHhzcQB+ACJ3DAAAABA\/QAAAAAAAAHhzcgBPb3JnLnNwcmluZ2ZyYW1ld29yay5zZWN1cml0eS5hdXRoZW50aWNhdGlvbi5Vc2VybmFtZVBhc3N3b3JkQXV0aGVudGljYXRpb25Ub2tlbgAAAAAAAAH0AgACTAALY3JlZGVudGlhbHNxAH4ABUwACXByaW5jaXBhbHEAfgAFeHEAfgADAXNyAB9qYXZhLnV0aWwuQ29sbGVjdGlvbnMkRW1wdHlMaXN0ergXtDynnt4CAAB4cHNyABdqYXZhLnV0aWwuTGlua2VkSGFzaE1hcDTATlwQbMD7AgABWgALYWNjZXNzT3JkZXJ4cQB+ABc\/QAAAAAAABncIAAAACAAAAAR0AA1jbGllbnRfc2VjcmV0dAAIbXlzZWNyZXRxAH4AGXEAfgAacQB+ABtxAH4AHHEAfgAdcQB+AB54AHBzcgAyY29tLmJjLnF1aWNrdGFzay5zdGFuZGFsb25lLm1vZGVsLkN1c3RvbVVzZXJEZXRhaWz9dbY7wdosOwIAAkwABmdyb3Vwc3EAfgAITAAEdXNlcnQAKExjb20vYmMvcXVpY2t0YXNrL3N0YW5kYWxvbmUvbW9kZWwvVXNlcjt4cHNxAH4ACwAAAAB3BAAAAAB4c3IAJmNvbS5iYy5xdWlja3Rhc2suc3RhbmRhbG9uZS5tb2RlbC5Vc2VyWvIkR494dqQCAAdMAAljb21wYW55SWRxAH4AD0wADWV4dGVybmFsTG9naW5xAH4AD0wAAmlkcQB+AA9MAAlpc0VuYWJsZWR0ABNMamF2YS9sYW5nL0Jvb2xlYW47TAAJaXNWaXNpYmxlcQB+ADhMAAhwYXNzd29yZHEAfgAPTAAIdXNlcm5hbWVxAH4AD3hwdAAKY29tcGFueS0tMXQABm15dXNlcnQACXVzZXJJZC0tMXNyABFqYXZhLmxhbmcuQm9vbGVhbs0gcoDVnPruAgABWgAFdmFsdWV4cAFxAH4APnQACHBhc3N3b3JkdAAGbXl1c2Vy\",\r\n      \"authenticationId\": \"202d6940ebd428bbe2098530c8de3958\",\r\n      \"clientId\": \"myclient\",\r\n      \"refreshToken\": \"7613ffc6480ae83beb8f0988ef9ecfcf\",\r\n      \"token\": {\r\n        \"_class\": \"org.springframework.security.oauth2.common.DefaultOAuth2AccessToken\",\r\n        \"additionalInformation\": {},\r\n        \"expiration\": 1537420023432,\r\n        \"refreshToken\": {\r\n          \"_class\": \"org.springframework.security.oauth2.common.DefaultExpiringOAuth2RefreshToken\",\r\n          \"expiration\": 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        \"tokenType\": \"bearer\",\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>He utilizado\u00a0<a href=\"https:\/\/github.com\/caelwinner\">de caelwinner<\/a> proyecto como referencia, he aqu\u00ed mi especial agradecimiento a \u00e9l.<\/p>\n<p>Si tiene alguna pregunta, no dude en tuitearme en <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 v26.2 (Yoast SEO v26.2) - 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\/es\/custom-token-store-spring-securtiy-oauth2\/\" \/>\n<meta property=\"og:locale\" content=\"es_MX\" \/>\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\/es\/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 minutos\" \/>\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\":\"es\",\"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\":\"es\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/custom-token-store-spring-securtiy-oauth2\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@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\":\"es\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\",\"name\":\"The Couchbase Blog\",\"url\":\"https:\/\/www.couchbase.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@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\":\"es\",\"@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\/es\/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 ya tiene soporte integrado para JDBC y JWT, pero debes crear tu propio almac\u00e9n de tokens de seguridad de Spring. Descubra c\u00f3mo hacerlo aqu\u00ed.","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\/es\/custom-token-store-spring-securtiy-oauth2\/","og_locale":"es_MX","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\/es\/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 minutos"},"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":"es","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 ya tiene soporte integrado para JDBC y JWT, pero debes crear tu propio almac\u00e9n de tokens de seguridad de Spring. Descubra c\u00f3mo hacerlo aqu\u00ed.","breadcrumb":{"@id":"https:\/\/www.couchbase.com\/blog\/custom-token-store-spring-securtiy-oauth2\/#breadcrumb"},"inLanguage":"es","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.couchbase.com\/blog\/custom-token-store-spring-securtiy-oauth2\/"]}]},{"@type":"ImageObject","inLanguage":"es","@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":"El blog de Couchbase","description":"Couchbase, la base de datos NoSQL","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":"es"},{"@type":"Organization","@id":"https:\/\/www.couchbase.com\/blog\/#organization","name":"El blog de Couchbase","url":"https:\/\/www.couchbase.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"es","@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, Defensor del Desarrollador, Couchbase","image":{"@type":"ImageObject","inLanguage":"es","@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 es un Developer Advocate para Couchbase y vive en Munich - Alemania. Tiene una s\u00f3lida experiencia como ingeniero de software y habla con fluidez Java, Python, Scala y Javascript. A Denis le gusta escribir sobre b\u00fasqueda, Big Data, AI, Microservicios y todo lo que pueda ayudar a los desarrolladores a hacer una aplicaci\u00f3n hermosa, m\u00e1s r\u00e1pida, estable y escalable.","sameAs":["https:\/\/x.com\/deniswsrosa"],"url":"https:\/\/www.couchbase.com\/blog\/es\/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":"Denis Rosa es un Developer Advocate para Couchbase y vive en Munich - Alemania. Tiene una s\u00f3lida experiencia como ingeniero de software y habla con fluidez Java, Python, Scala y Javascript. A Denis le gusta escribir sobre b\u00fasqueda, Big Data, AI, Microservicios y todo lo que pueda ayudar a los desarrolladores a hacer una aplicaci\u00f3n hermosa, m\u00e1s r\u00e1pida, estable y escalable."}],"_links":{"self":[{"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/posts\/5833","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/users\/8754"}],"replies":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/comments?post=5833"}],"version-history":[{"count":0,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/posts\/5833\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/media\/5834"}],"wp:attachment":[{"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/media?parent=5833"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/categories?post=5833"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/tags?post=5833"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/ppma_author?post=5833"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}