java - Spring Boot 3.0/Security 6.0 迁移 - SecurityFilterChain 中的 "EL1057E: No bean resolver registered in the context to resolve access to bean..."

标签 java spring spring-boot spring-security

我正在尝试将我的应用程序升级到 Spring Boot 3.0。 我有一些 JUnit 测试,主要用于确保访问控制逻辑按预期工作。它们使用 SpEL 表达式和 @Bean 来帮助授权。

示例测试:


@ActiveProfiles("test")
@AutoConfigureMockMvc
@SpringBootTest
public class EmployeeControllerTest {

    private static final String TEST_EMP_ID = "10000";
    @Autowired
    private MockMvc mvc;

    @MockBean
    HRCoreService hrCore;

    
    @MockBean
    private EmpFilter empFilter;
    
    @MockBean
    private AWSHelper awsHelper;
    
    @Autowired
    private ObjectMapper mapper;

    
    @BeforeEach
    public void setup()
    {
    
        // The goal here is to test roles and validation, not the empFilter logic. Force it to return true.
        given(this.empFilter.checkUserId(any(Authentication.class), any(String.class)))
        .willReturn(true);
        given(this.empFilter.checkAccessByLoc(any(Authentication.class), any(String.class))).willReturn(true);
    }
    
// Code omitted for brevity
    @Test
    @WithMockUser(username = "admin", authorities = { RoleConstants.HR.VIEW_EMP })
    public void testGetEmployee() throws Exception {
        var dto = new EmployeeDTO();
        dto.setEmail("email");
        var jString = mapper.writeValueAsString(dto);

        given(this.hrCore.getEmployee(TEST_EMP_ID)).willReturn(dto);

        this.mvc.perform(get("/employees/" + TEST_EMP_ID)).andExpect(status().is2xxSuccessful())
                .andExpect(content().json(jString));
    }

EmpFilter的定义是

@Component
public class EmpFilter {
    
    private EmployeeRepo empRepo;
    
    @Autowired
    public EmpFilter(EmployeeRepo empRepo) {
        this.empRepo = empRepo;
    }

    public boolean checkUserId(Authentication authentication, String id) {
        return id.equals(authentication.getName());
    }
    
    
    public boolean checkAccessByLoc(Authentication authentication, String id) {
        var props = JwtUtils.extractProperties(authentication);
        return this.empRepo.empWorksAtLoc(id, props);
    }
}

来自上述端点的 SecurityConfig 的片段:


@EnableWebSecurity
@Configuration
@EnableMethodSecurity
public class WebSecurityConfig {

    private static final Logger LOGGER = LoggerFactory.getLogger(WebSecurityConfig.class);


    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests(authorize -> {
            try {
                        .requestMatchers(HttpMethod.GET, "/employees/{id}")
                        .access(
                                new WebExpressionAuthorizationManager(
                                SecurityUtils.buildEmpAccessString(List.of(RoleConstants.HR.VIEW_EMP), 
                                        List.of(RoleConstants.HR.VIEW_EMP, RoleConstants.ESS.MNG_SELF, RoleConstants.INS.APPLICANT))))

// Code omitted for brevity

}

运行测试时,出现以下错误:

java.lang.IllegalArgumentException: Failed to evaluate expression '(hasAnyAuthority('SCOPE_hr-view-employee-info') and @empFilter.checkAccessByLoc(authentication, #id) ) or (hasAnyAuthority('SCOPE_hr-view-employee-info','SCOPE_ess-manage-self','SCOPE_ins-applicant') and @empFilter.checkUserId(authentication, #id) )'
    at org.springframework.security.access.expression.ExpressionUtils.evaluateAsBoolean(ExpressionUtils.java:33)
    at org.springframework.security.web.access.expression.WebExpressionAuthorizationManager.check(WebExpressionAuthorizationManager.java:76)
    at org.springframework.security.web.access.expression.WebExpressionAuthorizationManager.check(WebExpressionAuthorizationManager.java:39)
    at org.springframework.security.web.access.intercept.RequestMatcherDelegatingAuthorizationManager.check(RequestMatcherDelegatingAuthorizationManager.java:82)
    at org.springframework.security.web.access.intercept.RequestMatcherDelegatingAuthorizationManager.check(RequestMatcherDelegatingAuthorizationManager.java:45)
    at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:95)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:126)
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:120)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)
    at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)
    at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:179)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)
    at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)
    at org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter.doFilterInternal(BearerTokenAuthenticationFilter.java:128)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107)
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)
    at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)
    at org.springframework.security.web.context.SecurityContextHolderFilter.doFilterInternal(SecurityContextHolderFilter.java:69)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)
    at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:351)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267)
    at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:132)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
    at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:132)
    at com.midamcorp.hr.config.SimpleCORSFilter.doFilter(SimpleCORSFilter.java:57)
    at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:132)
    at org.springframework.test.web.servlet.MockMvc.perform(MockMvc.java:201)
    at com.midamcorp.hr.controller.EmployeeControllerTest.testGetEmployee(EmployeeControllerTest.java:235)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:727)
    at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
    at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:156)
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:147)
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:86)
    at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103)
    at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
    at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:92)
    at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:86)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:217)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:213)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:138)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:95)
    at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:91)
    at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:60)
    at org.eclipse.jdt.internal.junit5.runner.JUnit5TestReference.run(JUnit5TestReference.java:98)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:40)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:529)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:756)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:452)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:210)
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1057E: No bean resolver registered in the context to resolve access to bean 'empFilter'
    at org.springframework.expression.spel.ast.BeanReference.getValueInternal(BeanReference.java:51)
    at org.springframework.expression.spel.ast.CompoundExpression.getValueRef(CompoundExpression.java:55)
    at org.springframework.expression.spel.ast.CompoundExpression.getValueInternal(CompoundExpression.java:91)
    at org.springframework.expression.spel.ast.SpelNodeImpl.getValue(SpelNodeImpl.java:188)
    at org.springframework.expression.spel.ast.OpAnd.getBooleanValue(OpAnd.java:57)
    at org.springframework.expression.spel.ast.OpAnd.getValueInternal(OpAnd.java:52)
    at org.springframework.expression.spel.ast.SpelNodeImpl.getValue(SpelNodeImpl.java:188)
    at org.springframework.expression.spel.ast.OpOr.getBooleanValue(OpOr.java:56)
    at org.springframework.expression.spel.ast.OpOr.getValueInternal(OpOr.java:47)
    at org.springframework.expression.spel.ast.OpOr.getValueInternal(OpOr.java:37)
    at org.springframework.expression.spel.ast.SpelNodeImpl.getTypedValue(SpelNodeImpl.java:117)
    at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:309)
    at org.springframework.security.access.expression.ExpressionUtils.evaluateAsBoolean(ExpressionUtils.java:30)
    ... 115 more

根据输出,该表达式看起来像是一个正确的 SpEL 表达式(抱歉,我知道它有点长,可以进行一些清理),并且该问题似乎与 @empFilter 未正确注册。

这在迁移之前运行良好。我已经回顾了Spring的迁移指南和relevant documentation对于 Spring Security 6.0,但没有发现任何可以解释我遇到的问题的信息。

任何指示将不胜感激。

最佳答案

Bean empFilter 已在测试的 ApplicationContext 中注册。在 Spring Security 6 中,WebExpressionAuthorizationManager 使用 DefaultHttpSecurityExpressionHandler 的实例作为其 SpEL 表达式处理程序。 DefaultHttpSecurityExpressionHandler 除非显式配置,否则无法访问应用程序上下文。

DefaultHttpSecurityExpressionHandler 可以通过如下方式了解应用程序上下文:

@Autowired
private ApplicationContext applicationContext;

private WebExpressionAuthorizationManager getWebExpressionAuthorizationManager(final String expression) {
    final var expressionHandler = new DefaultHttpSecurityExpressionHandler();
    expressionHandler.setApplicationContext(applicationContext);
    final var authorizationManager = new WebExpressionAuthorizationManager(expression);
    authorizationManager.setExpressionHandler(expressionHandler);
    return authorizationManager;
}

安全配置的相关部分将变为:

    .requestMatchers(HttpMethod.GET, "/employees/{id}")
    .access(getWebExpressionAuthorizationManager(
        SecurityUtils.buildEmpAccessString( // as in the question

看起来这是提供应用程序上下文的唯一方法。最近开通了issue已提交建议更容易设置所使用的表达式处理程序。

关于java - Spring Boot 3.0/Security 6.0 迁移 - SecurityFilterChain 中的 "EL1057E: No bean resolver registered in the context to resolve access to bean...",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74710493/

相关文章:

mysql - Spring Boot 2.0/Hibernate MySql 更改表语法错误

java - 创建注释来执行常见操作并返回结果

spring - 为什么不能使用注释在ItemProcessor中找到jobParameters?

java - 动态 <ui :include> doesn't work

java - MySQL : EOFException: Can not read response from server. 预计读取 4 个字节,在连接意外丢失之前读取了 0 个字节

rest - 尝试在外部 tomcat 9 上使用 rest Controller 运行 spring boot war 文件

java - Kotlin 模块源未打包在 aar 中

java - 当从 java VM 调用设置 cookie 的 REST 服务时,cookie 是如何工作的?

java - 如何从 ftp 服务器获取文件的路径?

java - Spring boot 1.3.4和hateoas 0.18.0依赖问题