当前位置: 首页 > 知识库问答 >
问题:

Spring Boot 2@WebMvcTest和OAuth2#oauth2.has范围控制器导致IllegalArgumentException

廉宇
2023-03-14

这是一个spring boot 2.0.6应用程序,使用一个MVC控制器,使用@PreAuthorize和#oauth2。hasScope来保护控制器endpoint。我使用@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,这是可行的,但是我不需要这些测试上的数据库,所以我想把它设置为不需要加载它。

共有1个答案

狄鸿禧
2023-03-14

这是因为Spring使用的是DefaultMethodSecurityExpressionHandler。请参见:#oauth2方法级别的安全表达式

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

  1. AuthorizationServerConfig
  2. 中删除 @EnableGlobalMethod odSecurity
  3. 添加以下新配置类

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class MethodSecurityConfiguration extends GlobalMethodSecurityConfiguration {
    @Override
    protected MethodSecurityExpressionHandler createExpressionHandler() {
        return new OAuth2MethodSecurityExpressionHandler();
    }
}

对我来说重要的是,我已经在另一个配置上安装了@EnableGlobalMethod odSecurity,所以Spring最初忽略了新的配置类

 类似资料:
  • 我在身份验证期间重定向到google时出错 网页具有:某些请求的作用域无效。{无效=[https://www.googleapis.com/drive/v2/files]}了解更多请求详细信息范围=https://www.googleapis.com/drive/v2/files 在我的驱动SDK控制台上,启用两个范围:[https://www.googleapis.com/auth/drive]

  • 我试图测试两个不同的控制器类,因为我想测试两个类中的每个方法,我将使用@WebMvcTest,我的问题是有一种方法可以将模拟注入到两个类中,可能是这样的? 当然,这会产生错误,所以这是否意味着当使用@WebMvcTest时,我们只能在一个控制器中测试方法?每班

  • 我试图在一个web应用程序(Java1.8/Tomcat8)中使用最新的JavaMail1.6.0 api,代表该应用程序的客户机用户发送电子邮件。一些客户使用Gmail。我不想要求他们按照javamail FAQ的建议,在他们的谷歌账户中允许访问不安全的应用程序,如果需要的话,我愿意实现oauth2。 创建了一个应用程序 为应用程序(clientid,clientsecret)创建了oauth2

  • 问题内容: 运行期间是否可以注入示波器或控制器?或任何其他建议将服务动态注入控制器? 提前致谢 问题答案: 可以使用$ injector 将服务动态(按名称)注入到控制器中。能够通过控制器参数注入服务只是Angular提供的一种便利。在后台,Angular使用$ injector来检索对象实例。但是我们也可以自己使用$ injector。 小提琴。

  • 我尝试注入$scope,然后使用,但这不起作用? 有人能提供建议吗?

  • 通过 Google 对用户进行身份验证 OAuth2 是用于授权的行业标准协议。它提供了一种机制,使用户可以在不共享用户名,密码和其他私有凭据的情况下,向Web 和桌面应用程序授予对私有信息的访问权限。 本教程构建了一个扩展程序,该扩展程序使用Google People API 和Chrome Identity API 访问用户的 Google 联系人。由于扩展程序无法通过 HTTPS加载,无法执