问题
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