Solucionado (ver solução)
Solucionado
(ver solução)
3
respostas

Configuração hibrida para o SecurityConfig

Tenho a configuração abaixo que funciona normalmente para uma api rest. Gostaria de adicionar uma tela de login e um painel administrativo com funcionalidade de criação de usuarios e reset de senha nesta api.] como ficara a forma correta do meu security config nesse caso para que possa funcionar tanto como uma rest api e com a parte web? Seria somente adicionar as rotas das paginas e conceder direitos para os usuarios necessários? abaixo o código atual funcionando e minha api rest.

@Configuration
@EnableWebSecurity
public class SecurityConfigurations {

    @Autowired
    private SecurityFilter securityFilter;
    
    
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        
        
        
        return http.csrf(csr -> csr.disable())
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authorizeHttpRequests(authorize -> { authorize
                                                        .requestMatchers(HttpMethod.POST,"/auth").permitAll()	
                                                        .requestMatchers(HttpMethod.GET,"/auth/login").permitAll()		/* esse seria para o meu login*/
                                                        .requestMatchers(HttpMethod.GET,"/empresa1/v1/**").hasAnyAuthority(Role.EMPRESA1.name(),Role.ADMIN.name())
                                                        .requestMatchers(HttpMethod.GET,"/empresa2/v1/**").hasAnyAuthority(Role.EMPRESA2.name(),Role.ADMIN.name())	
                                                        .requestMatchers(HttpMethod.POST,"/user/v1/**").hasAuthority(Role.ADMIN.name())
                                                        .anyRequest().authenticated();
                                                        
                                                        
                                                        })
                
                .addFilterBefore(securityFilter, UsernamePasswordAuthenticationFilter.class)
                
                .build();
        
       	} 
    
    @Bean
    public AuthenticationManager authenticationManager (AuthenticationConfiguration configuration) throws Exception {
        return configuration.getAuthenticationManager();
    }
    
    
     	@Bean
        public WebSecurityCustomizer webSecurityCustomizer() {
            return web -> web.ignoring().requestMatchers("/actuator", "/actuator/**","/v3/api-docs/**","/api-docs/**", "/swagger-ui.html", "/swagger-ui/**");
        }
    
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        
        return  new BCryptPasswordEncoder();
    }
    
    
    
    

    
}
3 respostas

Continuação dos objetos SecurityFilter

@Component
@Slf4j
public class SecurityFilter extends OncePerRequestFilter {

    
    @Autowired
    private TokenConfigService tokenService;
    @Autowired
    private UserRepository usuarioRepository;
    
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {			

      try {
            var tokenJwt = recuperarToken(request);

            if (tokenJwt != null) {

                var subject = tokenService.getSubject(tokenJwt);
                var usuario = usuarioRepository.findByLogin(subject);
                var authentication = new UsernamePasswordAuthenticationToken(usuario, null, usuario.getAuthorities());

                SecurityContextHolder.getContext().setAuthentication(authentication);
            }

            filterChain.doFilter(request, response);
            
              }
     catch(InvalidJwtException ex) {
          
          response.setStatus(HttpStatus.FORBIDDEN.value()); 
          response.setContentType("application/json");    	     	
          response.getWriter().write("{\"error\": \""+ex.getMessage()+"\"}");
          response.getWriter().flush();
         
      }
        
    }

    private String recuperarToken(HttpServletRequest request) {
        
        var authorizationHeader = request.getHeader("Authorization");
        
        if(authorizationHeader != null) {
            return authorizationHeader.replace("Bearer ", "");
        }
        
        return null;
        
    }
    

Token Service

@Service
@Slf4j
public class TokenConfigService {
    
    
    private static final String ISSUER = "my-api";
    @Value("${my-api.security.token.secret}")
    private String secret;
    @Autowired
    private UtilService utilService;
    
    
    public AuthenticateResponseDto gerarTokenService(UserModel usuario) {

        return decodificarTokenJwtService(gerarToken(usuario));
    }
    
    
    public String getSubject(String tokenJWT) {

        try {

            var algoritmo = Algorithm.HMAC256(secret);
            return JWT.require(algoritmo)
                      .withIssuer(ISSUER)
                      .build()
                      .verify(tokenJWT)
                      .getSubject();

        } catch (JWTVerificationException jwtEx) {
            log.error("JWTVerificationException -> Token JWT inválido ou expirado!",jwtEx.getMessage());
            
            throw new InvalidJwtException("Token JWT inválido ou expirado!");
            

        }
        
        


    }
    

    
    
    private String gerarToken(UserModel usuario) {
        
        try {
            var algoritmo = Algorithm.HMAC256(secret);
            
          return JWT.create()
                .withIssuer(ISSUER)
                .withSubject(usuario.getLogin())		        
                .withExpiresAt(dataExpiracao())
                .withClaim("empresa", usuario.getEmpresa())		  
                .withClaim("date", utilService.retornarLocalDateTimeNowString())		       
                .sign(algoritmo);
        } catch (JWTCreationException exception){
            log.error("JWTCreationException -> erro ao gerar token jwt",exception.getMessage());
           throw new RuntimeException("erro ao gerar token jwt",exception);
        }
        
    }
    
  private AuthenticateResponseDto decodificarTokenJwtService(String tokenJWT) {
        
        try {
            var algoritmo = Algorithm.HMAC256(secret);	
            var authenticateResponseDto = new AuthenticateResponseDto();
            
            DecodedJWT jwt = JWT.require(algoritmo)
                                .build()
                                .verify(tokenJWT);
            
            Map<String, Claim> claims = jwt.getClaims();
            
            if (claims.containsKey("sub")) {
                authenticateResponseDto.setUsuario(claims.get("sub").asString());

            }	
            
            if (claims.containsKey("exp")) {
                authenticateResponseDto.setExpiresIn(claims.get("exp").asLong());

            }	
            
            if (claims.containsKey("empresa")) {
                authenticateResponseDto.setEmpresa(claims.get("empresa").asString());

            }	
            
            if (claims.containsKey("date")) {
                authenticateResponseDto.setDate(claims.get("date").asString());

            }
            
            authenticateResponseDto.setAcessToken(tokenJWT);
            
            
            return authenticateResponseDto;
            
            
        }catch (JWTVerificationException jwtEx) {
            log.error("JWTVerificationException -> Erro ao tentar decodificar o token!",jwtEx.getMessage());
             throw new InvalidJwtException("Erro ao tentar decodificar o token!");
        }
        
        catch (Exception ex) {
            log.error("Exception -> Erro ao tentar decodificar o token!",ex.getMessage());
             throw new InvalidJwtException(" Erro ao tentar decodificar o token! "+ ex.getMessage());
        }
        
    }
    
    
    
    private Instant dataExpiracao() {
                
        return  LocalDateTime.now().plusMinutes(15).toInstant(ZoneOffset.of("-03:00"));
    }
    
solução!

Oi Marcos! Tudo bem?

Como APIs REST são stateless e aplicações web utilizam o recurso de criação de sessão, não é uma boa abordagem ter as duas coisas de maneira híbrida. Você tem duas opções pra esse painel administrativo:

  1. Deixar as rotas mapeadas no backend, e deixar que o frontend consuma essas rotas e tenha o painel administrativo
  2. Criar uma outra aplicação web que compartilhe o mesmo banco de dados com sua API.

Espero ter ajudado! Abraços e bons estudos!

Obrigado!