Java Spring Boot 2@WebMvcTest和OAuth2#OAuth2.hasScope控制器导致IllegalArgumentException

Java Spring Boot 2@WebMvcTest和OAuth2#OAuth2.hasScope控制器导致IllegalArgumentException,java,spring-boot,spring-security,spring-test-mvc,Java,Spring Boot,Spring Security,Spring Test Mvc,这是一个spring boot 2.0.6应用程序,使用MVC控制器,使用@PreAuthorize和#oauth2.hasScope保护控制器端点。我使用@WebMvcTest编写了控制器测试。但是,当调用#oauth2.hasScope时,似乎正在发出请求,但没有给出要与之进行比较的值。我怎么了 例外情况 org.springframework.web.util.NestedServletException: Request processing failed; nested excepti

这是一个spring boot 2.0.6应用程序,使用MVC控制器,使用@PreAuthorize和#oauth2.hasScope保护控制器端点。我使用@WebMvcTest编写了控制器测试。但是,当调用
#oauth2.hasScope
时,似乎正在发出请求,但没有给出要与之进行比较的值。我怎么了

例外情况

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.IllegalArgumentException: Failed to evaluate expression '#oauth2.hasScope('foo')'

    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:982)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:866)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:635)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:851)
    at org.springframework.test.web.servlet.TestDispatcherServlet.service(TestDispatcherServlet.java:71)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
    at org.springframework.mock.web.MockFilterChain$ServletFilterProxy.doFilter(MockFilterChain.java:166)
    at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:133)
...
Caused by: java.lang.IllegalArgumentException: Failed to evaluate expression '#oauth2.hasScope('eq.distributor')'
    at org.springframework.security.access.expression.ExpressionUtils.evaluateAsBoolean(ExpressionUtils.java:30)
    at org.springframework.security.access.expression.method.ExpressionBasedPreInvocationAdvice.before(ExpressionBasedPreInvocationAdvice.java:59)
    at org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter.vote(PreInvocationAuthorizationAdviceVoter.java:72)
    ... 72 more
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1011E: Method call: Attempted to call method hasScope(java.lang.String) on null context object
    at org.springframework.expression.spel.ast.MethodReference.throwIfNotNullSafe(MethodReference.java:153)
    at org.springframework.expression.spel.ast.MethodReference.getValueRef(MethodReference.java:82)
    ... 94 more
@Configuration
@EnableAuthorizationServer
@EnableWebSecurity(debug = true)
@EnableGlobalMethodSecurity(prePostEnabled = true)
@SuppressWarnings("static-method")
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Bean
    public static PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }

    @Override
    public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("testclient")
                .secret("password")
                .authorizedGrantTypes("client_credentials")
                .accessTokenValiditySeconds(0)
                .scopes("foo")
                .and()
                .withClient("bar")
                .secret("password")
                .authorizedGrantTypes("client_credentials")
                .accessTokenValiditySeconds(0)
                .scopes("eq.distributor");
    }
}
测试类

@RunWith(SpringRunner.class)
@WebMvcTest
public class MockMvcTestBase {
    @Autowired
    protected WebApplicationContext context;

    private ObjectMapper objectMapper;

    private MockMvc mockMvc;

    @Before
    public void setUp() {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
                .apply(documentationConfiguration(this.restDocumentation)
                        .uris()
                        .withScheme("http")
                        .withHost("development.com")
                        .withPort(80))
                .alwaysDo(document("{method-name}", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint())))
                .apply(springSecurity())
                .build();

        this.objectMapper = new ObjectMapper();
    }


@Test
@WithMockUser(username = "testclient", authorities = "foo")
public void givenValidUser_whenGetOrderDetailsWebEC_thenGetOrder() throws Exception {
    getMockMvc().perform(get("/ok")
            .header("Authorization", authorizationHeader()))
            .andExpect(status().isOk())
            .andExpect(content().string("ok"));
}
}
验证配置

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.IllegalArgumentException: Failed to evaluate expression '#oauth2.hasScope('foo')'

    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:982)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:866)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:635)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:851)
    at org.springframework.test.web.servlet.TestDispatcherServlet.service(TestDispatcherServlet.java:71)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
    at org.springframework.mock.web.MockFilterChain$ServletFilterProxy.doFilter(MockFilterChain.java:166)
    at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:133)
...
Caused by: java.lang.IllegalArgumentException: Failed to evaluate expression '#oauth2.hasScope('eq.distributor')'
    at org.springframework.security.access.expression.ExpressionUtils.evaluateAsBoolean(ExpressionUtils.java:30)
    at org.springframework.security.access.expression.method.ExpressionBasedPreInvocationAdvice.before(ExpressionBasedPreInvocationAdvice.java:59)
    at org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter.vote(PreInvocationAuthorizationAdviceVoter.java:72)
    ... 72 more
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1011E: Method call: Attempted to call method hasScope(java.lang.String) on null context object
    at org.springframework.expression.spel.ast.MethodReference.throwIfNotNullSafe(MethodReference.java:153)
    at org.springframework.expression.spel.ast.MethodReference.getValueRef(MethodReference.java:82)
    ... 94 more
@Configuration
@EnableAuthorizationServer
@EnableWebSecurity(debug = true)
@EnableGlobalMethodSecurity(prePostEnabled = true)
@SuppressWarnings("static-method")
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Bean
    public static PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }

    @Override
    public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("testclient")
                .secret("password")
                .authorizedGrantTypes("client_credentials")
                .accessTokenValiditySeconds(0)
                .scopes("foo")
                .and()
                .withClient("bar")
                .secret("password")
                .authorizedGrantTypes("client_credentials")
                .accessTokenValiditySeconds(0)
                .scopes("eq.distributor");
    }
}
控制器

@PreAuthorize("#oauth2.hasScope('foo')")
@RequestMapping(value = "/ok", method = RequestMethod.GET)
public String ok() {
    return "ok";
}
Spring安全调试输出

org.springframework.mock.web.MockHttpServletRequest@54704b46

servletPath:
pathInfo:/ok
headers: 
Authorization: Bearer 57962883-e379-433b-b613-1f888471fb84
Accept: application/json;charset=UTF-8


Security filter chain: [
  WebAsyncManagerIntegrationFilter
  SecurityContextPersistenceFilter
  HeaderWriterFilter
  LogoutFilter
  OAuth2AuthenticationProcessingFilter
  RequestCacheAwareFilter
  SecurityContextHolderAwareRequestFilter
  AnonymousAuthenticationFilter
  SessionManagementFilter
  ExceptionTranslationFilter
  FilterSecurityInterceptor
]
任何关于我在设置中缺少什么的帮助都会非常有用。似乎确实有什么地方出错了,好像它实际上是在尝试验证是否返回了401/403。也不例外。减去编译所需的更改,这在Spring Boot 1.5.9下工作


如果我将模拟mvc注释替换为@SpringBootTest,这会起作用,但是我不需要这些测试上的数据库,所以我希望将其设置为不需要加载

之所以发生这种情况,是因为Spring使用的是
DefaultMethodSecurityExpressionHandler
。 见此:

要解决此问题,请执行以下操作:

  • AuthorizationServerConfig
  • 添加以下新配置类
  • 
    @配置
    @EnableGlobalMethodSecurity(Prespenabled=true,securedEnabled=true)
    公共类方法SecurityConfiguration扩展了GlobalMethodSecurityConfiguration{
    @凌驾
    受保护的MethodSecurityExpressionHandler createExpressionHandler(){
    返回新的OAuth2MethodSecurityExpressionHandler();
    }
    }
    

    对我来说很重要的一点是,我已经在另一个配置上安装了
    @EnableGlobalMethodSecurity
    ,因此spring最初忽略了新的配置类

    Hi Rig,您是否尝试解决此问题?我面临着同样的问题,但还没有找到任何有用的解决方案。我从来没有让它在Blangero工作过。一个解决方案是禁用安全性,但我想要我的控制器,但我想验证它们的安全性是否也正常工作。当我在1.5.x和2.0应用程序之间使用我的公共接口库进行虚假通信时,我或多或少地放弃了这一努力。我可能会在新的一年里再次解决这个问题,所以也许我会找到解决办法。