Parte 1: Criando um Sistema de Login com Google e JWT no Spring Boot

Introdução O login social é uma abordagem moderna para autenticação que utiliza serviços de terceiros, como Google, Facebook ou GitHub, para identificar usuários. Ele é baseado em protocolos como OAuth 2.0, que nasceu em 2006 como um framework de autorização, e OpenID Connect (OIDC), uma camada de autenticação sobre o OAuth 2.0 lançada em 2014. Esses padrões permitem que aplicações deleguem a autenticação a provedores confiáveis, reduzindo a complexidade de gerenciar senhas e aumentando a segurança com infraestrutura consolidada. Vamos construir um sistema de login com o Google usando Spring Boot e Spring Security, gerando um JSON Web Token (JWT) para encapsular as informações do usuário autenticado? O foco será no Google, mas outros serviços compatíveis com OIDC incluem Microsoft Azure AD, Okta, Auth0 e Keycloak — todos seguindo o mesmo padrão de troca de tokens e fornecimento de dados via userinfo. Usaremos ao máximo as classes prontas do Spring, como OidcUserService e JwtEncoder, que encapsulam lógicas testadas e otimizadas, evitando reinventar a roda. Por que login social? Além de simplificar a UX (sem formulários de senha), ele aproveita autenticação multifator (MFA) e outros recursos de segurança dos provedores, tornando-o ideal para aplicações modernas. Credenciais do Google Caso não possua, crie as credenciais no Google. Acesse o Google Cloud Console Vá para console.cloud.google.com. Faça login com sua conta Google. Crie ou Selecione um Projeto No topo, clique no menu de projetos (ao lado de "Google Cloud") e selecione "New Project". Dê um nome (ex.: "MySpringBootApp"), escolha a organização (se aplicável) e clique em "Create". Selecione o projeto recém-criado no menu de projetos. Habilite a API do Google No menu lateral, vá para "APIs & Services" > "Library". Pesquise por "Google OAuth" ou "Google API" e habilite a "Google+ API" ou "People API" (necessária para userinfo com profile e email). Configure a Tela de Consentimento OAuth No menu lateral, vá para "APIs & Services" > "OAuth consent screen". Escolha o tipo de usuário: Internal: Apenas usuários da sua organização. External: Qualquer usuário com conta Google (mais comum para testes). Preencha os campos obrigatórios: App name: Nome da sua aplicação (ex.: "MySpringBootApp"). User support email: Seu email. Developer contact information: Seu email. Em "Scopes", adicione openid, profile, e email. Salve e continue. Crie as Credenciais No menu lateral, vá para "APIs & Services" > "Credentials". Clique em "Create Credentials" e selecione "OAuth 2.0 Client IDs". Configure: Application type: "Web application". Name: Um nome para identificar (ex.: "Spring Boot Client"). Authorized JavaScript origins: Deixe em branco (não necessário para Spring Boot). Authorized redirect URIs: Adicione a URI de callback do seu app, como http://localhost:8080/login/oauth2/code/google (padrão do Spring Boot). Clique em "Create". Obtenha as Credenciais Após criar, você verá o Client ID e o Client Secret. Configurando o projeto Tenha no seu projeto as dependências necessárias: spring-boot-starter-web: Inclui Tomcat e Spring MVC para criar endpoints REST. spring-boot-starter-oauth2-client: Fornece suporte a OAuth 2.0 e OIDC, com filtros como OAuth2AuthorizationRequestRedirectFilter. nimbus-jose-jwt: Biblioteca para criar JWTs assinados com algoritmos como RS256. pom.xml org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-oauth2-client com.nimbusds nimbus-jose-jwt 9.37 Configure as credenciais do Google no projeto (criadas anteriormente): client-id e client-secret: Credenciais únicas do Google para identificar sua aplicação. scope: Define os dados solicitados (openid para autenticação, profile para nome/foto, email para email). provider.google: Endpoints padrão do Google para autorização, tokens e informações do usuário. application.yml spring: security: oauth2: client: registration: google: client-id: [seu-client-id-do-google] client-secret: [seu-client-secret-do-google] scope: - openid - profile - email redirect-uri: http://localhost:8080/login/oauth2/code/google server: port: 8080 servlet: session: tracking-modes: cookie # Usa apenas cookies, não URL Fluxo Geral da Aplicação Requisição Inicial: O usuário acessa /home, uma rota protegida. Redirecionamento OAuth2: O filtro OAuth2AuthorizationRequestRedirectFilter detecta a falta de autenticação e redireciona para /oauth2/authorization/google. Autorização no Google: O Google exibe a tela de login/consentimento e retorna um cód

Mar 28, 2025 - 02:59
 0
Parte 1: Criando um Sistema de Login com Google e JWT no Spring Boot

Introdução

O login social é uma abordagem moderna para autenticação que utiliza serviços de terceiros, como Google, Facebook ou GitHub, para identificar usuários. Ele é baseado em protocolos como OAuth 2.0, que nasceu em 2006 como um framework de autorização, e OpenID Connect (OIDC), uma camada de autenticação sobre o OAuth 2.0 lançada em 2014.

Esses padrões permitem que aplicações deleguem a autenticação a provedores confiáveis, reduzindo a complexidade de gerenciar senhas e aumentando a segurança com infraestrutura consolidada.

Vamos construir um sistema de login com o Google usando Spring Boot e Spring Security, gerando um JSON Web Token (JWT) para encapsular as informações do usuário autenticado?

O foco será no Google, mas outros serviços compatíveis com OIDC incluem Microsoft Azure AD, Okta, Auth0 e Keycloak — todos seguindo o mesmo padrão de troca de tokens e fornecimento de dados via userinfo.

Usaremos ao máximo as classes prontas do Spring, como OidcUserService e JwtEncoder, que encapsulam lógicas testadas e otimizadas, evitando reinventar a roda.

Por que login social? Além de simplificar a UX (sem formulários de senha), ele aproveita autenticação multifator (MFA) e outros recursos de segurança dos provedores, tornando-o ideal para aplicações modernas.

Credenciais do Google

Caso não possua, crie as credenciais no Google.

  1. Acesse o Google Cloud Console

  2. Crie ou Selecione um Projeto

    • No topo, clique no menu de projetos (ao lado de "Google Cloud") e selecione "New Project".
    • Dê um nome (ex.: "MySpringBootApp"), escolha a organização (se aplicável) e clique em "Create".
    • Selecione o projeto recém-criado no menu de projetos.
  3. Habilite a API do Google

    • No menu lateral, vá para "APIs & Services" > "Library".
    • Pesquise por "Google OAuth" ou "Google API" e habilite a "Google+ API" ou "People API" (necessária para userinfo com profile e email).
  4. Configure a Tela de Consentimento OAuth

    • No menu lateral, vá para "APIs & Services" > "OAuth consent screen".
    • Escolha o tipo de usuário:
      • Internal: Apenas usuários da sua organização.
      • External: Qualquer usuário com conta Google (mais comum para testes).
    • Preencha os campos obrigatórios:
      • App name: Nome da sua aplicação (ex.: "MySpringBootApp").
      • User support email: Seu email.
      • Developer contact information: Seu email.
    • Em "Scopes", adicione openid, profile, e email.
    • Salve e continue.
  5. Crie as Credenciais

    • No menu lateral, vá para "APIs & Services" > "Credentials".
    • Clique em "Create Credentials" e selecione "OAuth 2.0 Client IDs".
    • Configure:
      • Application type: "Web application".
      • Name: Um nome para identificar (ex.: "Spring Boot Client").
      • Authorized JavaScript origins: Deixe em branco (não necessário para Spring Boot).
      • Authorized redirect URIs: Adicione a URI de callback do seu app, como http://localhost:8080/login/oauth2/code/google (padrão do Spring Boot).
    • Clique em "Create".
  6. Obtenha as Credenciais

    • Após criar, você verá o Client ID e o Client Secret.

Configurando o projeto

Tenha no seu projeto as dependências necessárias:

  • spring-boot-starter-web: Inclui Tomcat e Spring MVC para criar endpoints REST.
  • spring-boot-starter-oauth2-client: Fornece suporte a OAuth 2.0 e OIDC, com filtros como OAuth2AuthorizationRequestRedirectFilter.
  • nimbus-jose-jwt: Biblioteca para criar JWTs assinados com algoritmos como RS256.

pom.xml

    
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            org.springframework.boot
            spring-boot-starter-oauth2-client
        
        
            com.nimbusds
            nimbus-jose-jwt
            9.37
        
    

Configure as credenciais do Google no projeto (criadas anteriormente):

  • client-id e client-secret: Credenciais únicas do Google para identificar sua aplicação.
  • scope: Define os dados solicitados (openid para autenticação, profile para nome/foto, email para email).
  • provider.google: Endpoints padrão do Google para autorização, tokens e informações do usuário.
application.yml
spring:
  security:
    oauth2:
      client:
        registration:
          google:
            client-id: [seu-client-id-do-google]
            client-secret: [seu-client-secret-do-google]
            scope:
              - openid
              - profile
              - email
            redirect-uri: http://localhost:8080/login/oauth2/code/google
server:
  port: 8080
  servlet:
    session:
      tracking-modes: cookie  # Usa apenas cookies, não URL

Fluxo Geral da Aplicação

  1. Requisição Inicial: O usuário acessa /home, uma rota protegida.
  2. Redirecionamento OAuth2: O filtro OAuth2AuthorizationRequestRedirectFilter detecta a falta de autenticação e redireciona para /oauth2/authorization/google.
  3. Autorização no Google: O Google exibe a tela de login/consentimento e retorna um código para /login/oauth2/code/google.
  4. Troca de Tokens: O Spring usa o código para obter access_token e id_token do Google via token-uri.
  5. Carregamento do Usuário: O OidcUserService consulta o user-info-uri e retorna um OidcUser.
  6. Geração do JWT: O HomeController cria um JWT com os dados do usuário e retorna ao cliente.

Show Me The Code!

1. Configurar o Spring Security (SecurityConfig)

import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.proc.SecurityContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import org.springframework.security.oauth2.jwt.NimbusJwtEncoder;
import org.springframework.security.web.SecurityFilterChain;

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.UUID;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http, OidcUserService oidcUserService) throws Exception {
        return http
            .csrf(AbstractHttpConfigurer::disable) // Desativa proteção CSRF (adequado para APIs REST)
            .authorizeHttpRequests(authorize -> authorize
                .requestMatchers("/oauth2/**", "/login/oauth2/**", "/error", "/favicon.ico", "/").permitAll() // Rotas públicas
                .anyRequest().authenticated() // Todas as outras exigem autenticação
            )
            .oauth2Login(oauth2 -> oauth2
                .defaultSuccessUrl("/home", true) // Após login, vai para /home
                .failureUrl("/error?error=true") // Em caso de erro
                .userInfoEndpoint(userInfo -> userInfo.oidcUserService(oidcUserService())) // Define o serviço de usuário
            )
            .logout(logout -> logout.logoutSuccessUrl("/").permitAll()) // Logout redireciona para raiz
            .build();
    }

    @Bean
    public OidcUserService oidcUserService() {
        return new OidcUserService(); // Serviço padrão para carregar dados do usuário via OIDC
    }

    @Bean
    public JwtEncoder jwtEncoder() {
        JWKSource<SecurityContext> jwkSource = getJwkSource();
        return new NimbusJwtEncoder(jwkSource); // Encoder para gerar JWTs assinados
    }

    private JWKSource<SecurityContext> getJwkSource() {
        KeyPair keyPair = generateRsaKey();
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        RSAKey rsaKey = new RSAKey.Builder(publicKey)
            .privateKey(privateKey)
            .keyID(UUID.randomUUID().toString()) // Identificador único da chave
            .build();
        JWKSet jwkSet = new JWKSet(rsaKey); // Conjunto de chaves JWK
        return new ImmutableJWKSet<>(jwkSet); // Fonte imutável de chaves
    }

    private KeyPair generateRsaKey() {
        try {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            keyPairGenerator.initialize(2048); // Tamanho da chave para segurança
            return keyPairGenerator.generateKeyPair(); // Gera par público/privado
        } catch (Exception ex) {
            throw new IllegalStateException("Erro ao gerar RSA KeyPair", ex);
        }
    }
}
  • securityFilterChain(HttpSecurity http, OidcUserService oidcUserService):
    • Configura a cadeia de filtros de segurança.
    • csrf.disable(): Remove proteção CSRF, pois estamos usando tokens (não formulários).
    • authorizeHttpRequests: Define regras de acesso — /oauth2/** e /login/oauth2/** são rotas internas do OAuth2, /error e /favicon.ico são utilitárias, e / é a página inicial pública.
    • oauth2Login: Ativa o fluxo OIDC, redirecionando para /home após sucesso e usando o oidcUserService.
    • logout: Limpa a sessão e redireciona para /.
  • oidcUserService(): Retorna um serviço padrão que consulta o user-info-uri do Google e retorna um OidcUser com atributos como sub, email, e name.
  • jwtEncoder(): Cria um encoder que usa o JWKSource para assinar JWTs com RS256.
  • getJwkSource(): Monta um JWKSet com uma chave RSA, encapsulada em um ImmutableJWKSet para ser usada pelo encoder.
  • generateRsaKey(): Usa o algoritmo RSA com 2048 bits para gerar um par de chaves seguro.
2. Criar o Controlador (HomeController)
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.security.oauth2.jwt.JwtClaimsSet;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import org.springframework.security.oauth2.jwt.JwtEncoderParameters;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.Instant;
import java.util.HashMap;
import java.util.Map;

@RestController
public class HomeController {

    private final JwtEncoder jwtEncoder;

    public HomeController(JwtEncoder jwtEncoder) {
        this.jwtEncoder = jwtEncoder; // Injetado pelo Spring
    }

    @GetMapping("/home")
    public ResponseEntity<Map<String, String>> home(@AuthenticationPrincipal OidcUser oidcUser) {
        Instant now = Instant.now();
        JwtClaimsSet claims = JwtClaimsSet.builder()
            .issuer("self") // Identifica o emissor do token
            .issuedAt(now) // Data de emissão
            .expiresAt(now.plusSeconds(3600)) // Expira em 1 hora
            .subject(oidcUser.getSubject()) // ID único do usuário
            .claim("email", oidcUser.getEmail()) // Email do usuário
            .claim("name", oidcUser.getFullName()) // Nome completo
            .build();

        String jwt = jwtEncoder.encode(JwtEncoderParameters.from(claims)).getTokenValue(); // Gera o JWT assinado

        Map<String, String> response = new HashMap<>();
        response.put("message", "Welcome " + oidcUser.getFullName());
        response.put("jwt", jwt);
        return ResponseEntity.ok(response);
    }

    @GetMapping("/")
    public String root() {
        return "Bem-vindo à página inicial!";
    }
}
  • HomeController(JwtEncoder jwtEncoder): O construtor recebe o JwtEncoder via injeção de dependência.
  • home(@AuthenticationPrincipal OidcUser oidcUser):
    • @AuthenticationPrincipal: Extrai o OidcUser do SecurityContext, preenchido pelo Spring Security após o login.
    • JwtClaimsSet.builder(): Cria um conjunto de reivindicações (claims) para o JWT, incluindo iss, iat, exp, sub, e claims customizados (email, name).
    • jwtEncoder.encode(): Usa a chave privada RSA do JWKSource para assinar o JWT com RS256, retornando uma string no formato header.payload.signature.
    • ResponseEntity.ok(): Retorna um JSON com a mensagem e o JWT.
  • root(): Endpoint público simples para a raiz.
3. Testar o Sistema
  • Execute mvn spring-boot:run.
  • Acesse http://localhost:8080/home.
  • O fluxo redireciona ao Google, e após login, retorna:
  {
    "message": "Welcome Uira Teste",
    "jwt": "eyJraWQiOiJhYjNkZGYy..."
  }

Outras Formas de Fazer

  1. Senhas Tradicionais com Spring Security:
    • Como: Usa UserDetailsService e autenticação por formulário com um banco de usuários.
    • Vantagens: Total controle local, sem dependência externa.
    • Desvantagens: Complexidade de gerenciar senhas, sem integração social nativa.
  2. Auth0 como Provedor:
    • Como: Substitui o Google por Auth0, configurando-o no application.yml.
    • Vantagens: Suporte a múltiplos provedores, painel de gerenciamento.
    • Desvantagens: Custo adicional, menos controle sobre o backend.

Espero que este artigo tenha sido útil para entender o fluxo de autenticação e como aproveitar as ferramentas do framework.

O que achou dessa abordagem? Deixe seu feedback nos comentários abaixo