如何使用@WebMvcTest、MockMvc 和 OAuth Security 测试控制器
How can I test a Controller with @WebMvcTest, MockMvc and OAuth Security
我有一个 Spring Boot 2.4.5 项目,Kotlin 使用这个 example
创建
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 用户名密码安全性?
您可以使用 Spring 安全提供的 RequestPostProcessors
之一。
fun testAuthentication() {
mockMvc
.perform(get("/")
.with(oauth2Login())
.andExpect(status().isOk)
}
我找到并适应我的用例的替代解决方案是:
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
但是另一个更短并且工作正常...
如果需要测试特定的角色、范围或名称,这个可能更灵活...
我有一个 Spring Boot 2.4.5 项目,Kotlin 使用这个 example
创建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 用户名密码安全性?
您可以使用 Spring 安全提供的 RequestPostProcessors
之一。
fun testAuthentication() {
mockMvc
.perform(get("/")
.with(oauth2Login())
.andExpect(status().isOk)
}
我找到并适应我的用例的替代解决方案是:
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
但是另一个更短并且工作正常...
如果需要测试特定的角色、范围或名称,这个可能更灵活...