es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Utilizo Spring Security y un token JWT. No es posible aplicar el rol de administrador en sus permisos.

Aquí está la clase de configuración donde distribuyo el acceso según los roles:

@Configuration
@EnableWebSecurity
@AllArgsConstructor
//@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurity extends WebSecurityConfigurerAdapter {
    private final UserDetailServiceImpl userDetailsService;
    private final BCryptPasswordEncoder bCryptPasswordEncoder;

    private static final String[] PUBLIC_URLS = {
        "/v2/api-docs",
        "/swagger-resources/**",
        "/swagger-ui/**",
        "/webjars/**"
    };

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and().csrf().disable().authorizeRequests()

            .antMatchers(HttpMethod.POST, SIGN_UP_URL).permitAll()
            .antMatchers(PUBLIC_URLS).permitAll()
            .antMatchers ("/user/**").hasAnyRole ("USER", "ADMIN")
            .antMatchers("/user/admin/**").hasRole("ADMIN")
            .anyRequest().authenticated()
            .and()
            .addFilter(new JWTAuthenticationFilter(authenticationManager()))
            .addFilter(new JWTAuthorizationFilter(authenticationManager()))
            /* this disables session creation on Spring Security */
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
    }

    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
        return source;
    }
}

Esta es la clase del controlador donde quiero usar el rol de administrador:

@AllArgsConstructor
@Validated
@RestController
@RequestMapping("/user")
@Api(tags = "Users")
public class UserController {
    private final UserService userService;

    @DeleteMapping("/admin/{id}")
    @PreAuthorize("hasRole('ADMIN')")
    public ResponseEntity<void> delete(@PathVariable @Min(1) int id) {
        userService.delete(id);
        return ResponseEntity.ok().build();
    }
}

Si elimina el siguiente código:

.antMatchers ("/user/").hasAnyRole ("USER", "ADMIN")
.antMatchers("/user/admin/").hasRole("ADMIN")

Luego, el usuario también lo eliminará. Pero si deja este emparejamiento de roles y realiza una solicitud desde el administrador, se produce un error al eliminar el usuario.

{
  "timestamp": "2021-12-01T21:30:05.647+00:00",
  "status": 403,
  "error": "Forbidden",
  "message": "",
  "path": "/user/admin/2"
}

Si deja esta anotación @EnableGlobalMethodSecurity(prePostEnabled = true), entonces nuevamente se produce el mismo error y no se puede eliminar nada.

¿Hay alguna sugerencia para resolver este problema?

Tags:  , ,

Answer

  1. Avatar for davy.ai

    Si desea restringir el acceso al endpoint de eliminación solamente al rol ADMIN, puede modificar la configuración de WebSecurity eliminando la siguiente línea:

    .antMatchers("/user/**").hasAnyRole("USER", "ADMIN")
    

    Esta línea permite que tanto los roles USER como ADMIN tengan acceso a cualquier endpoint que comience con “/user/”. Al eliminarla, solo los usuarios con el rol ADMIN podrán acceder al endpoint “/user/admin/{id}”.

    Sin embargo, también debe asegurarse de que el usuario administrador esté autenticado y tenga asignado el rol ADMIN. Para hacerlo, puede modificar JWTAuthenticationFilter para agregar el rol en el token JWT cuando el usuario inicie sesión:

    List<string> roles = user.getAuthorities().stream()
            .map(GrantedAuthority::getAuthority)
            .collect(Collectors.toList());
    
    String token = Jwts.builder()
            .setSubject(user.getUsername())
            .claim("authorities", roles)
            .setIssuedAt(new Date())
            .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
            .signWith(SignatureAlgorithm.HS512, SECRET.getBytes())
            .compact();
    

    Luego, puede modificar JWTAuthorizationFilter para verificar si el usuario tiene el rol ADMIN en el token JWT:

    String user = null;
    String token = request.getHeader(HEADER_STRING);
    if (token != null) {
        Claims claims = Jwts.parser()
                .setSigningKey(SECRET.getBytes())
                .parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
                .getBody();
    
        user = claims.getSubject();
    
        List<><string, string="">> authorities = (List<><string, string="">>) claims.get("authorities");
    
        if (authorities != null) {
            for (Map<string, string=""> authority : authorities) {
                if ("ADMIN".equals(authority.get("authority"))) { // Verificar si el usuario tiene el rol ADMIN
                    return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
                }
            }
        }
    }
    
    return null;
    

    Con estas modificaciones, solo los usuarios autenticados con el rol ADMIN podrán acceder al endpoint de eliminación.</string,></string,></string,>

Comments are closed.