Spring Boot + Spring Безопасность + иерархические роли

Я пытаюсь настроить иерархические роли в моем загрузочном приложении Spring без успеха. Я сделал все, что было сказано в разных местах в Интернете. Но ни один из них не смог решить проблему.

Вот код моего класса SecurityConfig. Когда я вхожу в приложение с пользователем с ROLE_ADMIN, он должен иметь возможность извлекать данные из "/users", но в настоящее время я получаю исключение, связанное с доступом. Если у пользователя есть учетные данные ROLE_USER, он работает нормально. Может ли кто-нибудь помочь мне понять, что не удается? Заранее спасибо.

@Configuration
@EnableWebMvcSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private SigpaUserDetailsService userDetailsService;

    @Bean
    public RoleHierarchyImpl roleHierarchy() {
        RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
        roleHierarchy.setHierarchy("ROLE_ADMIN > ROLE_USER");
        return roleHierarchy;
    }

    @Bean
    public RoleHierarchyVoter roleVoter() {     
        return new RoleHierarchyVoter(roleHierarchy());
    }

    @Bean 
    public DefaultWebSecurityExpressionHandler expressionHandler(){
        DefaultWebSecurityExpressionHandler expressionHandler = new DefaultWebSecurityExpressionHandler();
        expressionHandler.setRoleHierarchy(roleHierarchy());
        return expressionHandler;
    }

    @Bean
    @SuppressWarnings(value = { "rawtypes" })
    public AffirmativeBased accessDecisionManager() {       
        List<AccessDecisionVoter> decisionVoters = new ArrayList<AccessDecisionVoter>();
        WebExpressionVoter webExpressionVoter = new WebExpressionVoter();
        webExpressionVoter.setExpressionHandler(expressionHandler());
        decisionVoters.add(webExpressionVoter);
        decisionVoters.add(roleVoter());
        return new AffirmativeBased(decisionVoters);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
            .accessDecisionManager(accessDecisionManager())
            .expressionHandler(expressionHandler())
            .antMatchers("/users/**")
                .access("hasRole('ROLE_USER')")
            .anyRequest().authenticated();
        http
            .formLogin()
                .loginPage("/login").permitAll()
                .and()
            .logout()
                .permitAll();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder registry)
            throws Exception {
        registry.userDetailsService(userDetailsService);
    }
}

Обновление: Вот код, обновленный с вашим предложением, но все еще не работает.

Ответ 1

Я только что прошел через эти настройки, так что, безусловно, ты будешь работать сейчас. Вот сделка:

Вы внесли эту аннотацию @EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true), но не указали какой-либо код для использования Pre/Post Authorize/Filter, поэтому я не знаю, действительно ли вам это нужно.

  • Если вам не нужна безопасность/фильтрация уровня класса/метода, все, что вам нужно сделать, это:

    @Bean
    public RoleHierarchyImpl roleHierarchy() {
        RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
        roleHierarchy.setHierarchy("ROLE_ADMIN > ROLE_USER");
        return roleHierarchy;
    }
    

и

        private SecurityExpressionHandler<FilterInvocation> webExpressionHandler() {
            DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler();
            defaultWebSecurityExpressionHandler.setRoleHierarchy(roleHierarchy());
            return defaultWebSecurityExpressionHandler;
        }

http
        .authorizeRequests()
        .expressionHandler(webExpressionHandler())

Вам не нужно переопределять свой собственный accessDecisionManager, если вам нужно только ввести иерархию ролей.

  1. Если вам также нужна безопасность уровня класса/метода, т.е. используя PreAuthorize, PostAuthorize, PreFilter, PostFilter в ваших методах/классах, то также создайте @Configuration, как это в вашем пути к классам (и удалите аннотацию @EnableGlobalMethodSecurity из вашего класса GlobalMethodSecurityConfig):

    @Configuration
    @EnableGlobalMethodSecurity(prePostEnabled=true)
    public class AnyNameYouLike extends GlobalMethodSecurityConfiguration {
    
    @Resource
    private RoleHierarchy roleHierarchy;
    
    @Override
    protected MethodSecurityExpressionHandler createExpressionHandler() {
        DefaultMethodSecurityExpressionHandler expressionHandler = (DefaultMethodSecurityExpressionHandler) super.createExpressionHandler();
        expressionHandler.setRoleHierarchy(roleHierarchy);
        return expressionHandler;
    }
    

    }

Я бы дал имя GlobalMethodSecurityConfig этому новому классу и изменил ваш текущий класс GlobalMethodSecurityConfig на WebSecurityConfig или что-то, что отразится на настройке безопасности для веб-уровня.

Я определяю RoleHierarchy bean в webSecurityConfig и внедряю/использую его в globalMethodSecurityConfig, но вы можете сделать это любым способом, если вы не создаете 2 beans без необходимости.

Надеюсь, что это поможет.

Ответ 2

Вам нужно установить иерархию роли в элементе голосования веб-выражения. Что-то вроде:

DefaultWebSecurityExpressionHandler expressionHandler = new DefaultWebSecurityExpressionHandler();
expressionHandler.setRoleHierarchy(roleHierarchy);
webExpressionVoter.setExpressionHandler(expressionHandler);

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

http
    .authorizeRequests()
    .expressionHandler(expressionHandler)
    ...

Ответ 3

Вы должны установить иерархию роли в MethodSecurityExpressionHandler:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public static class GlobalMethodSecurityConfig extends GlobalMethodSecurityConfiguration {
    @Autowired
    private RoleHierarchy roleHierarchy;

    @Override
    protected MethodSecurityExpressionHandler createExpressionHandler() {
        final DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
        handler.setRoleHierarchy(this.roleHierarchy);
        return handler;
    }
}

За дополнительной информацией обращайтесь Javadoc для @EnableGlobalMethodSecurity. Особенно отметим: , что EnableGlobalMethodSecurity по-прежнему должны быть включены в класс, расширяющий GlobalMethodSecurityConfiguration, чтобы определить настройки.

Ответ 4

Включить защиту на уровне метода (т.е. @EnableGlobalMethodSecurity (prePostEnabled = true)) вместе с поддержкой Hierarchical-role в WebSecurityConfigurerAdapter.

1. Просто нужно отделить RoleHierarchy от любого другого класса, помеченного @Bean
2. Введите его, используя @Autowired на WebSecurityConfigurerAdapter. Это работает безупречно на моих проектах.

Пожалуйста, посмотрите на мой код.

WeSecurityConfig.class

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private RoleHierarchy roleHierarchy;

    private SecurityExpressionHandler<FilterInvocation>    webExpressionHandler() {
        DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler     = new DefaultWebSecurityExpressionHandler();
        defaultWebSecurityExpressionHandler.setRoleHierarchy(roleHierarchy);
        return defaultWebSecurityExpressionHandler;
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        super.configure(web);
        web.ignoring().antMatchers("/static/**");
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.
            authorizeRequests()
            .expressionHandler(webExpressionHandler())
            .antMatchers("/static/**","/bower_components/**","/").permitAll()
            .antMatchers("/user/login","/user/login?error").anonymous()
            .anyRequest().authenticated()
            .and()
            .formLogin().loginPage("/user/login").passwordParameter("password").usernameParameter("username")
            .defaultSuccessUrl("/")
            .permitAll()
            .and()
            .logout().logoutUrl("/user/logout")
            .logoutSuccessUrl("/user/login?logout")
            .and().csrf();

    }

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(daoAuthenticationProvider());
    }

    public DaoAuthenticationProvider daoAuthenticationProvider(){
        final DaoAuthenticationProvider auth = new DaoAuthenticationProvider();
        auth.setUserDetailsService(userDetailService);
        auth.setPasswordEncoder(passwordEncoder);
        return auth;
    }
}

BeanConfiguration.class

@Configuration
public class BeanConfiguration {

    @Bean
    public RoleHierarchy roleHierarchy() {
        RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
        /* tricks lies here */
        roleHierarchy.setHierarchy("ROLE_SUPREME > ROLE_ADMIN ROLE_ADMIN > ROLE_OPERATOR ROLE_OPERATOR > ROLE_GUEST");
        return roleHierarchy;
    }
}

Надеюсь, это поможет вам.