带有 post 的 Spring WebMvcTest 返回 403

Dav*_*rts 6 rest spring kotlin spring-boot

我想知道我的代码问题出在哪里,每次我运行后测试时(不管它的目标是什么控制器或方法),我都会返回 403 错误,在某些情况下我期望是 401,而在其他情况下是200 响应(带身份验证)。

这是我的控制器的片段:

@RestController
@CrossOrigin("*")
@RequestMapping("/user")
class UserController @Autowired constructor(val userRepository: UserRepository) {
    @PostMapping("/create")
    fun addUser(@RequestBody user: User): ResponseEntity<User> {
        return ResponseEntity.ok(userRepository.save(user))
    }
}
Run Code Online (Sandbox Code Playgroud)

我的单元测试针对这个控制器

@RunWith(SpringRunner::class)
@WebMvcTest(UserController::class)
class UserControllerTests {
    @Autowired
    val mvc: MockMvc? = null

    @MockBean
    val repository: UserRepository? = null

    val userCollection = mutableListOf<BioRiskUser>()

    @Test
    fun testAddUserNoAuth() {
        val user = BioRiskUser(
                0L,
                "user",
                "password",
                mutableListOf(Role(
                    0L,
                    "administrator"
                )))
        repository!!
        `when`(repository.save(user)).thenReturn(createUser(user))
        mvc!!
        mvc.perform(post("/create"))
                .andExpect(status().isUnauthorized)
    }

    private fun createUser(user: BioRiskUser): BioRiskUser? {
        user.id=userCollection.count().toLong()
        userCollection.add(user)
        return user
    }
}
Run Code Online (Sandbox Code Playgroud)

我错过了什么?

根据要求,我的安全配置...

@Configuration
@EnableWebSecurity
class SecurityConfig(private val userRepository: UserRepository, private val userDetailsService: UserDetailsService) : WebSecurityConfigurerAdapter() {
    @Bean
    override fun authenticationManagerBean(): AuthenticationManager {
        return super.authenticationManagerBean()
    }

    override fun configure(auth: AuthenticationManagerBuilder) {
        auth.authenticationProvider(authProvider())
    }

    override fun configure(http: HttpSecurity) {
        http
            .csrf().disable()
            .cors()
            .and()
            .httpBasic()
            .realmName("App Realm")
            .and()
            .authorizeRequests()
            .antMatchers("/img/*", "/error", "/favicon.ico", "/doc")
            .anonymous()
            .anyRequest().authenticated()
            .and()
            .logout()
            .invalidateHttpSession(true)
            .clearAuthentication(true)
            .logoutSuccessUrl("/user")
            .permitAll()
    }

    @Bean
    fun authProvider(): DaoAuthenticationProvider {
        val authProvider = CustomAuthProvider(userRepository)
        authProvider.setUserDetailsService(userDetailsService)
        authProvider.setPasswordEncoder(encoder())
        return authProvider
    }
}
Run Code Online (Sandbox Code Playgroud)

和身份验证提供者

class CustomAuthProvider constructor(val userRepository: UserRepository) : DaoAuthenticationProvider() {
    override fun authenticate(authentication: Authentication?): Authentication {
        authentication!!
        val user = userRepository.findByUsername(authentication.name)
        if (!user.isPresent) {
            throw BadCredentialsException("Invalid username or password")
        }
        val result = super.authenticate(authentication)
        return UsernamePasswordAuthenticationToken(user, result.credentials, result.authorities)
    }


    override fun supports(authentication: Class<*>?): Boolean {
        return authentication?.equals(UsernamePasswordAuthenticationToken::class.java) ?: false
    }
}
Run Code Online (Sandbox Code Playgroud)

Kus*_*yal 9

您需要在注释之后添加@ContextConfiguration(classes=SecurityConfig.class)到类的顶部。UserControllerTests@WebMvcTest(UserController::class)

  • 使用“MockMvc”和方法“mvc.perform()”,您实际上并不是在编写单元测试。您实际上是在进行 api 调用,因此需要所有配置。如果您要为控制器编写单元测试,您将存根更深的层并直接调用控制器的方法,在您的情况下是“addUser()”方法,而不是使用“MockMvc”。 (3认同)

Dir*_*irk 8

就我而言,csrf-Protection 在我的 WebMvcTest 中似乎仍然处于活动状态(即使在您的配置中禁用)。

所以为了解决这个问题,我只是将我的 WebMvcTest 更改为:

    @Test
    public void testFoo() throws Exception {

        MvcResult result = mvc.perform(
                    post("/foo").with(csrf()))
                .andExpect(status().isOk())
                .andReturn();

        // ...
    }
Run Code Online (Sandbox Code Playgroud)

所以.with(csrf())在我的情况下,缺少是问题所在。