Autenticação e Autorização com JWT em Aplicações Spring Boot

Para iniciar, adicione as dependências necessárias no arquivo pom.xml do projeto:

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.12.6</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.12.6</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.12.6</version>
    <scope>runtime</scope>
</dependency>

Classe Utilitária para Manipulação do Token

A seguir, a implementação da classe responsável pela criação e validação dos tokens JWT:

public final class TokenJwtHelper {

    private TokenJwtHelper() {}

    private static SecretKey obterChaveAssinatura(ConfiguracaoJwt props) {
        return Keys.hmacShaKeyFor(
            Decoders.BASE64URL.decode(props.getChaveSecreta())
        );
    }

    public static String criarToken(Usuario usuario, ConfiguracaoJwt props) {
        Instant agora = Instant.now();
        Instant expiracao = agora.plusMillis(props.getTempoExpiracao());

        return Jwts.builder()
                .subject(usuario.getNomeUsuario())
                .issuedAt(Date.from(agora))
                .claim("identificador", usuario.getIdentificador())
                .expiration(Date.from(expiracao))
                .signWith(obterChaveAssinatura(props), Jwts.SIG.HS256)
                .compact();
    }

    public static Claims extrairDados(String token, ConfiguracaoJwt props) {
        return Jwts.parser()
                .verifyWith(obterChaveAssinatura(props))
                .build()
                .parseSignedClaims(token)
                .getPayload();
    }
}

Preparação dos Componentes de Suporte

Propreidades de Configuração

Arquivo application.yml com as configurações do token:

seguranca:
  jwt:
    chave-secreta: QW5vdGhlclN0cmluZ1NlY3JldGFLZXlGb3JKd3RTaWduaW5nUHVycG9zZXMxMjM0NTY3OA==
    tempo-expiracao: 7200000
    nome-cabecalho: Authorization

Classe de Mapeamento das Propriedades

@Component
@ConfigurationProperties(prefix = "seguranca.jwt")
@Data
public class ConfiguracaoJwt {
    private String chaveSecreta;
    private long tempoExpiracao;
    private String nomeCabecalho;
}

Estrutura de Resposta Padronizada

@Data
public class RespostaApi<T> implements Serializable {

    private Integer codigo;
    private String mensagem;
    private T conteudo;

    public static <T> RespostaApi<T> sucesso(T dados) {
        RespostaApi<T> resposta = new RespostaApi<>();
        resposta.setCodigo(200);
        resposta.setConteudo(dados);
        return resposta;
    }

    public static <T> RespostaApi<T> erro(String descricao) {
        RespostaApi<T> resposta = new RespostaApi<>();
        resposta.setCodigo(400);
        resposta.setMensagem(descricao);
        return resposta;
    }
}

Constantes da Aplicação

public final class ConstantesJwt {
    private ConstantesJwt() {}
    
    public static final String CAMPO_IDENTIFICADOR = "identificador";
}

Implementação do Endpoint de Autenticação

Camada de Controle

@RestController
@RequestMapping("/auth")
public class AutenticacaoController {

    @Autowired
    private AutenticacaoService autenticacaoService;
    
    @Autowired
    private ConfiguracaoJwt configuracaoJwt;

    @PostMapping("/entrar")
    public RespostaApi<UsuarioResposta> autenticar(@RequestBody CredenciaisDTO credenciais) {
        Usuario usuarioAutenticado = autenticacaoService.verificarCredenciais(credenciais);

        String jwt = TokenJwtHelper.criarToken(usuarioAutenticado, configuracaoJwt);

        UsuarioResposta dadosResposta = UsuarioResposta.builder()
                .identificador(usuarioAutenticado.getIdentificador())
                .nomeExibicao(usuarioAutenticado.getNomeUsuario())
                .tokenAcesso(jwt)
                .build();

        return RespostaApi.sucesso(dadosResposta);
    }
}

Camada de Serviço

public interface AutenticacaoService {
    Usuario verificarCredenciais(CredenciaisDTO credenciais);
}

@Service
public class AutenticacaoServiceImpl implements AutenticacaoService {

    @Autowired
    private RepositorioUsuario repositorioUsuario;

    @Override
    public Usuario verificarCredenciais(CredenciaisDTO credenciais) {
        Usuario cadastrado = repositorioUsuario.buscarPorNomeUsuario(credenciais.getNomeUsuario());

        if (cadastrado == null) {
            throw new RuntimeException("Credenciais inválidas");
        }

        if (!cadastrado.getSenha().equals(credenciais.getSenha())) {
            throw new RuntimeException("Credenciais inválidas");
        }

        return cadastrado;
    }
}

Camada de Acesso a Dados

@Mapper
public interface RepositorioUsuario {

    @Select("SELECT * FROM usuarios WHERE nome_usuario = #{nomeUsuario}")
    Usuario buscarPorNomeUsuario(String nomeUsuario);
}

Teste da Implementação

Requisição para o endpoint POST /auth/entrar:

POST http://localhost:8080/auth/entrar
Content-Type: application/json

{
  "nomeUsuario": "maria_silva",
  "senha": "senhaSegura123"
}

Exemplo de resposta obtida:

{
  "codigo": 200,
  "mensagem": null,
  "conteudo": {
    "identificador": 42,
    "nomeExibicao": "maria_silva",
    "tokenAcesso": "eyJhbGciOiJIUzI1NiJ9.eyJpZGVudGlmaWNhZG9yIjo0MiwiZXhwIjoxNzQxMTAzMzI2fQ.signatureAqui"
  }
}

Tags: jwt Spring Boot jjwt java autenticacao

Publicado em 8-5 08:20