2
respostas

Dúvida no SecurityFilterChain do Spring Boot - Versão 3.1.4

Boa noite, Seguindo as mudanças na versão 3.1 apresentadas no curso, percebi que ocorre um erro de validação quando se usa "roles" para usuários diferentes. Testei a exclusão com usuários com roles "ROLE_ADMIN" e "ROLE_USER", contudo os dois conseguiram realizar a exclusão. Existem alguma forma de tratar para que não ocorra.

@Table(name = "users")
@Entity(name = "Usuario")
@Getter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(of = "id")
public class Usuario implements UserDetails{

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String login;
    private String password;
    private boolean enabled;

    @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinTable(
            name = "users_roles",
            joinColumns = @JoinColumn(name = "user_id"),
            inverseJoinColumns = @JoinColumn(name = "role_id")
    )
    private Set<Role> roles = new HashSet<>();

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {

       // return List.of(new SimpleGrantedAuthority("ROLE_USER");

        List<SimpleGrantedAuthority> authorities = new ArrayList<>();
        for (Role role : getRoles()) {
            authorities.add(new SimpleGrantedAuthority(role.getName().trim()));
        }
        return authorities;
    }

    @Override
    public String getPassword() {
        return password;
    }

    @Override
    public String getUsername() {
        return login;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return enabled;
    }
}

==========

public interface UsuarioRepository extends JpaRepository <Usuario, Long> {

    @Query("SELECT u FROM Usuario u JOIN FETCH u.roles where u.login = :login ")
    Usuario findByUserNameFetchRoles(@Param("login") String login);

}

======

@Configuration
@EnableWebSecurity
public class SecurityConfigurations {

    private static final String SIGN_UP_URL = "/login";

    @Autowired
    private SecurityFilter securityFilter;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity
                .csrf(csrf -> csrf.disable())
                .sessionManagement(sess -> sess.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authorizeHttpRequests(req -> {
                    // Não cheque essas requisições
                    req.requestMatchers(HttpMethod.POST, SIGN_UP_URL).permitAll();
                    req.requestMatchers(HttpMethod.DELETE,  "/medicos").hasAuthority("ROLE_ADMIN");
                    req.requestMatchers(HttpMethod.DELETE,  "/pacientes").hasAuthority("ROLE_ADMIN");
                    // Qualquer outra requisição deve ser checada
                   req.anyRequest().authenticated();
                })
                .addFilterBefore(securityFilter, UsernamePasswordAuthenticationFilter.class)
                .build();
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    ======
    
@Component
public class SecurityFilter extends OncePerRequestFilter {

    private static final String TOKEN_PREFIX = "Bearer ";
    private  static final String HEADER_STRING = "Authorization";

    @Autowired
    private TokenService tokenService;

    @Autowired
    private UsuarioRepository usuarioRepository;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        var tokenJWT = recuperarToken(request);

        if(tokenJWT != null){
            var subject = tokenService.getSubject(tokenJWT);
            var usuario = usuarioRepository.findByUserNameFetchRoles(subject);
            var authentication = new UsernamePasswordAuthenticationToken(usuario, null, usuario.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }
        filterChain.doFilter(request, response);
    }

    private String recuperarToken(HttpServletRequest request) {
        var authorizationHeader = request.getHeader(HEADER_STRING);
        if(authorizationHeader != null){
            return authorizationHeader.replace(TOKEN_PREFIX, "").trim();
        }
        return null;
    }
}
2 respostas

Olá, Joelson! Tudo bem com você?

Parece que o problema está na forma como você está verificando as autoridades no método securityFilterChain(HttpSecurity httpSecurity).

A função hasAuthority("ROLE_ADMIN") verifica se o usuário tem a autoridade "ROLE_ADMIN", mas não restringe o acesso de usuários com outras autoridades.

Para restringir o acesso apenas aos usuários com a autoridade "ROLE_ADMIN", você pode usar a função hasRole("ADMIN") em vez de hasAuthority("ROLE_ADMIN").

Aqui está como ficaria o seu código:

@Configuration
@EnableWebSecurity
public class SecurityConfigurations {

    private static final String SIGN_UP_URL = "/login";

    @Autowired
    private SecurityFilter securityFilter;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity
                .csrf(csrf -> csrf.disable())
                .sessionManagement(sess -> sess.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authorizeHttpRequests(req -> {
                    // Não cheque essas requisições
                    req.requestMatchers(HttpMethod.POST, SIGN_UP_URL).permitAll();
                    req.requestMatchers(HttpMethod.DELETE,  "/medicos").hasRole("ADMIN");
                    req.requestMatchers(HttpMethod.DELETE,  "/pacientes").hasRole("ADMIN");
                    // Qualquer outra requisição deve ser checada
                   req.anyRequest().authenticated();
                })
                .addFilterBefore(securityFilter, UsernamePasswordAuthenticationFilter.class)
                .build();
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

Desta forma, apenas os usuários com o role "ADMIN" poderão realizar operações de DELETE nos endpoints "/medicos" e "/pacientes".

Espero ter ajudado e bons estudos!

Caso este post tenha lhe ajudado, por favor, marcar como solucionado ✓.

@Armano Barros Alves Junior, fiz as alterações que foi passada acima e não funcionou, os dois usuário continuam a executar a deleção lógica.

Insira aqui a descrição dessa imagem para ajudar na acessibilidade![](Insira aqui a descrição dessa imagem para ajudar na acessibilidade )

 @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity
                .csrf(csrf -> csrf.disable())
                .sessionManagement(sess -> sess.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authorizeHttpRequests(req -> {
                    // Não cheque essas requisições
                    req.requestMatchers(HttpMethod.POST, SIGN_UP_URL).permitAll();
                    req.requestMatchers(HttpMethod.DELETE,  "/medicos").hasRole("ADMIN");
                    req.requestMatchers(HttpMethod.DELETE,  "/pacientes").hasRole("ADMIN");
                    // Qualquer outra requisição deve ser checada
                   req.anyRequest().authenticated();
                })
                .addFilterBefore(securityFilter, UsernamePasswordAuthenticationFilter.class)
                .build();
    }