{"id":5830,"date":"2018-09-20T09:40:50","date_gmt":"2018-09-20T16:40:50","guid":{"rendered":"https:\/\/www.couchbase.com\/blog\/?p=5830"},"modified":"2023-08-11T11:13:47","modified_gmt":"2023-08-11T18:13:47","slug":"spring-security-oauth2_authentication","status":"publish","type":"post","link":"https:\/\/www.couchbase.com\/blog\/pt\/spring-security-oauth2_authentication\/","title":{"rendered":"Como configurar a autentica\u00e7\u00e3o OAuth2 com o Spring-Security-Oauth2"},"content":{"rendered":"<p>Como voc\u00ea deve ter notado em meus posts anteriores, sou um grande f\u00e3 de <a href=\"https:\/\/www.couchbase.com\/blog\/pt\/couchbase-spring-boot-spring-data\/\">Spring + Java<\/a> e <a href=\"https:\/\/www.couchbase.com\/blog\/pt\/kotlin-spring-boot-spring-data\/\">Primavera + Kotlin<\/a>. Consequentemente, sempre que preciso implementar uma autentica\u00e7\u00e3o OAuth 2.0, a biblioteca spring-security-oauth2 \u00e9 uma escolha natural.<\/p>\n<p>No entanto, n\u00e3o h\u00e1 quase nada no mercado que mostre como unir o Spring Security e o OAuth2 - conectando o <em><strong>seguran\u00e7a de primavera-oauth2<\/strong><\/em> com diferentes fontes de dados al\u00e9m de inMemory e JDBC. Como temos que configurar muitas coisas, dividirei este tutorial em tr\u00eas partes: Como autenticar um usu\u00e1rio, <a href=\"https:\/\/www.couchbase.com\/blog\/pt\/custom-token-store-spring-securtiy-oauth2\/\">como configurar um armazenamento de tokens<\/a> e como configurar clientes din\u00e2micos. Ent\u00e3o, vamos come\u00e7ar!<\/p>\n<p>Primeiro, presumo que voc\u00ea esteja usando uma das vers\u00f5es mais recentes do <em><strong>seguran\u00e7a de primavera-oauth2<\/strong><\/em>:<\/p>\n<pre class=\"lang:xhtml decode:true\">&lt;dependency&gt;\r\n    &lt;groupId&gt;org.springframework.security.oauth&lt;\/groupId&gt;\r\n    &lt;artifactId&gt;spring-security-oauth2&lt;\/artifactId&gt;\r\n    &lt;version&gt;2.3.3.RELEASE&lt;\/version&gt;\r\n&lt;\/dependency&gt;\r\n<\/pre>\n<p>Em segundo lugar, estou usando o Couchbase com o Spring Data. Se voc\u00ea estiver usando qualquer outra fonte de dados, ainda poder\u00e1 reutilizar muitos c\u00f3digos desta s\u00e9rie do blog.<\/p>\n<pre class=\"lang:xhtml decode:true\">&lt;dependency&gt;\r\n    &lt;groupId&gt;org.springframework.data&lt;\/groupId&gt;\r\n    &lt;artifactId&gt;spring-data-couchbase&lt;\/artifactId&gt;\r\n    &lt;version&gt;3.0.5.RELEASE&lt;\/version&gt;\r\n&lt;\/dependency&gt;\r\n<\/pre>\n<p>Al\u00e9m disso, adicionei <em><strong>Lombok<\/strong><\/em> como uma depend\u00eancia para reduzir o boilerplate do Java:<\/p>\n<pre class=\"lang:default decode:true\">&lt;dependency&gt;\r\n    &lt;groupId&gt;org.projectlombok&lt;\/groupId&gt;\r\n    &lt;artifactId&gt;lombok&lt;\/artifactId&gt;\r\n    &lt;optional&gt;true&lt;\/optional&gt;\r\n&lt;\/dependency&gt;<\/pre>\n<p>Vamos configurar nosso Resource Server, de acordo com a documenta\u00e7\u00e3o do Spring Security relacionada a <strong>seguran\u00e7a de primavera-oauth2<\/strong>: <em>\"Um Servidor de Recursos (pode ser o mesmo que o Servidor de Autoriza\u00e7\u00e3o ou um aplicativo separado) serve recursos que s\u00e3o protegidos pelo token OAuth2. O Spring OAuth fornece um filtro de autentica\u00e7\u00e3o do Spring Security que implementa essa prote\u00e7\u00e3o. Voc\u00ea pode ativ\u00e1-lo com\u00a0<strong>@EnableResourceServer<\/strong>\u00a0em um\u00a0<strong>@Configura\u00e7\u00e3o<\/strong>\u00a0e configur\u00e1-lo (conforme necess\u00e1rio) usando um ResourceServerConfigurer\"<\/em><\/p>\n<pre class=\"lang:java decode:true\">@Configuration\r\n@EnableResourceServer\r\npublic class ResourceServerConfig extends ResourceServerConfigurerAdapter {\r\n\r\n    private static final String RESOURCE_ID = \"resource_id\";\r\n\r\n    @Override\r\n    public void configure(ResourceServerSecurityConfigurer resources) {\r\n        resources.resourceId(RESOURCE_ID).stateless(false);\r\n    }\r\n}\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>Agora, vamos implementar uma interface chamada <em><strong>UserDetailsService.<\/strong><\/em> \u00c9 a interface respons\u00e1vel por fazer a ponte entre sua fonte de dados e o Spring Security OAuth:<\/p>\n<pre class=\"lang:java decode:true\">import com.bc.quicktask.standalone.model.CustomUserDetail;\r\nimport com.bc.quicktask.standalone.model.SecurityGroup;\r\nimport com.bc.quicktask.standalone.model.User;\r\nimport com.bc.quicktask.standalone.repository.UserRepository;\r\nimport lombok.extern.slf4j.Slf4j;\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\nimport org.springframework.security.core.userdetails.UserDetails;\r\nimport org.springframework.security.core.userdetails.UserDetailsService;\r\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\r\nimport org.springframework.stereotype.Service;\r\n\r\nimport java.util.List;\r\nimport java.util.stream.Collectors;\r\n\r\n@Slf4j\r\n@Service\r\npublic class CustomUserDetailsService implements UserDetailsService {\r\n\r\n    @Autowired\r\n    private UserRepository  userRepository;\r\n    @Autowired\r\n    private SecurityGroupService securityGroupService;\r\n\r\n    @Override\r\n    public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {\r\n        List&lt;User&gt; users = userRepository.findByUsername(name);\r\n        if(users.isEmpty()) {\r\n            throw new UsernameNotFoundException(\"Could not find the user \"+name);\r\n        }\r\n\r\n        User user = users.get(0);\r\n        List&lt;SecurityGroup&gt; securityGroups = securityGroupService.listUserGroups(user.getCompanyId(), user.getId());\r\n\r\n        return new CustomUserDetail(user, securityGroups.stream()\r\n                .map(e-&gt;e.getId())\r\n                .collect(Collectors.toList()) );\r\n    }\r\n}\r\n<\/pre>\n<p>No c\u00f3digo acima, estamos retornando uma classe do tipo <em><strong>UserDetails<\/strong><\/em>que tamb\u00e9m \u00e9 do Spring. Aqui est\u00e1 sua implementa\u00e7\u00e3o:<\/p>\n<pre class=\"lang:java decode:true\">Data\r\npublic class CustomUserDetail implements UserDetails {\r\n\r\n    private User user;\r\n    private List&lt;String&gt; groups;\r\n\r\n    public CustomUserDetail(User user, List&lt;String&gt; groups) {\r\n        this.user = user;\r\n        this.groups = groups;\r\n    }\r\n\r\n\r\n    @Override\r\n    public Collection&lt;? extends GrantedAuthority&gt; getAuthorities() {\r\n        return null;\r\n    }\r\n\r\n    @Override\r\n    public String getPassword() {\r\n        return user.getPassword();\r\n    }\r\n\r\n    @Override\r\n    public String getUsername() {\r\n        return user.getUsername();\r\n    }\r\n\r\n    @Override\r\n    public boolean isAccountNonExpired() {\r\n        return true;\r\n    }\r\n\r\n    @Override\r\n    public boolean isAccountNonLocked() {\r\n        return true;\r\n    }\r\n\r\n    @Override\r\n    public boolean isCredentialsNonExpired() {\r\n        return true;\r\n    }\r\n\r\n    @Override\r\n    public boolean isEnabled() {\r\n        return user.getIsEnabled();\r\n    }\r\n\r\n}\r\n<\/pre>\n<p>Eu poderia ter feito com que a classe User implementasse o UserDetails diretamente. No entanto, como meu caso de uso tamb\u00e9m exige a lista de grupos em que o usu\u00e1rio est\u00e1, adicionei a implementa\u00e7\u00e3o acima.<\/p>\n<p>Veja a seguir a apar\u00eancia do usu\u00e1rio, do SecurityGroup e de seus respectivos reposit\u00f3rios:<\/p>\n<pre class=\"lang:java decode:true\">@Data\r\npublic class User extends BasicEntity implements Serializable {\r\n\r\n    @Id\r\n    @NotNull\r\n    private String id;\r\n    @Field\r\n    @NotNull\r\n    private String username;\r\n    @Field\r\n    @NotNull\r\n    private String companyId;\r\n\r\n    @Field\r\n    @NotNull\r\n    private String password;\r\n    @NotNull\r\n    private Boolean isEnabled;\r\n    @Field\r\n    private Boolean isVisible;\r\n}\r\n<\/pre>\n<pre class=\"lang:default decode:true\">@N1qlPrimaryIndexed\r\n@ViewIndexed(designDoc = \"user\")\r\npublic interface UserRepository extends CouchbasePagingAndSortingRepository&lt;User, String&gt; {\r\n\r\n    List&lt;User&gt; findByUsername(String username);\r\n\r\n}\r\n<\/pre>\n<pre class=\"lang:java decode:true\">@Document\r\n@Data\r\n@NoArgsConstructor\r\n@Builder\r\npublic class SecurityGroup extends BasicEntity implements Serializable {\r\n\r\n    @Id\r\n    private String id;\r\n    @NotNull\r\n    @Field\r\n    private String name;\r\n    @Field\r\n    private String description;\r\n    @NotNull\r\n    @Field\r\n    private String companyId;\r\n    @Field\r\n    private List&lt;String&gt; users = new ArrayList&lt;&gt;();\r\n    @Field\r\n    private boolean removed = false;\r\n}\r\n<\/pre>\n<pre class=\"lang:java decode:true\">@N1qlPrimaryIndexed\r\n@ViewIndexed(designDoc = \"securityGroup\")\r\npublic interface SecurityGroupRepository extends\r\n        CouchbasePagingAndSortingRepository&lt;SecurityGroup, String&gt; {\r\n\r\n\r\n    @Query(\"#{#n1ql.selectEntity} where #{#n1ql.filter} and companyId = $1 and removed = false \" +\r\n            \" AND ARRAY_CONTAINS(users, $2) \")\r\n    List&lt;SecurityGroup&gt; listUserGroups(String companyId, String userId);\r\n}\r\n<\/pre>\n<p>O <em><strong>BasicEntity<\/strong><\/em> A classe tamb\u00e9m \u00e9 um pequeno hack para trabalhar melhor com o Spring Data e o Couchbase:<\/p>\n<pre class=\"lang:java decode:true\">public class BasicEntity {\r\n\r\n    @Getter(PROTECTED)\r\n    @Setter(PROTECTED)\r\n    @Ignore\r\n    protected String _class;\r\n}\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>Por fim, aqui est\u00e1 a implementa\u00e7\u00e3o da nossa classe SecurityConfig:<\/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\r\n    @Bean \r\n    @Override \r\n    public AuthenticationManager authenticationManagerBean() throws Exception { \r\n        return super.authenticationManagerBean(); \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 InMemoryTokenStore();\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<\/pre>\n<p>&nbsp;<\/p>\n<p>N\u00e3o podemos mais injetar diretamente o bean AuthenticationManager no Spring-Boot 2.0, mas ele ainda \u00e9 necess\u00e1rio para a estrutura Spring Security. Portanto, precisamos implementar um pequeno hack para obter acesso a esse objeto:<\/p>\n<pre class=\"lang:java decode:true\">@Bean \r\n@Override \r\npublic AuthenticationManager authenticationManagerBean() throws Exception { \r\n    return super.authenticationManagerBean(); \r\n}<\/pre>\n<p>Vamos dividir essa classe em pequenas partes para entender o que est\u00e1 acontecendo:<\/p>\n<pre class=\"lang:java decode:true\">@Bean\r\npublic PasswordEncoder encoder(){\r\n    return  NoOpPasswordEncoder.getInstance();\r\n}\r\n<\/pre>\n<p>A senha do meu usu\u00e1rio est\u00e1 em texto simples, ent\u00e3o eu simplesmente retorno uma nova inst\u00e2ncia de NoOpPasswordEncoder. Um padr\u00e3o comum \u00e9 retornar uma inst\u00e2ncia da classe BCryptPasswordEncoder.<\/p>\n<pre class=\"lang:default decode:true\">@Bean\r\npublic TokenStore tokenStore() {\r\n    return new InMemoryTokenStore();\r\n}\r\n<\/pre>\n<p>Por enquanto, usaremos um armazenamento de tokens na mem\u00f3ria. Veremos na parte 2 como usar tamb\u00e9m o Couchbase como um armazenamento de tokens.<\/p>\n<pre class=\"lang:java decode:true\">@Autowired\r\npublic void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {\r\n    auth.userDetailsService(customUserDetailsService)\r\n            .passwordEncoder(encoder());\r\n}\r\n<\/pre>\n<p>\u00c9 aqui que a m\u00e1gica acontece com o OAuth2 e o Spring Boot. Especificamente, estamos dizendo ao Spring para usar nosso <em><strong>CustomUserDetailsService<\/strong><\/em> para pesquisar usu\u00e1rios. Esse bloco de c\u00f3digo \u00e9 a parte principal do que fizemos at\u00e9 agora.<\/p>\n<pre class=\"lang:java decode:true\">@Bean\r\npublic 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<\/pre>\n<p>Esse bloco nos permitir\u00e1 fazer solicita\u00e7\u00f5es usando CORS (Cross-Origin Resource Sharing)<\/p>\n<pre class=\"lang:java decode:true\">@Override\r\npublic void configure( WebSecurity web ) throws Exception {\r\n    web.ignoring().antMatchers( HttpMethod.OPTIONS, \"\/**\" );\r\n}\r\n<\/pre>\n<p>E, por fim, se voc\u00ea precisar chamar sua API por meio do JQuery, tamb\u00e9m precisar\u00e1 adicionar o c\u00f3digo acima. Caso contr\u00e1rio, voc\u00ea receber\u00e1 uma mensagem \"<em><strong>A resposta para o preflight n\u00e3o tem o status HTTP ok<\/strong><\/em>.\" Erro.<\/p>\n<p>Agora s\u00f3 falta uma coisa: precisamos adicionar um servidor de autoriza\u00e7\u00e3o:<\/p>\n<pre class=\"lang:java decode:true\">import org.springframework.beans.factory.annotation.Autowired;\r\nimport org.springframework.context.annotation.Configuration;\r\nimport org.springframework.security.authentication.AuthenticationManager;\r\nimport org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;\r\nimport org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;\r\nimport org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;\r\nimport org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;\r\nimport org.springframework.security.oauth2.provider.token.TokenStore;\r\n\r\n\r\n@Configuration\r\n@EnableAuthorizationServer\r\npublic class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {\r\n\r\n    static final String CLIENT_ID = \"android-client\";\r\n    static final String CLIENT_SECRET = \"android-secret\";\r\n    static final String GRANT_TYPE_PASSWORD = \"password\";\r\n    static final String AUTHORIZATION_CODE = \"authorization_code\";\r\n    static final String REFRESH_TOKEN = \"refresh_token\";\r\n    static final String IMPLICIT = \"implicit\";\r\n    static final String SCOPE_READ = \"read\";\r\n    static final String SCOPE_WRITE = \"write\";\r\n    static final String TRUST = \"trust\";\r\n    static final int ACCESS_TOKEN_VALIDITY_SECONDS = 1*60*60;\r\n    static final int REFRESH_TOKEN_VALIDITY_SECONDS = 6*60*60;\r\n\r\n    @Autowired\r\n    private TokenStore tokenStore;\r\n\r\n    @Autowired\r\n    private AuthenticationManager authenticationManager;\r\n\r\n\r\n    @Override\r\n    public void configure(ClientDetailsServiceConfigurer configurer) throws Exception {\r\n\r\n        configurer\r\n                .inMemory()\r\n                .withClient(CLIENT_ID)\r\n                .secret(CLIENT_SECRET)\r\n                .authorizedGrantTypes(GRANT_TYPE_PASSWORD, AUTHORIZATION_CODE, REFRESH_TOKEN, IMPLICIT )\r\n                .scopes(SCOPE_READ, SCOPE_WRITE, TRUST)\r\n                .accessTokenValiditySeconds(ACCESS_TOKEN_VALIDITY_SECONDS).\r\n                refreshTokenValiditySeconds(REFRESH_TOKEN_VALIDITY_SECONDS);\r\n    }\r\n\r\n    @Override\r\n    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {\r\n        endpoints.tokenStore(tokenStore)\r\n                .authenticationManager(authenticationManager);\r\n    }\r\n\r\n\r\n   @Override\r\n    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {\r\n        oauthServer.tokenKeyAccess(\"permitAll()\")\r\n                .checkTokenAccess(\"isAuthenticated()\");\r\n    }\r\n}\r\n<\/pre>\n<p>Muito bem, agora voc\u00ea pode iniciar seu aplicativo e cham\u00e1-lo por meio do Postman ou do Jquery:<\/p>\n<pre class=\"lang:js decode:true\">\u00a0 var data = {\r\n    \"grant_type\": \"password\",\r\n    \"username\": \"myuser\",\r\n    \"password\":\"mypassword\",\r\n    \"client_id\":\"android-client\",\r\n    \"client_secret\":\"android-secret\"\r\n  }  \r\n\r\n  $.ajax({\r\n       'url': \"https:\/\/localhost:8080\/oauth\/token\",\r\n       'type': 'POST',\r\n       \"crossDomain\": true,\r\n       \"headers\": { 'Authorization': 'Basic YW5kcm9pZC1jbGllbnQ6YW5kcm9pZC1zZWNyZXQ=', \/\/android-client:android-secret in Base64\r\n       'Content-Type':'application\/x-www-form-urlencoded'},\r\n       \"data\":data,\r\n       'success': function (result) {\r\n           console.log( \"My Access token = \"+ result.access_token);\r\n           console.log( \"My refresh token = \"+ result.refresh_token);\r\n           console.log(\"expires in = \"+result.expires_in)\r\n           succesCallback()\r\n       },\r\n       'error': function (XMLHttpRequest, textStatus, errorThrown) {\r\n         errorCallback(XMLHttpRequest, textStatus, errorThrown)\r\n       }\r\n   });<\/pre>\n<p>&nbsp;<\/p>\n<h3><strong>Aumento do desempenho<\/strong><\/h3>\n<p>Se voc\u00ea estiver usando o Couchbase, sugiro que use o nome de usu\u00e1rio como a chave do seu documento. Isso permitir\u00e1 que voc\u00ea use o <em><strong>Armazenamento de chave-valor<\/strong><\/em> em vez de executar consultas N1QL, o que aumentar\u00e1 significativamente o desempenho de seu login.<\/p>\n<p>&nbsp;<\/p>\n<p>Se voc\u00ea tiver alguma d\u00favida sobre o Couchbase, a seguran\u00e7a do OAuth ou a otimiza\u00e7\u00e3o da autentica\u00e7\u00e3o do Spring e do OAuth2, envie um tweet para <a href=\"https:\/\/twitter.com\/deniswsrosa\">@deniswsrosa<\/a><\/p>","protected":false},"excerpt":{"rendered":"<p>As you might have noticed in my previous blog posts, I am a big fan of Spring + Java and Spring + Kotlin. Consequently, whenever I need to implement an OAuth 2.0 authentication, spring-security-oauth2 lib is a natural choice. However, [&hellip;]<\/p>","protected":false},"author":8754,"featured_media":9926,"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":[],"ppma_author":[9059],"class_list":["post-5830","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-best-practices-and-tutorials","category-java","category-security"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v26.3 (Yoast SEO v26.3) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Spring-Security-Oauth2: Connecting Varying Data Sources<\/title>\n<meta name=\"description\" content=\"This Spring-Security-Oauth2 tutorial contains 3 parts: how to authenticate a user, how to configure a token store and how to configure dynamic clients.\" \/>\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\/pt\/spring-security-oauth2_authentication\/\" \/>\n<meta property=\"og:locale\" content=\"pt_BR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"How to Configure OAuth2 Authentication With Spring-Security-Oauth2\" \/>\n<meta property=\"og:description\" content=\"This Spring-Security-Oauth2 tutorial contains 3 parts: how to authenticate a user, how to configure a token store and how to configure dynamic clients.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.couchbase.com\/blog\/pt\/spring-security-oauth2_authentication\/\" \/>\n<meta property=\"og:site_name\" content=\"The Couchbase Blog\" \/>\n<meta property=\"article:published_time\" content=\"2018-09-20T16:40:50+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2023-08-11T18:13:47+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2018\/09\/OAuth2-Spring-Security-Oauth2-blogbanner.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1575\" \/>\n\t<meta property=\"og:image:height\" content=\"628\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\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=\"3 minutos\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/spring-security-oauth2_authentication\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/spring-security-oauth2_authentication\/\"},\"author\":{\"name\":\"Denis Rosa, Developer Advocate, Couchbase\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/fe3c5273e805e72a5294611a48f62257\"},\"headline\":\"How to Configure OAuth2 Authentication With Spring-Security-Oauth2\",\"datePublished\":\"2018-09-20T16:40:50+00:00\",\"dateModified\":\"2023-08-11T18:13:47+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/spring-security-oauth2_authentication\/\"},\"wordCount\":645,\"commentCount\":1,\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/spring-security-oauth2_authentication\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/09\/OAuth2-Spring-Security-Oauth2-blogbanner.jpg\",\"articleSection\":[\"Best Practices and Tutorials\",\"Java\",\"Security\"],\"inLanguage\":\"pt-BR\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/spring-security-oauth2_authentication\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/spring-security-oauth2_authentication\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/spring-security-oauth2_authentication\/\",\"name\":\"Spring-Security-Oauth2: Connecting Varying Data Sources\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/spring-security-oauth2_authentication\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/spring-security-oauth2_authentication\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/09\/OAuth2-Spring-Security-Oauth2-blogbanner.jpg\",\"datePublished\":\"2018-09-20T16:40:50+00:00\",\"dateModified\":\"2023-08-11T18:13:47+00:00\",\"description\":\"This Spring-Security-Oauth2 tutorial contains 3 parts: how to authenticate a user, how to configure a token store and how to configure dynamic clients.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/spring-security-oauth2_authentication\/#breadcrumb\"},\"inLanguage\":\"pt-BR\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/spring-security-oauth2_authentication\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"pt-BR\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/spring-security-oauth2_authentication\/#primaryimage\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/09\/OAuth2-Spring-Security-Oauth2-blogbanner.jpg\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/09\/OAuth2-Spring-Security-Oauth2-blogbanner.jpg\",\"width\":1575,\"height\":628,\"caption\":\"A developer looking at the login screen on a laptop, working on Spring Security and OAuth2 authentication.\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/spring-security-oauth2_authentication\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.couchbase.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"How to Configure OAuth2 Authentication With Spring-Security-Oauth2\"}]},{\"@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\":\"pt-BR\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\",\"name\":\"The Couchbase Blog\",\"url\":\"https:\/\/www.couchbase.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"pt-BR\",\"@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\":\"pt-BR\",\"@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\/pt\/author\/denis-rosa\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Spring-Security-Oauth2: Connecting Varying Data Sources","description":"Este tutorial do Spring-Security-Oauth2 cont\u00e9m tr\u00eas partes: como autenticar um usu\u00e1rio, como configurar um armazenamento de token e como configurar clientes din\u00e2micos.","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\/pt\/spring-security-oauth2_authentication\/","og_locale":"pt_BR","og_type":"article","og_title":"How to Configure OAuth2 Authentication With Spring-Security-Oauth2","og_description":"This Spring-Security-Oauth2 tutorial contains 3 parts: how to authenticate a user, how to configure a token store and how to configure dynamic clients.","og_url":"https:\/\/www.couchbase.com\/blog\/pt\/spring-security-oauth2_authentication\/","og_site_name":"The Couchbase Blog","article_published_time":"2018-09-20T16:40:50+00:00","article_modified_time":"2023-08-11T18:13:47+00:00","og_image":[{"width":1575,"height":628,"url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2018\/09\/OAuth2-Spring-Security-Oauth2-blogbanner.jpg","type":"image\/jpeg"}],"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":"3 minutos"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.couchbase.com\/blog\/spring-security-oauth2_authentication\/#article","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/spring-security-oauth2_authentication\/"},"author":{"name":"Denis Rosa, Developer Advocate, Couchbase","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/fe3c5273e805e72a5294611a48f62257"},"headline":"How to Configure OAuth2 Authentication With Spring-Security-Oauth2","datePublished":"2018-09-20T16:40:50+00:00","dateModified":"2023-08-11T18:13:47+00:00","mainEntityOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/spring-security-oauth2_authentication\/"},"wordCount":645,"commentCount":1,"publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/spring-security-oauth2_authentication\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/09\/OAuth2-Spring-Security-Oauth2-blogbanner.jpg","articleSection":["Best Practices and Tutorials","Java","Security"],"inLanguage":"pt-BR","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.couchbase.com\/blog\/spring-security-oauth2_authentication\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.couchbase.com\/blog\/spring-security-oauth2_authentication\/","url":"https:\/\/www.couchbase.com\/blog\/spring-security-oauth2_authentication\/","name":"Spring-Security-Oauth2: Connecting Varying Data Sources","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/spring-security-oauth2_authentication\/#primaryimage"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/spring-security-oauth2_authentication\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/09\/OAuth2-Spring-Security-Oauth2-blogbanner.jpg","datePublished":"2018-09-20T16:40:50+00:00","dateModified":"2023-08-11T18:13:47+00:00","description":"Este tutorial do Spring-Security-Oauth2 cont\u00e9m tr\u00eas partes: como autenticar um usu\u00e1rio, como configurar um armazenamento de token e como configurar clientes din\u00e2micos.","breadcrumb":{"@id":"https:\/\/www.couchbase.com\/blog\/spring-security-oauth2_authentication\/#breadcrumb"},"inLanguage":"pt-BR","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.couchbase.com\/blog\/spring-security-oauth2_authentication\/"]}]},{"@type":"ImageObject","inLanguage":"pt-BR","@id":"https:\/\/www.couchbase.com\/blog\/spring-security-oauth2_authentication\/#primaryimage","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/09\/OAuth2-Spring-Security-Oauth2-blogbanner.jpg","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/09\/OAuth2-Spring-Security-Oauth2-blogbanner.jpg","width":1575,"height":628,"caption":"A developer looking at the login screen on a laptop, working on Spring Security and OAuth2 authentication."},{"@type":"BreadcrumbList","@id":"https:\/\/www.couchbase.com\/blog\/spring-security-oauth2_authentication\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.couchbase.com\/blog\/"},{"@type":"ListItem","position":2,"name":"How to Configure OAuth2 Authentication With Spring-Security-Oauth2"}]},{"@type":"WebSite","@id":"https:\/\/www.couchbase.com\/blog\/#website","url":"https:\/\/www.couchbase.com\/blog\/","name":"Blog do Couchbase","description":"Couchbase, o banco de dados 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":"pt-BR"},{"@type":"Organization","@id":"https:\/\/www.couchbase.com\/blog\/#organization","name":"Blog do Couchbase","url":"https:\/\/www.couchbase.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"pt-BR","@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 dos desenvolvedores, Couchbase","image":{"@type":"ImageObject","inLanguage":"pt-BR","@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 \u00e9 um Developer Advocate do Couchbase e mora em Munique, na Alemanha. Ele tem uma s\u00f3lida experi\u00eancia como engenheiro de software e fala fluentemente Java, Python, Scala e Javascript. Denis gosta de escrever sobre pesquisa, Big Data, IA, microsservi\u00e7os e tudo o mais que possa ajudar os desenvolvedores a criar um aplicativo bonito, mais r\u00e1pido, est\u00e1vel e escal\u00e1vel.","sameAs":["https:\/\/x.com\/deniswsrosa"],"url":"https:\/\/www.couchbase.com\/blog\/pt\/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 \u00e9 um Developer Advocate do Couchbase e mora em Munique, na Alemanha. Ele tem uma s\u00f3lida experi\u00eancia como engenheiro de software e fala fluentemente Java, Python, Scala e Javascript. Denis gosta de escrever sobre pesquisa, Big Data, IA, microsservi\u00e7os e tudo o mais que possa ajudar os desenvolvedores a criar um aplicativo bonito, mais r\u00e1pido, est\u00e1vel e escal\u00e1vel."}],"_links":{"self":[{"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/posts\/5830","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/users\/8754"}],"replies":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/comments?post=5830"}],"version-history":[{"count":0,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/posts\/5830\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/media\/9926"}],"wp:attachment":[{"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/media?parent=5830"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/categories?post=5830"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/tags?post=5830"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/ppma_author?post=5830"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}