REST request blocked when executed in an `@SpringBootTest` but not when I run the application?

自古美人都是妖i 提交于 2019-12-10 18:39:45

问题


I have a Spring Boot application with a Jersey REST api at /rest:

@Component
@ApplicationPath("/rest")
public class RestApplication extends ResourceConfig {
    public RestApplication(){
        packages("ch.cypherk.myapp.service.rest");
    }
}

this here is the controller that should allow a user to log in:

@Service
@Path("/login")
class LoginRestController
@Inject constructor(
    private val authenticationService: MyAuthenticationProvider
) {

    @Operation(summary = "Login user, use cookie JSESSIONID in response header for further requests authentication.",
        requestBody = RequestBody(description = "Login credentials of user.", required = true,
            content = [Content(schema = Schema(implementation = LoginDto::class))]),
        responses = [
            ApiResponse(responseCode = "202", description = "Login successful."),
            ApiResponse(responseCode = "403", description = "Invalid credentials supplied.")])
    @POST
    fun postLogin(loginCredentials: LoginDto) {
        val auth = UsernamePasswordAuthenticationToken(loginCredentials.username,loginCredentials.password)
        authenticationService.authenticate(auth)
    }
}

Where

@Service
class MyAuthenticationProvider (
    ...
): AbstractUserDetailsAuthenticationProvider() {

    @Transactional
    override fun authenticate(authentication: Authentication?): Authentication {
        return super.authenticate(authentication)
    }
    ...
}

is just a custom implementation that adds a bunch of extra checks.

If I start up the Spring Boot application and use postman to make a post request to http://localhost:8080/rest/login and with valid credentials, everything works as intended.

However, if I try to write a test for it,

import com.mashape.unirest.http.Unirest
import io.restassured.RestAssured.given
import org.apache.http.HttpStatus.*
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner

@RunWith(SpringJUnit4ClassRunner::class)
@SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT)
class LoginRestControllerIT {

    @Test
    fun login() {
        given()
            .contentType("application/json")
            .body(LoginDto("testuserA", "secret"))
        .When()
            .post("/rest/login")

        .then()
            .statusCode(SC_NO_CONTENT)
            .cookie("JSESSIONID")
    }
}

it gives me an error because I expected 204 (no content) response code and got an 403 (forbidden).

Why?

The above code is using RestAssured, but the issue isn't due to the framework as I've tried rewriting the test with Unirest,

val response = Unirest.post("http://localhost:8080/rest/login")
    .field("username","testuserA")
    .field("password","secret")
    .asJson()

assertThat(response.status).isEqualTo(SC_NO_CONTENT)

and got the same error.

I've set a breakpoint at the first statement in my LoginRestController's login function and it is NOT reached. Which means something somewhere earlier in the chain already rejects the request

My security config looks like this:

@Configuration
@EnableWebSecurity
@EnableVaadin
@EnableVaadinSharedSecurity
@EnableGlobalMethodSecurity(
    securedEnabled = true,
    prePostEnabled = true,
    proxyTargetClass = true
)
class VaadinAwareSecurityConfiguration @Inject constructor(
    private val authenticationProvider:AuthenticationProvider
) : WebSecurityConfigurerAdapter() {


    override fun configure(http: HttpSecurity) {
        http
            .csrf().disable()
            .httpBasic().disable()
            .formLogin().disable()
            .authorizeRequests()
                .antMatchers("/").permitAll()
                .antMatchers("/login").anonymous()
                .antMatchers("/rest/login/**").permitAll()
                .antMatchers("/vaadinServlet/UIDL/**").permitAll()
                .antMatchers("/vaadinServlet/HEARTBEAT/**").permitAll()
                .anyRequest().authenticated()
                .and()
            .logout()
                .addLogoutHandler(logoutHandler())
                .logoutUrl("/logout")
                .logoutSuccessUrl("/login?goodbye").permitAll()
                .and()
            .exceptionHandling()
                .authenticationEntryPoint(LoginUrlAuthenticationEntryPoint("/login"))
    }

    override fun configure(web: WebSecurity) {
        web
            .ignoring().antMatchers(
                "/VAADIN/**"
            )
    }

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

    private fun logoutHandler(): LogoutHandler {
        return VaadinSessionClosingLogoutHandler()
    }

    @Bean
    fun myAuthenticationManager(): AuthenticationManager {
        return super.authenticationManagerBean()
    }
}

UPDATE

So it looks like it's the org.springframework.security.web.csrf.CsrfFilter that ends up throwing an AccessDeniedException because the csrf token is missing ... but it shouldn't do that as I've disabled Spring's csrf protection ...

来源:https://stackoverflow.com/questions/56541326/rest-request-blocked-when-executed-in-an-springboottest-but-not-when-i-run-th

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!