PreAuthorize не работает на контроллере

Я пытаюсь определить правила доступа на уровне метода, но это не работает.

Конфигурация безопасности

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().
                withUser("user").password("user").roles("USER").and().
                withUser("admin").password("admin").roles("ADMIN");
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers("/v2/**").authenticated()
                .and()
                .httpBasic()
                .realmName("Secure api")
                .and()
                .csrf()
                .disable();
    }
}

ПримерКонтроллер

@EnableAutoConfiguration
@RestController
@RequestMapping({"/v2/"})
public class ExampleController {
    @PreAuthorize("hasAuthority('ROLE_ADMIN')")
    @RequestMapping(value = "/home", method = RequestMethod.GET)
    String home() {
        return "Hello World";
    }
}

Всякий раз, когда я пытаюсь получить доступ к /v2/home с помощью user:user, он выполняется нормально, разве он не должен выдавать мне ошибку «Отказано в доступе» из-за того, что «пользователь» не имеет ROLE_ADMIN?

На самом деле я думаю о том, чтобы отказаться от правил доступа на уровне метода и придерживаться правил http() ant, но я должен знать, почему это не работает для меня.


person prettyvoid    schedule 07.09.2015    source источник


Ответы (10)


Распространенная проблема с использованием аннотаций PrePost на контроллерах заключается в том, что безопасность метода Spring основана на Spring AOP, который по умолчанию реализуется с помощью прокси-серверов JDK.

Это означает, что он отлично работает на уровне службы, который внедряется на уровне контроллера в качестве интерфейсов, но игнорируется на уровне контроллера, поскольку контроллер обычно не реализует интерфейсы.

Далее только мое мнение:

  • предпочтительный способ: переместите аннотацию до поста на сервисный слой
  • если вы не можете (или не хотите), попробуйте реализовать в контроллере интерфейс, содержащий все аннотированные методы.
  • в крайнем случае используйте proxy-target-class=true
person Serge Ballesta    schedule 07.09.2015
comment
Спасибо за ответ. Я пробовал использовать аннотацию на интерфейсе CrudRepository, и все работало нормально. Иметь интерфейс для каждого контроллера, на мой взгляд, глупо, поскольку интерфейс на самом деле не нужен. proxy-target-class=true не имело значения, аннотации по-прежнему не работают на контроллерах, однако это вызвало сбой (при значении true) при наличии аннотации внутри интерфейса репозитория (невозможно создать подкласс com.sun.proxy). В любом случае, я думаю, что буду придерживаться правил в конфигурации безопасности и, возможно, использовать PreAuth в некоторых из моих репозиториев. - person prettyvoid; 08.09.2015
comment
Еще одна вещь, которую я заметил, я видел примеры, когда @PreAuthorize использовался в class вместо interface, и это должно было работать. Интересно, почему это работает для них, но не для меня. - person prettyvoid; 08.09.2015
comment
Включить Aop в контексте приложения ‹aop:aspectj-autoproxy /› - person Mradul Pandey; 02.06.2016
comment
Добавление интерфейса исправило это для меня, но в другом проекте нет интерфейсов, а методы контроллера защищены. Должна быть какая-то другая конфигурация, которую они используют, а я нет. - person Kieveli; 02.06.2016
comment
Я столкнулся с той же проблемой, но с точностью до наоборот. @PreAuthorize нормально работает на уровне контроллера, но не на уровне сервиса. Ты знаешь почему? - person An Nguyen; 14.07.2016
comment
У меня были странные проблемы с использованием @PreAuthorize на контроллере: некоторые приложения работают хорошо, а некоторые нет. - person Dherik; 26.01.2018
comment
это уже изменилось? Поскольку аннотации prePost отлично работают на моих контроллерах без реализации какого-либо интерфейса или разрешения проксирования целевого класса. - person Wecherowski; 29.08.2020

Вы должны добавить @EnableGlobalMethodSecurity(prePostEnabled = true) в свой WebSecurityConfig.

Вы можете найти его здесь: http://www.baeldung.com/spring-security-expressions-basic

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
person Eric Zürcher    schedule 15.02.2018
comment
Также согласно предыдущему комментатору, если поместить pre/post в метод контроллера, который не реализует интерфейс, также добавьте свойство аннотации proxyTargetClass = true в EnableGlobalMethodSecurity - person GameSalutes; 17.07.2018
comment
К сожалению, у меня не работает (см. com/questions/53125388/). - person Stefan Falk; 02.11.2018

У меня была аналогичная проблема, и следующее решило ее:

1) Мне пришлось сделать свой метод общедоступным (т.е. сделать ваш метод home() общедоступным)

2) я должен использовать hasRole вместо hasAuthority

person Horowitzathome    schedule 02.03.2016
comment
У меня была такая же проблема, и обнародование методов решило ее! - person Rudolf Schmidt; 02.05.2016
comment
обнародование методов контроллера решило это для меня. - person Piyush; 04.12.2017
comment
Боже мой! Это было так просто, почему так сложно отлаживать такие вещи на Java?? Спасибо - person Frankie Drake; 19.02.2019
comment
Изменение метода с частного на общедоступный сработало для меня. - person AreUMinee; 10.10.2020

поместите @EnableGlobalMethodSecurity(prePostEnabled = true) в класс MvcConfig (расширяет WebMvcConfigurerAdapter) вместо (расширяет WebSecurityConfigurerAdapter).

Как показано ниже: -

@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MvcConfiguration extends WebMvcConfigurerAdapter {
person stanicmail    schedule 15.07.2019

Есть два разных способа использовать это: один с префиксом, а другой без него. И вы, возможно, измените @PreAuthorize("hasAuthority('ROLE_ADMIN')") на @PreAuthorize("hasAuthority('ADMIN')"), все будет в порядке.

далее @PreAuthorize исходный код.

private String defaultRolePrefix = "ROLE_";
public final boolean hasAuthority(String authority) {
    return hasAnyAuthority(authority);
}

public final boolean hasAnyAuthority(String... authorities) {
    return hasAnyAuthorityName(null, authorities);
}

public final boolean hasRole(String role) {
    return hasAnyRole(role);
}

public final boolean hasAnyRole(String... roles) {
    return hasAnyAuthorityName(defaultRolePrefix, roles);
}
person beautifulcode    schedule 31.08.2017
comment
и этот источник должен быть добавлен, где и как мой контроллер получает к нему доступ? - person valik; 08.01.2018
comment
Проблема ExampleController, только изменил ROLE_ADMIN на ADMIN - person beautifulcode; 27.11.2018
comment
Я попробовал, как вы предложили, он отлично работает, но как правильно реализовать предложение hasRole, например @PreAuthorize("hasRole('ADMIN')") - person Arshad Ali; 15.03.2020

Чтобы заставить его работать на уровне контроллера. Мне пришлось поместить @EnableAspectJAutoProxy в мой класс конфигурации. Пример :

@Configuration
@EnableWebMvc
@EnableAspectJAutoProxy
@ComponentScan(basePackages = { "com.abc.fraud.ts.userservices.web.controller" })
public class WebConfig extends WebMvcConfigurerAdapter{

}
person kripal kashyav    schedule 14.07.2017
comment
мой работал без этого @EnableAspectJAutoProxy, я просто использовал @PreAuthorize(hasAuthority('ADMIN')) - person valik; 08.01.2018
comment
почему это? @kripal kashyav - person valik; 08.01.2018

Методы моего контроллера были закрытыми, они должны быть общедоступными.

person Georgi Peev    schedule 28.10.2019
comment
я пробую это в течение последнего месяца, все правильно, ожидайте этого публичного ... адское обучение. Наконец нашел проблему. спасибо @georgi Peev - person anavaras lamurep; 25.06.2021

Если у вас есть файл контекста xml для ваших bean-компонентов безопасности и отдельный файл для вашего веб-контекста/сервлета, вам также необходимо добавить:

<security:global-method-security pre-post-annotations="enabled"/>

в ваш контекст web-context.xml/servlet. Недостаточно просто добавить его в контекст безопасности xml.

Он не наследуется в дочерних контекстах.

ХТН

person Chris    schedule 26.06.2018
comment
ОП уже сделал это, используя @EnableGlobalMethodSecurity(prePostEnabled = true) - person Krzysztof Skrzynecki; 12.08.2019

ваш метод запроса защищен по умолчанию. Чтобы они могли его отсканировать. Выложи на всеобщее обозрение!!!

person Vo Nhu Khang    schedule 28.12.2020

Я столкнулся с той же проблемой при попытке авторизовать пользователя AD Group, эти шаги сработали для меня.

  1. @EnableGlobalMethodSecurity(prePostEnabled = true) для класса с аннотацией @EnableWebSecurity

  2. Созданы пользовательские (GroupUserDetails) UserDetails, которые будут иметь имя пользователя и полномочия и возвращать экземпляр GroupUserDetails из custome UserDetailsService

  3. Аннотировать метод контроллера с помощью @PreAuthorize(hasAuthority('Group-Name'))

person Minakshi Jha    schedule 14.06.2021