4
votes

Spring Security renvoie 404 au lieu de 403 lors de l'utilisation de @PreAuthorize

Après avoir lutté avec cela pendant quelques jours (en recherchant SO pour des questions similaires, en faisant des essais et des erreurs), je suis tenté d'abandonner ...

Le problème est donc que j'ai un service REST basé sur Spring Boot utilisant Spring Security et JWT pour l'authentification. Maintenant, je veux sécuriser certaines des méthodes pour qu'elles ne soient appelées que par des personnes autorisées en utilisant l'annotation @PreAuthorize . Cela semble fonctionner en partie parce qu'au lieu d'appeler la méthode Spring renvoie 404. J'aurais attendu 403.

J'ai lu ceci SO-question et a essayé les réponses données là-bas, mais cela n'a pas aidé. J'ai déplacé la @EnableGlobalMethodSecurity (prePostEnabled = true) -Annotation de ma SecurityConfiguration à la classe Application comme suggéré ailleurs, mais cela ne fonctionne toujours pas.

Ma configuration de sécurité ressemble à ceci :

@RequestMapping(path = "/licenses", method = RequestMethod.GET)
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<?> getAllLicenses(@RequestParam("after") int pagenumber, @RequestParam("size") int pagesize
        , @RequestParam("searchText") String searchText) {       
    List<LicenseDTO> result = ...
    return new ResponseEntity<Object>(result, HttpStatus.OK);
}

}

La méthode du contrôleur ressemble à ceci

@Configuration
@Profile("production")
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

@Value("${adDomain}")
private String adDomain;

@Value("${adUrl}")
private String adUrl;

@Value("${rootDn}")
private String rootDn;

@Value("${searchFilter}")
private String searchFilter;

private final AuthenticationManagerBuilder auth;

private final SessionRepository sessionRepository;

@Autowired
public SecurityConfiguration(AuthenticationManagerBuilder auth, SessionRepository sessionRepository) {
    this.auth = auth;
    this.sessionRepository = sessionRepository;
}

@Override
public void configure(WebSecurity webSecurity) throws Exception
{
    webSecurity
            .ignoring()
            // All of Spring Security will ignore the requests
            .antMatchers("/static/**", "/api/web/logout")
            .antMatchers(HttpMethod.POST, "/api/web/login");
}

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable() // Using JWT there is no need for CSRF-protection!
            .authorizeRequests()
            .anyRequest().authenticated()
            .and()
            .addFilter(new JwtAuthorizationFilter(authenticationManagerBean(), sessionRepository));
}

@Bean(name = BeanIds.AUTHENTICATION_MANAGER)
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
    ActiveDirectoryLdapAuthenticationProvider adProvider =
            new ActiveDirectoryLdapAuthenticationProvider(adDomain, adUrl, rootDn);
    adProvider.setConvertSubErrorCodesToExceptions(true);
    adProvider.setUseAuthenticationRequestCredentials(true);
    adProvider.setSearchFilter(searchFilter);
    adProvider.setUserDetailsContextMapper(new InetOrgPersonContextMapper());
    auth.authenticationProvider(adProvider);
    return super.authenticationManagerBean();
}

Je suis sûr que je il me manque quelque chose de très simple, mais je n'arrive pas à comprendre quoi.

Au fait: si l'utilisateur qui demande les licences a le rôle ADMIN, tout fonctionne comme prévu, donc le problème n'est pas un vrai 404.


2 commentaires

Quelle est la réponse exacte de Spring Security?


Il n'y a rien dans le corps de la réponse. Juste le statut 404 dans l'en-tête. En faisant plus de débogage, j'ai découvert qu'il y avait le 403 au début. Il est juste changé en 404 plus haut dans la chaîne de filtrage.


3 Réponses :


1
votes

J'ai enfin trouvé une solution adaptée à mes objectifs. Je ne sais pas si c'est la meilleure façon de gérer cela, mais le simple fait d'ajouter un ExceptionHandler a fait l'affaire. Quelque part au fond de la chaîne de filtres, le 403 mute en 404 lorsqu'il n'y a pas de gestionnaire de ce type en place. Peut-être que je dois vider pour lire et comprendre la documentation, mais je n'ai rien trouvé qui suggère que vous deviez le faire. Alors peut-être que je me trompe en résolvant le problème comme ça, mais voici le code qui a fait l'affaire (c'est une implémentation vraiment basique qui devrait être améliorée avec le temps):

@ControllerAdvice
public class MyExceptionHandler {

    @ExceptionHandler(Throwable.class)
    @ResponseBody
    public ResponseEntity<String> handleControllerException(Throwable ex) {
        HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
        if(ex instanceof AccessDeniedException) {
            status = HttpStatus.FORBIDDEN;
        }

        return new ResponseEntity<>(ex.getMessage(), status);
    }
}


1 commentaires

Vous pouvez changer Throwable.class en AccessDeniedException.class et vous n'aurez pas à utiliser instanceof .



3
votes

Vous devez définir la gestion des exceptions lors de la configuration de la sécurité comme suit,

public class AccessDeniedExceptionHandler implements AccessDeniedHandler
{
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response,
            AccessDeniedException ex) throws IOException, ServletException {
        response.setStatus(HttpStatus.FORBIDDEN);
    }
}

Vous pouvez définir la classe AccessDeniedExceptionHandler comme suit,

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable() // Using JWT there is no need for CSRF-protection!
            .authorizeRequests()
            .anyRequest().authenticated()
            .and()
            .exceptionHandling().accessDeniedHandler(new AccessDeniedExceptionHandler())
            .and()
            .addFilter(new JwtAuthorizationFilter(authenticationManagerBean(), sessionRepository));
}


0 commentaires

0
votes

La sécurité de la méthode globale peut être activée à l'aide de l'annotation @EnableGlobalMethodSecurity (prePostEnabled = true) . La combinaison de ceci et de @Preauthorize créera un nouveau proxy pour votre contrôleur et perdra le mappage de requête, ce qui entraînera une exception 404.

Pour gérer cela, vous pouvez utiliser l'annotation @EnableGlobalMethodSecurity (prePostEnabled = true, proxyTargetClass = true) qui se trouve dans votre classe SecurityConfiguration .

A fourni les détails dans une autre réponse également.


0 commentaires