【问题标题】:How can I test a Controller with @WebMvcTest, MockMvc and OAuth Security如何使用 @WebMvcTest、MockMvc 和 OAuth 安全性测试控制器
【发布时间】:2021-07-24 05:16:03
【问题描述】:

我有一个使用 example 创建的带有 Kotlin 的 Spring Boot 2.4.5 项目

src/
├── main/
│   └── kotlin/
│       └── de.mbur.myapp/
│           ├── controller/
│           │   └── WebController.kt
│           ├── security/
│           │   └── WebSecurityConfiguration.kt
│           └── MyApplication.kt
└── test/
    └── kotlin/
        └── de.mbur.myapp/
            ├── controller/
            │   └── WebControllerTest.kt
            ├── security/
            └── MyApplicationTests.kt

安全配置:

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
class WebSecurityConfiguration(private val configurer: AADB2COidcLoginConfigurer) : WebSecurityConfigurerAdapter() {

  override fun configure(http: HttpSecurity) {
    http.authorizeRequests()
      .anyRequest().authenticated()
      .and()
      .apply(configurer)
  }
}

控制器:

@Controller
class WebController {
  private fun initializeModel(model: Model, token: OAuth2AuthenticationToken?) {
    if (token != null) {
      val user = token.principal
      model.addAllAttributes(user.attributes)
      model.addAttribute("grant_type", user.authorities)
      model.addAttribute("name", user.name)
    }
  }

  @GetMapping("/")
  fun index(model: Model, token: OAuth2AuthenticationToken?): String {
    initializeModel(model, token)
    return "home"
  }

}

最后是测试:

@WebMvcTest(WebController::class)
internal class WebControllerTest {

  @Autowired
  private lateinit var mockMvc: MockMvc

  @MockBean
  private lateinit var configurer: AADB2COidcLoginConfigurer

  @Test
  fun testWebController() {
    mockMvc
      .perform(get("/"))
      .andDo(print())
      .andExpect(status().isForbidden)
  }

  @Test
  @WithMockUser
  fun testAuthentication() {
    mockMvc
      .perform(get("/"))
      .andDo(print())
      .andExpect(status().isOk)
  }

}

首先我必须提到,我必须模拟 AADB2COidcLoginConfigurer 才能使测试 testWebController 完全运行。

然后我尝试使用 @WithMockUser 注释运行第二个测试,就像我现在从经典的 Spring 安全测试中一样。但这不起作用:

Request processing failed; nested exception is java.lang.IllegalStateException: Current user principal is not of type [org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken]: UsernamePasswordAuthenticationToken [Principal=org.springframework.security.core.userdetails.User [Username=user, Password=[PROTECTED], Enabled=true, AccountNonExpired=true, credentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=[ROLE_USER]], Credentials=[PROTECTED], Authenticated=true, Details=null, Granted Authorities=[ROLE_USER]]

当需要OAuth2AuthenticationToken 时,如何运行类似于 Spring 用户名密码安全性的测试?

【问题讨论】:

    标签: azure spring-boot spring-security oauth-2.0 junit5


    【解决方案1】:

    我发现并适应我的用例的另一种解决方案是:

    import org.springframework.security.test.context.support.WithSecurityContext
    
    @Retention(AnnotationRetention.RUNTIME)
    @WithSecurityContext(factory = WithMockOAuth2SecurityContextFactory::class)
    annotation class WithMockOAuth2
    
    import org.springframework.security.core.context.SecurityContext
    import org.springframework.security.core.context.SecurityContextHolder
    import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken
    import org.springframework.security.oauth2.core.oidc.OidcIdToken
    import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser
    import org.springframework.security.test.context.support.WithSecurityContextFactory
    
    
    class WithMockOAuth2SecurityContextFactory : WithSecurityContextFactory<WithMockOAuth2?> {
    
      override fun createSecurityContext(mockOAuth2: WithMockOAuth2?): SecurityContext {
        val context = SecurityContextHolder.createEmptyContext()
        if (mockOAuth2 != null) {
          val authorities = null
          val idToken = OidcIdToken
            .withTokenValue("test")
            .claim("sub", "test")
            .build()
          val principal = DefaultOidcUser(authorities, idToken)
          val auth = OAuth2AuthenticationToken(principal, authorities, "test")
          context.authentication = auth
        }
        return context
      }
    }
    
      @Test
      @WithMockOAuth2
      fun testAuthentication() {
        mockMvc
    

    但另一个更短并且工作正常......

    如果需要测试特定的角色、范围或名称,这可能会更灵活...

    【讨论】:

      【解决方案2】:

      您可以使用 Spring Security 提供的RequestPostProcessors 之一。

      fun testAuthentication() {
          mockMvc
            .perform(get("/")
                  .with(oauth2Login())
            .andExpect(status().isOk)
        }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-03-14
        • 2013-01-11
        • 2014-05-31
        • 2019-01-20
        • 2016-11-05
        相关资源
        最近更新 更多