java - Spring Boot 开启 @GlobalMethodSecurity 时出现异常

标签 java spring-boot spring-security

我正在尝试在 Spring Boot 中启用 @GlobalMethodSecurity 以使用 CustomPermissionEvaluator。 因此,我添加了相应的 MethodSecurityExpressionHandler bean 并实现 CustomPermissionEvaluator。请参阅下面的来源。

问题。 当我尝试在 WebSecurityConfigurerAdapter 中添加 @EnableGlobalMethodSecurity 时,出现以下错误:

Failed to instantiate [org.springframework.web.servlet.HandlerMapping]: Factory method 'resourceHandlerMapping' threw exception; nested exception is java.lang.IllegalStateException: No ServletContext set*.

我搜索了解决方法,并找到了将 MethodSecurityExpressionHandler 移动到另一个 @Configuration 类中的建议。但这是正确的决定吗?我认为这是一个错误,已于 2016 年成功修复。 Github link

来源

WebSecurityConfigurerAdapter

@Configuration
@EnableWebSecurity //Turns on Spring's web security component
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    UserService userService;
    @Autowired
    UserDetailsServiceImpl userDetailsService;
    @Autowired
    private DataSource dataSource;
    @Autowired
    private CustomPermissionEvaluator permissionEvaluator;

    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
    //Password encoder settings to avoid storing plain passwords in DB
        return new BCryptPasswordEncoder();
    }

    @Bean
    public PersistentTokenRepository persistentTokenRepository() {
    //Choose where to save user session: DB or memory (in our case: DB)
        JdbcTokenRepositoryImpl db = new JdbcTokenRepositoryImplCust();
        db.setDataSource(dataSource);
        return db;
    }

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
    //Settings service to find User in DB + password
        auth
            .userDetailsService(userDetailsService)
            .passwordEncoder(new BCryptPasswordEncoder());
    }

    /* !Using method security here */
    @Bean
    public MethodSecurityExpressionHandler methodSecurityExpressionHandler() {
        DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
        handler.setPermissionEvaluator(permissionEvaluator);
        return handler;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
    //Main method for Spring's web security
        http
            // URL checks
            .authorizeRequests()
                .antMatchers("/resource/**").permitAll() //All users can see it
                .antMatchers("/users/**","/roles/**").hasAuthority("ADMIN") //Only users with Admin Role can see it
                .anyRequest().authenticated() //All another URL requires authorization
                .and()
            // Login forms
            .formLogin()
                .loginPage("/login")
                .defaultSuccessUrl("/main")//Redirect page if
                .failureUrl("/login?error")
                .usernameParameter("username") //parse from POST
                .passwordParameter("password") //parse from POST
                .permitAll()
                .and()
            // Actions when logoff
            .logout()
                .permitAll() 
                .logoutUrl("/logout")
                .logoutSuccessUrl("/login?logout")
                .invalidateHttpSession(true)
                .and()
            // Exceptions
            .exceptionHandling()
                .accessDeniedPage("/denied")
                .and()
            //Cookies
            .rememberMe()
                .tokenRepository(this.persistentTokenRepository()) //Choose sessions repository
                .rememberMeParameter("remember-me")
                .tokenValiditySeconds(1 * 24 * 60 * 60) //24H
            ;
    }
}

权限评估器类

@Component
public class CustomPermissionEvaluator implements PermissionEvaluator {
    @Override
    public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
        System.out.println("Permission eveluator: called");
        boolean permissionGranted = false;
        if (authentication != null && targetDomainObject instanceof String) {
            //Checking if user have such role by evaluating Role object
            Collection<Role> roles = (Collection<Role>) authentication.getAuthorities();
            System.out.println("Permission eveluator: checking rights for: " + authentication.getPrincipal());

            for (Role role : roles) {
                if (role.getName().equals(targetDomainObject) && role.getPermission().getName() == permission) {
                    permissionGranted = true;
                    System.out.println("Permission eveluator: permission granted");
                }
            }
        }
        return permissionGranted;
    }

    @Override
    public boolean hasPermission(Authentication authentication, Serializable serializable, String targetType,
                                 Object permission) {
        return false;
    }
}

错误堆栈

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2020-03-25 16:18:45.206 ERROR 14044 --- [           main] o.s.boot.SpringApplication               : Application run failed

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'resourceHandlerMapping' defined in class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.servlet.HandlerMapping]: Factory method 'resourceHandlerMapping' threw exception; nested exception is java.lang.IllegalStateException: No ServletContext set
    at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:627) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:456) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1305) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1144) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:849) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:877) ~[spring-context-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549) ~[spring-context-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:142) ~[spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775) [spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) [spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:316) [spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1260) [spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1248) [spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE]
    at com.platform.Application.main(Application.java:24) [classes/:na]
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.servlet.HandlerMapping]: Factory method 'resourceHandlerMapping' threw exception; nested exception is java.lang.IllegalStateException: No ServletContext set
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:622) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    ... 19 common frames omitted
Caused by: java.lang.IllegalStateException: No ServletContext set
    at org.springframework.util.Assert.state(Assert.java:73) ~[spring-core-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport.resourceHandlerMapping(WebMvcConfigurationSupport.java:486) ~[spring-webmvc-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration$$EnhancerBySpringCGLIB$$ff99df16.CGLIB$resourceHandlerMapping$34(<generated>) ~[spring-boot-autoconfigure-2.1.3.RELEASE.jar:2.1.3.RELEASE]
    at org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration$$EnhancerBySpringCGLIB$$ff99df16$$FastClassBySpringCGLIB$$eed8a8e5.invoke(<generated>) ~[spring-boot-autoconfigure-2.1.3.RELEASE.jar:2.1.3.RELEASE]
    at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244) ~[spring-core-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:363) ~[spring-context-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    at org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration$$EnhancerBySpringCGLIB$$ff99df16.resourceHandlerMapping(<generated>) ~[spring-boot-autoconfigure-2.1.3.RELEASE.jar:2.1.3.RELEASE]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_201]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_201]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_201]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_201]
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    ... 20 common frames omitted


Process finished with exit code 1

最佳答案

按照 Rob Winch 的建议,我将 @EnableGlobalMethodSecurity 移至单独的配置中,因此现在可以使用了。

@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class CustomMethodSecurityConfig extends GlobalMethodSecurityConfiguration {

    @Autowired
    CustomPermissionEvaluator permissionEvaluator;

    @Bean
    public MethodSecurityExpressionHandler methodSecurityExpressionHandler() {
        DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
        handler.setPermissionEvaluator(permissionEvaluator);
        return handler;
    }

}

关于java - Spring Boot 开启 @GlobalMethodSecurity 时出现异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60851042/

相关文章:

java - 在到达底部后自动滚动到回收器 View 顶部的最佳方法是什么?

java - Guice 不加载绑定(bind)

java - 如何实现两个不可旋转矩形之间的碰撞响应

java - 错误7405 --- [主要] o.s.boot.SpringApplication : Application startup failed

java - 如何在 Spring Security 中从 LDAP 获取额外的用户属性?

java - 堆栈计算器: Trouble evaluating postfix expression because of casting issue

java - kubernetes:无法拉取图像

java - Spring Boot 测试 MalformedURLException : unknown protocol: classpath

java - 在 Spring Boot 中使用相对重定向失败

spring - 无法更改 Spring 安全 SAML SSO 中的默认 "reply Url"(断言消费者服务位置)