Spring Security возвращает 404 вместо 403 при использовании @PreAuthorize - PullRequest
2 голосов
/ 29 апреля 2019

После нескольких дней борьбы с этим (поиска в СЦ похожих вопросов, проб и ошибок) у меня возникает соблазн отказаться ...

Так что проблема в том, что у меня есть служба REST, основанная наSpring Boot использует Spring Security и JWT для аутентификации.Теперь я хочу защитить некоторые методы, которые будут вызываться только авторизованными людьми, использующими аннотацию @PreAuthorize.Кажется, это работает отчасти потому, что вместо вызова метода Spring возвращает 404. Я бы ожидал 403.

Я прочитал этот SO-вопрос и попробовал ответы, данные там, но он сделалнет помощи.Я переместил @EnableGlobalMethodSecurity(prePostEnabled = true) -Annotation из моей SecurityConfiguration в класс Application, как это было предложено в другом месте, но он не работает.

Моя конфигурация безопасности выглядит следующим образом:

@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();
}

}

Метод контроллера выглядит следующим образом

@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);
}

Я совершенно уверен, что упускаю что-то очень простое, но просто не могу понять, что.

Кстати: еслипользователь, запрашивающий лицензии, имеет роль ADMIN, все работает, как и ожидалось, поэтому проблема не в 404.

Ответы [ 2 ]

2 голосов
/ 30 апреля 2019

Вам необходимо определить exceptionHandling в конфигурации безопасности следующим образом,

@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));
}

Вы можете определить класс AccessDeniedExceptionHandler следующим образом,

public class AccessDeniedExceptionHandler implements AccessDeniedHandler
{
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response,
            AccessDeniedException ex) throws IOException, ServletException {
        response.setStatus(HttpStatus.FORBIDDEN);
    }
}
0 голосов
/ 30 апреля 2019

Я наконец нашел решение, соответствующее моим целям. Я не знаю, является ли это лучшим способом справиться с этим, но просто добавив ExceptionHandler, добились цели. Где-то глубоко внутри цепочки фильтров 403 изменяется на 404, когда такого обработчика нет. Возможно, я должен сделать дамп, чтобы прочитать и понять документацию, но я не нашел ничего, что подсказывало бы вам сделать это. Так что, возможно, я ошибаюсь, решая проблему, подобную этой, но вот код, который сделал свое дело (это действительно базовая реализация, которая должна быть улучшена со временем):

@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);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...