Spring boot JWT Authorization

Fala pessoal! Nesse post mostramos como criar autenticação com Spring security e JWT (Json web token). Na classe securityConfig definimos que o endpoint puducts é púplico e o category só pode ser acessado com autenticação. Como ainda não autorização, vai dá erro de acesso negado mesmo passando o token no cabeçalho da requisição:

Imagem

Da mesma forma que adicionamos o filtro JWTAuthenticationFilter em SecurityConfig, temos que adicionar JWTAuthorizationFilter para que a autorização funcione. Mas antes precisamos criar o filtro JWTAuthorizationFilter

 package com.br.davifelipe.springjwt.security;
import java.io.IOException;
import javax.servlet.FilterChain;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.security.core.userdetails.UserDetails;
 import org.springframework.security.core.userdetails.UserDetailsService;
 import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import com.br.davifelipe.springjwt.config.JWTUtil;
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
    private JWTUtil jwtUtil;
     
     private UserDetailsService userDetailsService;
     
     public JWTAuthorizationFilter(AuthenticationManager authenticationManager, JWTUtil jwtUtil, UserDetailsService userDetailsService) {
         super(authenticationManager);
         this.jwtUtil = jwtUtil;
         this.userDetailsService = userDetailsService;
     }
     
     @Override
     protected void doFilterInternal(HttpServletRequest request,
                                     HttpServletResponse response,
                                     FilterChain chain) throws IOException, ServletException {
         
         String header = request.getHeader("Authorization");
         if (header != null && header.startsWith("Bearer ")) {
             UsernamePasswordAuthenticationToken auth = getAuthentication(header.substring(7));
             if (auth != null) {
                 SecurityContextHolder.getContext().setAuthentication(auth);
             }
         }
         chain.doFilter(request, response);
     }
    private UsernamePasswordAuthenticationToken getAuthentication(String token) {
         if (jwtUtil.isValidToken(token)) {
             String username = jwtUtil.getUsername(token);
             UserDetails user = userDetailsService.loadUserByUsername(username);
             return new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
         }
         return null;
     }
 }

Também será necessário criar novos métodos em JWTUtil para recuperar o usuário a partir do do token e outro para verificar se o token é válido.

 package com.br.davifelipe.springjwt.config;
import java.util.Date;
import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Component;
import io.jsonwebtoken.Claims;
 import io.jsonwebtoken.Jwts;
 import io.jsonwebtoken.SignatureAlgorithm;
@Component
 public class JWTUtil {
    @Value("${auth.jwt-secret}")
     private String jwtSecret;
     
     @Value("${auth.jwt-expiration-miliseg}")
     private Long jwtExpirationMisiseg;
     
     public String genereteToken(String email) {
         return Jwts.builder()
                     .setSubject(email)
                     .setExpiration(new Date(System.currentTimeMillis() + this.jwtExpirationMisiseg))
                     .signWith(SignatureAlgorithm.HS512, this.jwtSecret.getBytes())
                     .compact();
     }
     
     public boolean isValidToken(String token) {
         Claims claims = getClaims(token);
         if (claims != null) {
             String username = claims.getSubject();
             Date expirationDate = claims.getExpiration();
             Date now = new Date(System.currentTimeMillis());
             if (username != null && expirationDate != null && now.before(expirationDate)) {
                 return true;
             }
         }
         return false;
     }
    public String getUsername(String token) {
         Claims claims = getClaims(token);
         if (claims != null) {
             return claims.getSubject();
         }
         return null;
     }
     
     private Claims getClaims(String token) {
         try {
             return Jwts.parser().setSigningKey(jwtSecret.getBytes()).parseClaimsJws(token).getBody();
         }
         catch (Exception e) {
             return null;
         }
     }
 }
 

Agora basta adicionar o filtro JWTAuthorizationFilter em securityConfig

 package com.br.davifelipe.springjwt.config;
import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.core.env.Environment;
 import org.springframework.http.HttpMethod;
 import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
 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.configuration.WebSecurityConfigurerAdapter;
 import org.springframework.security.config.http.SessionCreationPolicy;
 import org.springframework.security.core.userdetails.UserDetailsService;
 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 import org.springframework.web.cors.CorsConfiguration;
 import org.springframework.web.cors.CorsConfigurationSource;
 import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import com.br.davifelipe.springjwt.security.JWTAuthenticationFilter;
 import com.br.davifelipe.springjwt.security.JWTAuthorizationFilter;
@Configuration
 @EnableWebSecurity
 public class SecurityConfig extends WebSecurityConfigurerAdapter {
     
     public static final List<String> PUBLIC_MATCHERS = new ArrayList<String>();
     public static final List<String> PUBLIC_MATCHERS_GET = new ArrayList<String>();
     public static final List<String> PUBLIC_MATCHERS_POST = new ArrayList<String>();
     
     @Autowired
     JWTUtil jwtUtil;
     
     @Autowired
     UserDetailsService userDetailsService;
     
     @Autowired
     private Environment env;
     
     @Value("${auth.public-sing-up-url-enable}")
     private String publicSingUpUrlEnable;
     
     @Override
     protected void configure(HttpSecurity http) throws Exception {
         
         PUBLIC_MATCHERS.add("/h2-console/**");
         PUBLIC_MATCHERS_GET.add("/products/**");
         
         String[] activeProfiles = env.getActiveProfiles();
         
         if (Arrays.asList(activeProfiles).contains("dev")) {
             //disable it only for h2-console on dev envioment
             http.headers().frameOptions().disable();
         }
         
         if("true".equals(this.publicSingUpUrlEnable)) {
             PUBLIC_MATCHERS_POST.add("/auth/sing-up/**");
         }
         
         http.cors().and().csrf().disable();
         http.authorizeRequests()
             .antMatchers(PUBLIC_MATCHERS.toArray(new String[0])).permitAll()
             .antMatchers(HttpMethod.POST, PUBLIC_MATCHERS_POST.toArray(new String[0])).permitAll()
             .antMatchers(HttpMethod.GET, PUBLIC_MATCHERS_GET.toArray(new String[0])).permitAll()
             .anyRequest()
             .authenticated();
         http.addFilter(new JWTAuthenticationFilter(authenticationManager(), jwtUtil));
         http.addFilter(new JWTAuthorizationFilter(authenticationManager(), jwtUtil, userDetailsService));
         http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
     }
     
     @Override
     public void configure(AuthenticationManagerBuilder auth) throws Exception {
         auth.userDetailsService(userDetailsService).passwordEncoder(bcCryptPasswordEncoder());
     }
     
     @Bean
     CorsConfigurationSource configurationSource() {
         CorsConfiguration configuration = new CorsConfiguration().applyPermitDefaultValues();
         configuration.setAllowedMethods(Arrays.asList("POST", "GET", "PUT", "DELETE", "OPTIONS"));
         final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
         source.registerCorsConfiguration("/**", configuration);
         return source;
     }
     
     @Bean
     public BCryptPasswordEncoder bcCryptPasswordEncoder() {
         return new BCryptPasswordEncoder();
     }
 }
 

Funcionou. Retornou 404 por que não existe registro cadastrado, mas não deu mais erro de autorização.

Imagem

Já temos um endpoint para inserir uma nova categoria mas esse endpoint também precisa de autenticação. Então vamos inserir uma nova categoria passando o token

Imagem

E em seguida vamos recuperar a categoria que foi inserida

Imagem

Repositório do projeto: https://github.com/davifelipems/spring-backend-template/tree/jwt_authorization

 

Comentários

 

Quem Sou

Graduado em ADS (Análise e desenvolvimento de sistemas).

Não sou "devoto" de nenhuma linguagem de programação. Procuro aproveitar o melhor de cada uma de acordo com a necessidade do projeto. Prezo por uma arquitetura bem feita, código limpo, puro e simples! 

anuncio atendente