Spring Boot 2 @WebMvcTest 和 OAuth2 #oauth2.hasScope 控制器导致 IllegalArgumentException
Posted
技术标签:
【中文标题】Spring Boot 2 @WebMvcTest 和 OAuth2 #oauth2.hasScope 控制器导致 IllegalArgumentException【英文标题】:Spring Boot 2 @WebMvcTest and OAuth2 #oauth2.hasScope controller causes IllegalArgumentException 【发布时间】:2019-03-23 17:19:07 【问题描述】:这是一个 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
测试类
@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"));
身份验证配置
@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 Security 调试输出
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 这可以工作,但是我不需要这些测试中的数据库,所以我想将其设置为不需要加载它。
【问题讨论】:
嗨 Rig,你有没有尝试解决这个问题?我也面临同样的问题,但还没有找到任何有用的解决方案。 我从来没有让它工作过 Blangero。一种解决方案是禁用安全性,但我想要我的控制器,但我想验证它们的安全性是否也能正常工作。当我使用我的通用接口库在 1.5.x 和 2.0 应用程序之间遇到 Feign 通信问题时,这项工作或多或少地被放弃了。我可能会在新的一年再次解决这个问题,所以也许我会找到解决方案。 【参考方案1】:发生这种情况是因为 Spring 使用的是 DefaultMethodSecurityExpressionHandler
。
看到这个:#oauth2 security expressions on method level
要解决此问题,请执行以下操作:
-
从
AuthorizationServerConfig
中删除@EnableGlobalMethodSecurity
添加以下新配置类
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class MethodSecurityConfiguration extends GlobalMethodSecurityConfiguration
@Override
protected MethodSecurityExpressionHandler createExpressionHandler()
return new OAuth2MethodSecurityExpressionHandler();
对我来说重要的一点是,我已经在另一个配置中使用了 @EnableGlobalMethodSecurity
,所以 spring 最初忽略了新的配置类
【讨论】:
以上是关于Spring Boot 2 @WebMvcTest 和 OAuth2 #oauth2.hasScope 控制器导致 IllegalArgumentException的主要内容,如果未能解决你的问题,请参考以下文章
在 Spring Boot 中禁用 @WebMvcTest 的 Spring Security 配置类
在 Spring Boot 测试类上使用 @WebMvcTest 注释时出错
在 Spring Boot 1.4 MVC 测试中使用 @WebMvcTest 设置 MockMvc
@WebMvcTest 在 Spring Boot 测试中为不同服务提供“Error Creating bean with name”错误