Skip to content

Instantly share code, notes, and snippets.

@jeffsheets
Created September 6, 2017 16:33
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jeffsheets/44a60c5956648323b97064ffe15fa8ff to your computer and use it in GitHub Desktop.
Save jeffsheets/44a60c5956648323b97064ffe15fa8ff to your computer and use it in GitHub Desktop.
Spock Test for Spring Boot Security configuration - showing basic simple examples for unauthenticated users, role based access, and httpBasic logins
@EnableWebSecurity
class ApiSecurityConfig extends WebSecurityConfigurerAdapter {
@Inject
void configureGlobal(AuthenticationManagerBuilder auth) {
auth.inMemoryAuthentication()
.withUser('svc_acct').password('somePassword').roles('FULL_ACCESS')
}
@Override
protected void configure(HttpSecurity http) {
//Enable Basic authentication via http headers
http.httpBasic()
//Allow unauthenticated GET requests
http.authorizeRequests()
.antMatchers(HttpMethod.GET).permitAll()
//Everything else is secured with a login
.and().authorizeRequests()
.antMatchers('/**').hasRole('FULL_ACCESS')
http.csrf().disable()
}
}
import org.springframework.context.annotation.Profile
import org.springframework.http.HttpMethod
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
import javax.inject.Inject
@ContextConfiguration(classes = [SecurityTestController, ApiSecurityConfig])
@WebAppConfiguration
class ApiSecurityConfigSpec extends Specification {
@Autowired
WebApplicationContext context
MockMvc mvc
void setup() {
mvc = webAppContextSetup(context).apply(springSecurity()).build()
}
def 'GET is accessible without login'() {
when:
def response = mvc.perform(get('/securityTest/')).andReturn().response
then:
response.status == OK.value()
}
@Unroll
def '#method throws error without login'(HttpMethod method) {
when: 'request tried without login'
def response = mvc.perform(request(method, '/securityTest/')).andReturn().response
then: 'it fails'
response.status == UNAUTHORIZED.value()
where:
method << [POST, PUT, PATCH, DELETE]
}
@WithMockUser(roles = ['FULL_ACCESS'])
@Unroll
def '#method works when logged in for user with FULL_ACCESS'(HttpMethod method) {
when:
def response = mvc.perform(request(method, '/securityTest/')).andReturn().response
then:
response.status == OK.value()
where:
method << [POST, PUT, PATCH, DELETE]
}
def 'Login works with the system account'() {
given:
String user = 'svc_acct'
String pass = 'somePassword'
when:
def response = mvc.perform(post('/securityTest/').with(httpBasic(user, pass))).andReturn().response
then:
response.status == OK.value()
}
def 'Login fails with bad account info'() {
given:
String user = 'svc_acct'
String pass = 'ThisIsNotCorrect'
when:
def response = mvc.perform(post('/securityTest/').with(httpBasic(user, pass))).andReturn().response
then:
response.status == UNAUTHORIZED.value()
}
@RestController
@RequestMapping(value = '/securityTest', produces = MediaType.APPLICATION_JSON_VALUE)
static class SecurityTestController {
@GetMapping('/')
ResponseEntity<String> get() {
ok('Saul Goodman')
}
@PostMapping('/')
ResponseEntity post() {
ok('Saul Goodman')
}
@PutMapping('/')
ResponseEntity put() {
ok('Saul Goodman')
}
@PatchMapping('/')
ResponseEntity patch() {
ok('Saul Goodman')
}
@DeleteMapping('/')
ResponseEntity delete() {
ok('Saul Goodman')
}
}
}
//Yeah, imports at the bottom look weird but they work and make the gist look cleaner...
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.HttpMethod
import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity
import org.springframework.security.test.context.support.WithMockUser
import org.springframework.test.context.ContextConfiguration
import org.springframework.test.context.web.WebAppConfiguration
import org.springframework.test.web.servlet.MockMvc
import org.springframework.web.bind.annotation.*
import org.springframework.web.context.WebApplicationContext
import spock.lang.Specification
import spock.lang.Unroll
import static org.springframework.http.HttpMethod.*
import static org.springframework.http.HttpStatus.OK
import static org.springframework.http.HttpStatus.UNAUTHORIZED
import static org.springframework.http.ResponseEntity.ok
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup
@thanhphamduy
Copy link

Hi Jeff, thank you for your helpful example,
I followed it but got an issue java.lang.IllegalArgumentException: WebApplicationContext is required
Could you please share with me some ideas about that?

Regards

@jeffsheets
Copy link
Author

@thanhphamduy I'm not sure exactly, but I'm guessing that the WebApplicationContext context did not autowire correctly. I just ran these tests again locally and they appear fine for me. Can you provide a full stacktrace? (also, sorry but github doesn't give notifications on gist comments, so I didn't see this until now. ping me on twitter @Sheetsj if you are still stuck too)

@garikKalash
Copy link

hi everyone, the same problem on my side. Have you investigated any solution?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment