Você está na página 1de 9

Autenticação com JWT no Spring Boot http://andreybleme.com/2017-04-01/autenticaca...

ANDREYBLEME


Autenticação com JWT no Spring Boot
01 APR 2017

java spring security

JSON Web Token (JWT), é um padrão que define uma forma segura de transmitir mensagens
utilizando um token compacto e self-contained no formato de um objeto JSON.

É compacto porque além de leve, pode ser enviado através de um HTTP header, via URL, ou
como parâmetro no corpo de uma requisição HTTP. Dizemos que um JWT é self-contained
porque seu payload possui toda informação necessária para autenticar um usuário, assim, não
é necessário fazer mais que uma única consulta na base de dados.

JSON Web Tokens são comumente utilizados quando precisamos de autenticação em


aplicações com arquiteturas stateless (REST por exemplo). JWTs nos permitem autenticar um
usuário e garantir que as demais requisições serão feitas de forma autenticada, sendo possível
restringir acessos a recursos e serviços com diferentes níveis de permissões.

Estrutura do JSON Web Token

Um JWT é composto por três partes separadas por ponto.

hhh.ppp.sss

• H eader
• P ayload
• S ignature

O header consiste em duas partes diferentes: o tipo do token (no caso JWT), e o nome do
algorítimo responsável pelo hashing, HMAC SHA256 ou RSA.

1 {
2 "alg": "HS256",
3 "typ": "JWT"
4 }

header.json hosted with ❤ by GitHub view raw

Esse JSON será encoded via Base64Url e irá compor a primeira parte do token.

O payload contém o que chamamos de claims. Claims são atributos da entidade (no caso
usuário) e metadados. Um exemplo de payload:

1 {
2 "sub": "1337",
3 "name": "Lucas Bleme",
4 "admin": true
5 }

payload.json hosted with ❤ by GitHub view raw

Essse JSON será encoded via Base64Url e irá compor a segunda parte do token.

A signature verifica que o remetente do JWT é quem diz ser para garantir que a mensagem não
foi alterada durante o tráfego. Para criar a assinatura (signature), utiliza-se o header Base64
encoded, o payload também Base64 encoded, e o algorítimo especificado no header. Utilizando
o algorítimo HMAC SHA256, a signature ficaria assim:

1 HMACSHA256(
2 base64UrlEncode(header) + "." +
3 base64UrlEncode(payload),
4 secret)

signature.js hosted with ❤ by GitHub view raw

Por fim teremos uma String em Base64 separada por pontos, compondo o JSON Web Token.

eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTQ5MTUyMjg2NH0.OZQPWEgs-JaABOCEodulSiN-yd-T1gmZzrswY4kaNKNI_FyOVFJPaBsAqkcD3SgN010Y4VSFSNh6DXNq

1 of 9 11/4/17, 2:37 PM
Autenticação com JWT no Spring Boot http://andreybleme.com/2017-04-01/autenticaca...

Para testar esses conceitos e montar um JWT, você pode usar o JWT Debugger e ver o token
sendo formado na prática.

Criando o projeto Spring boot

A ideia é implementar autenticação para uma aplicação Springboot. Para criar um novo
projeto spring boot basta acessar https://start.spring.io/ e no campo “dependencies”, adicionar
apenas “Web”. Fazendo isso, um novo projeto pronto para ser executado será criado já com
todas as dependências que precisamos para executar a aplicação.

Feito isso, criaremos uma rota /home para verificarmos o funcionamento correto da nossa
aplicação, mais tarde criaremos outras rotas. Quando queremos criar métodos que
representem endpoints no Spring, precisamos criar um RestController . Por hora, vamos
adicionar o endpoint na única classe que temos no projeto, a classe principal.

1 package com.jwtme;
2
3 import org.springframework.boot.SpringApplication;
4 import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
5 import org.springframework.boot.autoconfigure.SpringBootApplication;
6 import org.springframework.web.bind.annotation.RequestMapping;
7 import org.springframework.web.bind.annotation.RestController;
8
9 @SpringBootApplication
10 @RestController
11 @EnableAutoConfiguration
12 public class JwtmeApplication {
13
14 public static void main(String[] args) {
15 SpringApplication.run(JwtmeApplication.class, args);
16 }
17
18 @RequestMapping("/home")
19 public String hello() {
20 return "Hello buddy!";
21 }
22 }

JwtmeApplication.java hosted with ❤ by GitHub view raw

Executando o método main, acessando o endereço localhost:8080/home devemos ver a


mensagem ““Hello buddy!”.

Agora vamos extender um pouco mais, criando a classe UserController . Essa classe também será
anotada com @RestController , vamos mapear a URL /users retornando um JSON simples
quando ela for acessada.

1 package com.jwtme.controllers;
2
3 import org.springframework.web.bind.annotation.RequestMapping;
4 import org.springframework.web.bind.annotation.ResponseBody;
5 import org.springframework.web.bind.annotation.RestController;
6
7 @RestController
8 public class UserController {
9
10 @RequestMapping("/users")
11 @ResponseBody
12 public String getUsers() {
13 return "{\"users\":[{\"name\":\"Lucas\", \"country\":\"Brazil\"}," +
14 "{\"name\":\"Jackie\",\"country\":\"China\"}]}";
15 }
16 }

UserController.java hosted with ❤ by GitHub view raw

Como fizemos na nossa classe principal, aqui também criamos um endpoint. A diferença é que
nosso método retorna um JSON. Por essa razão adicionamos a anotação @ResponseBody . Com
essa anotação, quando uma requisição especificar em seu header que aceita application/json , os
dados serão retornados para o client em formato JSON.

1 {"users":[{"name":"Lucas", "country":"Brazil"},{"name":"Jackie","country":"China"}]}

return.json hosted with ❤ by GitHub view raw

2 of 9 11/4/17, 2:37 PM
Autenticação com JWT no Spring Boot http://andreybleme.com/2017-04-01/autenticaca...

Adicionando segurança às rotas

Até esse momento, os recursos da nossa aplicação estão expostos para todos. Qualquer pessoa
pode acessar a lista de usuários do nosso servidor. Para que esse recurso seja restrito apenas a
usuários autenticados, vamos adicionar segurança à nossa aplicação com JSON Web Tokens!

Nesse exemplo, vamos expor publicamente apenas os recursos disponíveis em /home e /login .
Para acessar /users será necessário que o usuário envie ao nosso servidor um token JWT
válido. Para isso, vamos adicionar duas dependências ao pom.xml . A primeira é
spring-boot-starter-security que nos permite trabalhar com autenticação no Spring, e a segunda, é

jjwt que vai gerenciar nossos JWTs.

1 <dependencies>
2 <dependency>
3 <groupId>org.springframework.boot</groupId>
4 <artifactId>spring-boot-starter-web</artifactId>
5 </dependency>
6
7 <dependency>
8 <groupId>org.springframework.boot</groupId>
9 <artifactId>spring-boot-starter-test</artifactId>
10 <scope>test</scope>
11 </dependency>
12
13 <dependency>
14 <groupId>org.springframework.boot</groupId>
15 <artifactId>spring-boot-starter-security</artifactId>
16 </dependency>
17
18 <dependency>
19 <groupId>io.jsonwebtoken</groupId>
20 <artifactId>jjwt</artifactId>
21 <version>0.7.0</version>
22 </dependency>
23 </dependencies>

pom.xml hosted with ❤ by GitHub view raw

Adicionadas as dependências, a primeira coisa que queremos fazer é deixar de expor os


recursos de /users publicamente. Por tanto, vamos criar uma configuração que restrinja esse
acesso criando uma nova classe chamada WebSecurityConfig . Essa nova classe vai ser uma classe
filha da classe WebSecurityConfigurerAdapter do Spring security. Nesse exemplo, vamos cria-la em
um novo pacote com.jwtme.security .

1 package com.jwtme.security;
2
3 import org.springframework.context.annotation.Configuration;
4 import org.springframework.http.HttpMethod;
5 import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
6 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
7 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
8 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
9 import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
10
11 @Configuration
12 @EnableWebSecurity
13 public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
14
15 @Override
16 protected void configure(HttpSecurity httpSecurity) throws Exception {
17 httpSecurity.csrf().disable().authorizeRequests()
18 .antMatchers("/home").permitAll()
19 .antMatchers(HttpMethod.POST, "/login").permitAll()
20 .anyRequest().authenticated()
21 .and()
22
23 // filtra requisições de login
24 .addFilterBefore(new JWTLoginFilter("/login", authenticationManager()),
25 UsernamePasswordAuthenticationFilter.class)
26
27 // filtra outras requisições para verificar a presença do JWT no header
28 .addFilterBefore(new JWTAuthenticationFilter(),
29 UsernamePasswordAuthenticationFilter.class);
30 }
31
32 @Override
33 protected void configure(AuthenticationManagerBuilder auth) throws Exception {
34 // cria uma conta default

3 of 9 11/4/17, 2:37 PM
Autenticação com JWT no Spring Boot http://andreybleme.com/2017-04-01/autenticaca...

35 auth.inMemoryAuthentication()
36 .withUser("admin")
37 .password("password")
38 .roles("ADMIN");
39 }
40 }

WebSecurityConfig.java hosted with ❤ by GitHub view raw

Nessa classe definimos que todos podem acessar /home , e que o endpoint /login está
disponível apenas via requisições do tipo POST. Para todas as demais rotas a autenticação é
necessária. Não se preocupe com erros de compilação, já que ainda não criamos as classes
JWTLoginFilter e JWTAuthenticationFilter . Vamos cria-las em breve. Elas serão as classes

responsáveis por filtrar as requisições feitas em /login e em todas as outras rotas, para decidir
como essas requisições deverão ser tratadas. Repare que também adicionamos uma conta
default aqui, para testarmos o funcionamento da autenticação.

Uma grande vantagem de estarmos utilizando o Spring boot aqui, é que em momento algum foi
necessário mudar o código já existente das rotas, nem adicionar arquivos .xml de configuração!
Tudo foi feito pragmaticamente com uma classe de configuração anotada com @Configuration .

As classes JWTLoginFilter e JWTAuthenticationFilter serão responsáveis por lidar com login e


validação da autenticação dos usuários quando acessarem outras rotas. Por tanto, antes nos
preocuparmos com elas, vamos ter que criar as classes que irão lidar com os JWTs.

Criando os JWT Services no Spring boot

Nossos JWT services serão responsáveis por gerara e validar nossos JWT tokens. Nesse
exemplo vamos criar um token baseado em username e um expiration_time , em seguida iremos
assiná-lo com uma palavra chave secret .

Para criar e verificar nossos tokens, vamos criar a classe TokenAuthenticationService dentro do
mesmo pacote com.jwtme.security . Nela vamos utilizar a classe que incluímos como dependência
io.jsonwebtoken.Jwts para validar os tokens.

1 package com.jwtme.security;
2
3 import java.util.Collections;
4 import java.util.Date;
5
6 import javax.servlet.http.HttpServletRequest;
7 import javax.servlet.http.HttpServletResponse;
8
9 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
10 import org.springframework.security.core.Authentication;
11
12 import io.jsonwebtoken.Jwts;
13 import io.jsonwebtoken.SignatureAlgorithm;
14
15 public class TokenAuthenticationService {
16
17 // EXPIRATION_TIME = 10 dias
18 static final long EXPIRATION_TIME = 860_000_000;
19 static final String SECRET = "MySecret";
20 static final String TOKEN_PREFIX = "Bearer";
21 static final String HEADER_STRING = "Authorization";
22
23 static void addAuthentication(HttpServletResponse response, String username) {
24 String JWT = Jwts.builder()
25 .setSubject(username)
26 .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
27 .signWith(SignatureAlgorithm.HS512, SECRET)
28 .compact();
29
30 response.addHeader(HEADER_STRING, TOKEN_PREFIX + " " + JWT);
31 }
32
33 static Authentication getAuthentication(HttpServletRequest request) {
34 String token = request.getHeader(HEADER_STRING);
35
36 if (token != null) {
37 // faz parse do token
38 String user = Jwts.parser()
39 .setSigningKey(SECRET)
40 .parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
41 .getBody()

4 of 9 11/4/17, 2:37 PM
Autenticação com JWT no Spring Boot http://andreybleme.com/2017-04-01/autenticaca...

42 .getSubject();
43
44 if (user != null) {
45 return new UsernamePasswordAuthenticationToken(user, null, Collections.emptyList());
46 }
47 }
48 return null;
49 }
50
51 }

TokenAuthenticationService.java hosted with ❤ by GitHub view raw

Autenticando os JWTs

Já temos tudo que precisamos para usar os JWTs no processo de autenticação. Agora vamos
criar a classe JWTLoginFilter para interceptar as requisições do tipo POST feitas em /login e
tentar autenticar o usuário. Quando o usuário for autenticado com sucesso, um método irá
retornar um JWT com a autorização Authorization no cabeçalho da resposta.

1 package com.jwtme.security;
2
3 import java.io.IOException;
4 import java.util.Collections;
5
6 import javax.servlet.FilterChain;
7 import javax.servlet.ServletException;
8 import javax.servlet.http.HttpServletRequest;
9 import javax.servlet.http.HttpServletResponse;
10
11 import org.springframework.security.authentication.AuthenticationManager;
12 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
13 import org.springframework.security.core.Authentication;
14 import org.springframework.security.core.AuthenticationException;
15 import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
16 import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
17
18 import com.fasterxml.jackson.databind.ObjectMapper;
19
20 public class JWTLoginFilter extends AbstractAuthenticationProcessingFilter {
21
22 protected JWTLoginFilter(String url, AuthenticationManager authManager) {
23 super(new AntPathRequestMatcher(url));
24 setAuthenticationManager(authManager);
25 }
26
27 @Override
28 public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
29 throws AuthenticationException, IOException, ServletException {
30
31 AccountCredentials credentials = new ObjectMapper()
32 .readValue(request.getInputStream(), AccountCredentials.class);
33
34 return getAuthenticationManager().authenticate(
35 new UsernamePasswordAuthenticationToken(
36 credentials.getUsername(),
37 credentials.getPassword(),
38 Collections.emptyList()
39 )
40 );
41 }
42
43 @Override
44 protected void successfulAuthentication(
45 HttpServletRequest request,
46 HttpServletResponse response,
47 FilterChain filterChain,
48 Authentication auth) throws IOException, ServletException {
49
50 TokenAuthenticationService.addAuthentication(response, auth.getName());
51 }
52
53 }

JWTLoginFilter.java hosted with ❤ by GitHub view raw

Aqui o método attemptAuthentication é quem lida com a tentativa de autenticação. Pegamos o


username e password da requisição, e utilizamos o AuthenticationManager para verificar se os

dados são correspondentes aos dados do nosso usuário existente. Caso os dados estejam

5 of 9 11/4/17, 2:37 PM
Autenticação com JWT no Spring Boot http://andreybleme.com/2017-04-01/autenticaca...

corretos, invocamos o método successfulAuthentication para enviar ao service


TokenAuthenticationService o username do usuário para que este service adicione um JWT à nossa

resposta (response).

Agora sim, criaremos a classe JWTAuthenticationFilter .

1 package com.jwtme.security;
2
3 import java.io.IOException;
4
5 import javax.servlet.FilterChain;
6 import javax.servlet.ServletException;
7 import javax.servlet.ServletRequest;
8 import javax.servlet.ServletResponse;
9 import javax.servlet.http.HttpServletRequest;
10
11 import org.springframework.security.core.Authentication;
12 import org.springframework.security.core.context.SecurityContextHolder;
13 import org.springframework.web.filter.GenericFilterBean;
14
15 public class JWTAuthenticationFilter extends GenericFilterBean {
16
17 @Override
18 public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
19 throws IOException, ServletException {
20
21 Authentication authentication = TokenAuthenticationService
22 .getAuthentication((HttpServletRequest) request);
23
24 SecurityContextHolder.getContext().setAuthentication(authentication);
25 filterChain.doFilter(request, response);
26 }
27
28 }

JWTAuthenticationFilter.java hosted with ❤ by GitHub view raw

Nessa classe validamos a existência de um JWT nas requisições, com ajuda do service
TokenAuthenticationService .

Agora só falta criarmos a classe AccountCredentials , que será utilizada para enviarmos as
credenciais da conta a ser validada quando fizermos requisições do tipo POST à URL /login .
Requests de login portanto, devem ser feitas com um objeto do tipo AccountCredentials em seu
body.

1 package com.jwtme.security;
2
3 public class AccountCredentials {
4
5 private String username;
6 private String password;
7
8 public String getUsername() {
9 return username;
10 }
11
12 public void setUsername(String username) {
13 this.username = username;
14 }
15
16 public String getPassword() {
17 return password;
18 }
19
20 public void setPassword(String password) {
21 this.password = password;
22 }
23 }

AccountCredentials.java hosted with ❤ by GitHub view raw

Executando a autenticação

Nossa aplicação agora está segura e podemos realizar autenticações com JWT! Após reiniciar a
aplicação vamos tentar acessar o endereço http://localhost:8080/users . Se tudo estiver
funcionando corretamente, a resposta deve ser Access Denied .

Para nos autenticarmos corretamente, vamos enviar uma requisição do tipo POST para o

6 of 9 11/4/17, 2:37 PM
Autenticação com JWT no Spring Boot http://andreybleme.com/2017-04-01/autenticaca...

endereço http://localhost:8080/login com as credencias do nosso usuário default no body. Usando


o Postman a requisição feita com sucesso deve se parecer com isso:

No header da resposta dessa requisição temos nosso token com o prefixo Bearer . Para buscar
os usuários, agora precisamos enviar no header da requisição nosso token incluindo o
cabeçalho Authorization com o JWT que recebemos quando realizamos a autenticação com
sucesso.

Referências e links úteis

JSON Web Tokens

REST Security with JWT

What’s the difference between “Request Payload” vs “Form Data”

Spring Initializr

7 of 9 11/4/17, 2:37 PM
Autenticação com JWT no Spring Boot http://andreybleme.com/2017-04-01/autenticaca...

20 Comentários andreybleme 
1 Entrar

Ordenar por Mais votados


 Recomendar ⤤ Compartilhar

Participe da discussão...
FAZER LOGIN COM
OU REGISTRE-SE NO DISQUS ?

Nome

Sidronio Lima • 2 meses atrás


Perfeito, funcionou muito bem incluisve com autenticação JDBC. Agora é tentar
encapsular em um jar e reutilizar em outros projetos.
1△ ▽ • Responder • Compartilhar ›

Henrique • 3 dias atrás


Opa, muito bom teu post. Minha dúvida é... Configurei o Spring para trabalhar com o
CORS, porém não tenho noção de como obter o token com Vue. Quando eu pego
''response.headers.get('Authorization');'' recebo undefined. Pode ser algum problema
no servidor?
△ ▽ • Responder • Compartilhar ›

Henrique > Henrique • 3 dias atrás


Aliás, o retorno da minha requisição é exatamente igual o do post.
△ ▽ • Responder • Compartilhar ›

Luis Henrique Borges • 2 meses atrás


Olá

Estou com esse pequeno problema na hora de fazer a requisição, será que
poderiam me ajudar?

{
"timestamp": 1504558932845,
"status": 500,
"error": "Internal Server Error",
"exception": "io.jsonwebtoken.SignatureException",
"message": "JWT signature does not match locally computed signature. JWT validity
cannot be asserted and should not be trusted.",
"path": "/rest/users"
}
△ ▽ • Responder • Compartilhar ›

Lucas Andrey Mod > Luis Henrique Borges • 2 meses atrás


@Luis Henrique Borges o método "static void addAuthentication()" gera um
novo TOKEN cada vez que a aplicação é iniciada. Isso porquê valores
estáticos são inicializados quando uma classe é criada. Dessa forma, um
token JWT só é válido enquanto a aplicação não é reiniciada! Esse pode ser
o problema...

Faça o seguinte: Inicie a aplicação, gere um token, tente utilizá-lo na


requisição sem reiniciar o server. Se ainda assim o problema persistir, veja se
este link te ajuda (https://cursos.alura.com.br...
1△ ▽ • Responder • Compartilhar ›

Henrique Schmitt • 2 meses atrás


Ótimo tutorial, e se eu quiser validar usuário e senha indo até um repositório
utilizando Spring Data? Vou dar autowired no repositório dentro de JWTLoginFilter ?
△ ▽ • Responder • Compartilhar ›

Lucas Andrey Mod > Henrique Schmitt • 2 meses atrás


Oi @Henrique Schmitt, na verdade a alteração deve ser feita na classe
"WebSecurityConfig". No método "configure" vc não vai precisar criar uma
conta default.
△ ▽ • Responder • Compartilhar ›

Henrique Schmitt > Lucas Andrey • 2 meses atrás


Sim, fiz com JdbcAuthentication, funcionou 100%, obrigado!

auth.jdbcAuthentication().usersByUsernameQuery(usersQuery)
.authoritiesByUsernameQuery(rolesQuery)
.dataSource(dataSource)
.passwordEncoder(bCryptPasswordEncoder);
1△ ▽ • Responder • Compartilhar ›

Douglas Pac Martins • 2 meses atrás


Amigo estou recebendo via POSTMAN a seguinte resposta
{
"timestamp": 1503513455713,
"status": 500,
"error": "Internal Server Error",
"exception": "com.fasterxml.jackson.databind.JsonMappingException",
"message": "No content to map due to end-of-input\n at [Source:
org.apache.catalina.connector.CoyoteInputStream@605aca6d; line: 1, column: 0]",
"path": "/login"
}

sabe me dizer por que?


△ ▽ • Responder • Compartilhar ›

8 of 9 11/4/17, 2:37 PM
Autenticação com JWT no Spring Boot http://andreybleme.com/2017-04-01/autenticaca...

    
© 2017 Lucas Andrey. All rights reserved.

9 of 9 11/4/17, 2:37 PM