Json Web Token é um padrão aberto para transmissão de informações de forma segura entre partes.
O JWT pode ser utilizados para várias tarefas, mas nesse post vamos utilizar para autenticação de usuários.
A vantagem de se utilizar o JWT, é que toda a informação referente ao usuário autenticado, já está toda guardada nesse token, então todas as vezes que fizermos a chamada de uma API passando o token, a aplicação não precisa ir ao banco de dados buscar informações sobre o usuário autenticado, pois podemos armazenar todas essas informações dentro do token.
Um JWT é dividido em 3 partes que são separadas por pontos, um JWT se parece com isso: xxxxx.yyyyy.zzzzz
1 – Header: contém duas informações, o tipo do token, que no caso é JWT e o algoritmo que foi utilizado na criptografia.
2 – Payload: aqui que vamos colocar as informações sobre o usuário, tempo de expiração do token, etc.
3 – Signature: Para criar a assinatura é preciso fazer o encode do header, payload, secret e do algoritmo especificado no header.
<!–more–>
[markdown]
“`
signature(
base64UrlEncode(header) + “.” +
base64UrlEncode(payload),
secret)
“`
Overview
O usuário irá se autenticar na aplicação utilizando o serviço de login, após logado, será criado um token JWT com todas as informações do usuário e esse token será adicionado na resposta do login. A aplicação cliente que efetuou o login, deve guardar esse token para futuras requisições.
Vamos a implementação agora.
Podemos criar nosso projeto utilizando o http://start.spring.io/ para facilitar um pouco, devemos selecionar somente as dependências do Web e Security.
Segundo passo é adicionar a dependência do JWT ao projeto, existem várias bibliotecas, utilizaremos a seguinte:
https://github.com/jwtk/jjwt
“`
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.6.0</version>
</dependency>
“`
Configurando o Spring Security:
“`
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
@Autowired
private UserDetailsService userDetailsService;
public WebSecurityConfig() {
super(true);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
TokenAuthenticationService tokenAuthenticationService = new TokenAuthenticationService(“REDSPARK_SECRET”, userDetailsService);
JWTAuthenticationSuccessHandler athenticationSuccessHandler = new JWTAuthenticationSuccessHandler(tokenAuthenticationService);
JWTAuthenticationFailureHandler authenticationFailureHandler = new JWTAuthenticationFailureHandler();
http.formLogin().loginProcessingUrl(“/login”).successHandler(athenticationSuccessHandler).failureHandler(authenticationFailureHandler)
.and().exceptionHandling().and().anonymous().and().authorizeRequests()
.antMatchers(“/login”).permitAll().and()
.addFilterBefore(new StatelessAuthenticationFilter(tokenAuthenticationService), UsernamePasswordAuthenticationFilter.class);
}
}
“`
Pontos importantes dessa configuração:
Na classe seguinte, estamos criando o nosso UserDetailsService, que é responsável por recuperar o usuário.
“`
@Configuration
public class WebSecurityConfiguration extends GlobalAuthenticationConfigurerAdapter {
@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService());
}
@Bean
public UserDetailsService userDetailsService() {
return new UserDetailsService() {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return new User(username, username, true, true, true, true, AuthorityUtils.createAuthorityList(“ROLE_USUARIO”));
}
};
}
}
“`
Depois que autenticação é realizada com sucesso no login, a requisição passa pelo JWTAuthenticationSuccessHandler, a função dessa classe é chamar o serviço para criar o token e adicionar no header do response.
“`
public class JWTAuthenticationSuccessHandler implements AuthenticationSuccessHandler{
private TokenAuthenticationService tokenService;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
tokenService.addAuthentication(response, authentication);
}
public JWTAuthenticationSuccessHandler(TokenAuthenticationService tokenService) {
super();
this.tokenService = tokenService;
}
}
“`
TokenAuthenticationService é responsável por adicionar e retirar o token do header da requisição.
“`
public class TokenAuthenticationService {
private static final String AUTH_HEADER_NAME = “X-AUTH-TOKEN”;
private final TokenHandler tokenHandler;
public TokenAuthenticationService(String secret, UserDetailsService userService) {
tokenHandler = new TokenHandler(secret, userService);
}
public void addAuthentication(HttpServletResponse response, Authentication authentication) {
final User user = (User) authentication.getPrincipal();
response.addHeader(AUTH_HEADER_NAME, tokenHandler.createTokenForUser(user));
}
public Authentication getAuthentication(HttpServletRequest request) {
final String token = request.getHeader(AUTH_HEADER_NAME);
if (token != null) {
final User user = tokenHandler.parseUserFromToken(token);
if (user != null) {
return new UserAuthentication(user);
}
}
return null;
}
}
“`
A classe TokenHandler é responsável por criar e ler os tokens.
“`
public class TokenHandler {
private final String secret;
private final UserDetailsService userService;
public TokenHandler(String secret, UserDetailsService userService) {
this.secret = secret;
this.userService = userService;
}
public User parseUserFromToken(String token) {
String username = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody().getSubject();
return (User) userService.loadUserByUsername(username);
}
public String createTokenForUser(User user) {
return Jwts.builder().setSubject(user.getUsername()).signWith(SignatureAlgorithm.HS256, secret).compact();
}
}
“`
StatelessAuthenticationFilter é o filtro que verifica se existe um token nas requisições.
“`
public class StatelessAuthenticationFilter extends GenericFilterBean {
final TokenAuthenticationService authenticationService;
public StatelessAuthenticationFilter(TokenAuthenticationService authenticationService) {
this.authenticationService = authenticationService;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
Authentication authentication = authenticationService.getAuthentication(httpRequest);
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(request, response);
SecurityContextHolder.getContext().setAuthentication(null);
}
}
“`
No post foi descrito as principais classes para realizar a configuração, no github você pode encontrar o projeto completo.
https://github.com/caioferreira/SpringSecurityJWT
Espero que tenham gostado e até o próximo post.
[/markdown]