Basic and mechanical story Authentication / Authorization Story Remember-Me story CSRF story Session management story The story of the response header Method security story CORS story The story of Run-As The story of ACL Talk about cooperation with MVC and Boot
Extra edition What Spring Security can and cannot do
Spring Security provides a mechanism to support testing with JUnit.
For example, the user used for testing can be fixed and the authority can be specified.
Hello World
build.gradle
dependencies {
compile 'org.springframework.security:spring-security-web:4.2.1.RELEASE'
compile 'org.springframework.security:spring-security-config:4.2.1.RELEASE'
testCompile 'junit:junit:4.12'★ Addition
testCompile 'org.springframework.security:spring-security-test:4.2.1.RELEASE'★ Addition
}
--Added JUnit and Spring Test modules to dependencies
MyTestService.java
package sample.spring.security.test;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.context.SecurityContextHolder;
public class MyTestService {
@PreAuthorize("authenticated")
public String getMessage() {
String name = SecurityContextHolder.getContext().getAuthentication().getName();
return "Hello " + name;
}
}
--Class to be tested
--Check if authenticated with @PreAuthorize ()
--Get the name of the current ʻAuthentication and return it in the form
"Hello Name" `
namespace
src/test/resources/test-applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:sec="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<sec:global-method-security pre-post-annotations="enabled" />
<bean class="sample.spring.security.test.MyTestService" />
</beans>
--Spring config file for testing --Actually, I think that you should test using the same configuration file as the production, but since the purpose of this is to verify the operation, we have prepared another file --Turn on method security and register the class under test as a bean
MyTestServiceTest.java
package sample.spring.security.test;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/test-applicationContext.xml")
public class MyTestServiceTest {
@Autowired
private MyTestService service;
@Test(expected = AuthenticationException.class)
public void test_getMessage_no_authentication() throws Exception {
// exercise
this.service.getMessage();
}
@Test
@WithMockUser(username = "hoge")
public void test_getMessage() throws Exception {
// exercise
String message = this.service.getMessage();
// verify
Assert.assertEquals("Hello hoge", message);
}
}
--Test class
--Reading the test configuration file with @ContextConfiguration
--Testing that ʻAuthenticationException is thrown when
getMessage () is executed without specifying
@WithMockUser --If you specify
@WithMockUser, you are testing that the return message is constructed with the string specified by
@WithMockUser`.
Java Configuration
MyTestSpringSecurityConfig.java
package sample.spring.security.test;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class MyTestSpringSecurityConfig {
@Bean
public MyTestService myTestService() {
return new MyTestService();
}
}
--Spring configuration class for testing --The content is the same as the one in xml
MyTestServiceTest.java
package sample.spring.security.test;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=MyTestSpringSecurityConfig.class)
public class MyTestServiceTest {
@Autowired
private MyTestService service;
@Test(expected = AuthenticationException.class)
public void test_getMessage_no_authentication() throws Exception {
// exercise
this.service.getMessage();
}
@Test
@WithMockUser(username = "hoge")
public void test_getMessage() throws Exception {
// exercise
String message = this.service.getMessage();
// verify
Assert.assertEquals("Hello hoge", message);
}
}
--This is also the same as xml except that the configuration class is loaded with @ContextConfiguration
.
If you run each, the test will succeed.
MyTestServiceTest.java
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/test-applicationContext.xml")
--These two annotations are for Spring Test, not for Spring Security.
--With the settings here, you are building a ʻApplicationContextfor testing. --In
@ContextConfiguration, specify the Spring settings to use for testing. --For namespace, pass the location on the xml classpath, for Java Configuration, pass the
Class` object of the configuration class
MyTestServiceTest.java
@Test
@WithMockUser(username = "hoge")
public void test_getMessage() throws Exception {
--By annotating with @WithMockUser
, you can specify the user information (ʻAuthentication) that will be used while the test is running. --- Since username = "hoge"
, ʻAuthentication with username
hogewill be used. --Spring Security integrates with Spring Test to set and clear
SecurityContextHolder` information before and after the test.
Any information can be set in the SecurityContext
under test by annotation.
@WithMockUser
MyTestServiceTest.java
package sample.spring.security.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/test-applicationContext.xml")
public class MyTestServiceTest {
@Test
@WithMockUser
public void test() throws Exception {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String message = "class = " + auth.getClass() + "\n" +
"name = " + auth.getName() + "\n" +
"credentials = " + auth.getCredentials() + "\n" +
"authorities = " + auth.getAuthorities() + "\n" +
"principal = " + auth.getPrincipal() + "\n" +
"details = " + auth.getDetails();
System.out.println(message);
}
}
Execution result
class = class org.springframework.security.authentication.UsernamePasswordAuthenticationToken
name = user
credentials = password
authorities = [ROLE_USER]
principal = org.springframework.security.core.userdetails.User@36ebcb: Username: user; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER
details = null
--Using @WithMockUser
saves mock user information in SecurityContext
--If nothing is specified, it has the following information by default.
--Username is ʻuser --Password is
password --Permission is
ROLE_USER --ʻUsernamePasswordAuthenticationToken
is used to implement ʻAuthentication --The ʻUser
object is used as the principal
--ʻUser` is a mock user, so the user with that name doesn't really need to exist
--It is also possible to annotate the class (in which case all methods will use the mock user)
MyTestServiceTest.java
@Test
@WithMockUser("test-user")
public void test() throws Exception {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String message = "class = " + auth.getClass() + "\n" +
"name = " + auth.getName() + "\n" +
"credentials = " + auth.getCredentials() + "\n" +
"authorities = " + auth.getAuthorities() + "\n" +
"principal = " + auth.getPrincipal() + "\n" +
"details = " + auth.getDetails();
System.out.println(message);
}
Execution result
class = class org.springframework.security.authentication.UsernamePasswordAuthenticationToken
name = test-user
credentials = password
authorities = [ROLE_USER]
principal = org.springframework.security.core.userdetails.User@b6e8d426: Username: test-user; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER
details = null
--The character string set in value
is used as the user name
--Can be specified with ʻusername instead of
value --When setting with roles described later, it is easier to understand if you use ʻusername
rather than value
.
MyTestServiceTest.java
@Test
@WithMockUser(
username="test-user",
roles={"FOO", "BAR"}
)
public void test() throws Exception {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String message = "class = " + auth.getClass() + "\n" +
"name = " + auth.getName() + "\n" +
"credentials = " + auth.getCredentials() + "\n" +
"authorities = " + auth.getAuthorities() + "\n" +
"principal = " + auth.getPrincipal() + "\n" +
"details = " + auth.getDetails();
System.out.println(message);
}
Execution result
class = class org.springframework.security.authentication.UsernamePasswordAuthenticationToken
name = test-user
credentials = test-pass
authorities = [ROLE_BAR, ROLE_FOO]
principal = org.springframework.security.core.userdetails.User@b6e8d426: Username: test-user; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_BAR,ROLE_FOO
details = null
--By specifying roles
, the authority to be automatically given the ROLE_
prefix is set.
--An error occurs when set with ʻauthorities`
MyTestServiceTest.java
@Test
@WithMockUser(
username="test-user",
authorities={"FOO", "BAR"}
)
public void test() throws Exception {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String message = "class = " + auth.getClass() + "\n" +
"name = " + auth.getName() + "\n" +
"credentials = " + auth.getCredentials() + "\n" +
"authorities = " + auth.getAuthorities() + "\n" +
"principal = " + auth.getPrincipal() + "\n" +
"details = " + auth.getDetails();
System.out.println(message);
}
Execution result
class = class org.springframework.security.authentication.UsernamePasswordAuthenticationToken
name = test-user
credentials = password
authorities = [BAR, FOO]
principal = org.springframework.security.core.userdetails.User@b6e8d426: Username: test-user; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: BAR,FOO
details = null
--Permissions are set by specifying ʻauthorities --An error occurs when set with
roles`
MyTestServiceTest.java
@Test
@WithMockUser(password="test-pass")
public void test() throws Exception {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String message = "class = " + auth.getClass() + "\n" +
"name = " + auth.getName() + "\n" +
"credentials = " + auth.getCredentials() + "\n" +
"authorities = " + auth.getAuthorities() + "\n" +
"principal = " + auth.getPrincipal() + "\n" +
"details = " + auth.getDetails();
System.out.println(message);
}
Execution result
class = class org.springframework.security.authentication.UsernamePasswordAuthenticationToken
name = user
credentials = test-pass
authorities = [ROLE_USER]
principal = org.springframework.security.core.userdetails.User@36ebcb: Username: user; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER
details = null
--You can set a password with password
@WithAnonymousUser
MyTestServiceTest.java
package sample.spring.security.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.test.context.support.WithAnonymousUser;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@WithMockUser
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/test-applicationContext.xml")
public class MyTestServiceTest {
@Test
@WithAnonymousUser
public void testAnonymous() throws Exception {
this.printAuthentication("testAnonymous");
}
@Test
public void testDefault() throws Exception {
this.printAuthentication("testDefault");
}
private void printAuthentication(String testMethodName) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String message =
"[" + testMethodName + "]\n" +
"class = " + auth.getClass() + "\n" +
"name = " + auth.getName() + "\n" +
"credentials = " + auth.getCredentials() + "\n" +
"authorities = " + auth.getAuthorities() + "\n" +
"principal = " + auth.getPrincipal() + "\n" +
"details = " + auth.getDetails();
System.out.println(message);
}
}
Execution result
[testAnonymous]
class = class org.springframework.security.authentication.AnonymousAuthenticationToken
name = anonymous
credentials =
authorities = [ROLE_ANONYMOUS]
principal = anonymous
details = null
[testDefault]
class = class org.springframework.security.authentication.UsernamePasswordAuthenticationToken
name = user
credentials = password
authorities = [ROLE_USER]
principal = org.springframework.security.core.userdetails.User@36ebcb: Username: user; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER
details = null
--Anonymous user is used when annotating with @WithAnonymousUser
--Even if the class is annotated with @WithMockUser
, the setting can be overridden by annotating the method with @WithAnonymousUser
.
@WithUserDetails
MyUserDetailsService.java
package sample.spring.security.service;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import java.util.Collection;
public class MyUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return new MyUser(username, "test-password", AuthorityUtils.createAuthorityList("FOO", "BAR"));
}
private static class MyUser extends User {
private MyUser(String username, String password, Collection<? extends GrantedAuthority> authorities) {
super(username, password, authorities);
}
}
}
test-applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean class="sample.spring.security.service.MyUserDetailsService" />
</beans>
MyTestServiceTest.java
package sample.spring.security.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.test.context.support.WithUserDetails;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/test-applicationContext.xml")
public class MyTestServiceTest {
@Test
@WithUserDetails
public void test() throws Exception {
this.printAuthentication("test");
}
private void printAuthentication(String testMethodName) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String message =
"[" + testMethodName + "]\n" +
"class = " + auth.getClass() + "\n" +
"name = " + auth.getName() + "\n" +
"credentials = " + auth.getCredentials() + "\n" +
"authorities = " + auth.getAuthorities() + "\n" +
"principal = " + auth.getPrincipal() + "\n" +
"details = " + auth.getDetails();
System.out.println(message);
}
}
Execution result
[test]
class = class org.springframework.security.authentication.UsernamePasswordAuthenticationToken
name = user
credentials = test-password
authorities = [BAR, FOO]
principal = sample.spring.security.service.MyUserDetailsService$MyUser@36ebcb: Username: user; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: BAR,FOO
details = null
--If you annotate with @WithUserDetails
, the authentication information will be constructed with the user information obtained from ʻUserDetailsService registered as a bean. --Use when using your own ʻUserDetails
--@WithUserDetails`` value
allows you to specify the name of the user to search for (default is " user "
)
--ʻUserDetailsServiceBeanName allows you to specify the ʻUserDetailsService
bean to use
@WithSecurityContext
MyTestUser.java
package sample.spring.security.test;
import org.springframework.security.test.context.support.WithSecurityContext;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
@WithSecurityContext(factory=MyTestUserFactory.class)
public @interface MyTestUser {
String name();
String pass();
String authority();
}
MyTestUserFactory.java
package sample.spring.security.test;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.test.context.support.WithSecurityContextFactory;
public class MyTestUserFactory implements WithSecurityContextFactory<MyTestUser> {
@Override
public SecurityContext createSecurityContext(MyTestUser annotation) {
SecurityContext context = SecurityContextHolder.createEmptyContext();
String name = annotation.name();
String pass = annotation.pass();
String authority = annotation.authority();
UserDetails user = new User(name, pass, AuthorityUtils.createAuthorityList(authority));
Authentication authentication = new UsernamePasswordAuthenticationToken(
user, user.getPassword(), user.getAuthorities());
context.setAuthentication(authentication);
return context;
}
}
MyTestServiceTest.java
package sample.spring.security.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/test-applicationContext.xml")
public class MyTestServiceTest {
@Test
@MyTestUser(name="foo", pass="FOO", authority="TEST_FOO")
public void test() throws Exception {
this.printAuthentication("test");
}
private void printAuthentication(String testMethodName) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String message =
"[" + testMethodName + "]\n" +
"class = " + auth.getClass() + "\n" +
"name = " + auth.getName() + "\n" +
"credentials = " + auth.getCredentials() + "\n" +
"authorities = " + auth.getAuthorities() + "\n" +
"principal = " + auth.getPrincipal() + "\n" +
"details = " + auth.getDetails();
System.out.println(message);
}
}
Execution result
[test]
class = class org.springframework.security.authentication.UsernamePasswordAuthenticationToken
name = foo
credentials = FOO
authorities = [TEST_FOO]
principal = org.springframework.security.core.userdetails.User@18cc6: Username: foo; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: TEST_FOO
details = null
--You can create your own SecurityContext
with any implementation
MyTestUser.java
@WithSecurityContext(factory=MyTestUserFactory.class)
public @interface MyTestUser {
--Create an arbitrary annotation and annotate it with @WithSecurityContext
--In factory
, specify the Class
object of the class that implements the WithSecurityContextFactory
interface.
--The factory class specified here implements the process to create SecurityContext
.
MyTestUserFactory.java
package sample.spring.security.test;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.test.context.support.WithSecurityContextFactory;
public class MyTestUserFactory implements WithSecurityContextFactory<MyTestUser> {
@Override
public SecurityContext createSecurityContext(MyTestUser annotation) {
SecurityContext context = SecurityContextHolder.createEmptyContext();
...
return context;
}
}
--A factory class created by implementing the WithSecurityContextFactory <T>
interface
--In the type variable T
, specify the type of annotation handled by the factory class.
--Implement the createSecurityContext ()
method to create and return your own SecurityContext
--This class does not need to be registered in the Spring container.
--WithSecurityContextTestExecutionListener
will generate a factory from the annotation when the test is run
MyTestServiceTest.java
@MyTestUser(name="foo", pass="FOO", authority="TEST_FOO")
public void test() throws Exception {
--After that, if you annotate the test method with your own annotation, the SecurityContext
created in the specified factory will be used as the authentication information.
MyTestUserFactory.java
public class MyTestUserFactory implements WithSecurityContextFactory<MyTestUser> {
private UserDetailsService userDetailsService;
@Autowired
public MyTestUserFactory(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
--Factory classes can inject other beans in the same way as regular beans
--The above implementation uses constructor injection (@Autowired
can be omitted)
HogeUser.java
package sample.spring.security.test;
import org.springframework.security.test.context.support.WithMockUser;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@WithMockUser(username = "hoge", authorities = {"FOO", "BAR"})
public @interface HogeUser {
}
MyTestServiceTest.java
package sample.spring.security.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/test-applicationContext.xml")
public class MyTestServiceTest {
@Test
@HogeUser
public void test() throws Exception {
this.printAuthentication("test");
}
private void printAuthentication(String testMethodName) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String message =
"[" + testMethodName + "]\n" +
"class = " + auth.getClass() + "\n" +
"name = " + auth.getName() + "\n" +
"credentials = " + auth.getCredentials() + "\n" +
"authorities = " + auth.getAuthorities() + "\n" +
"principal = " + auth.getPrincipal() + "\n" +
"details = " + auth.getDetails();
System.out.println(message);
}
}
Execution result
[test]
class = class org.springframework.security.authentication.UsernamePasswordAuthenticationToken
name = hoge
credentials = password
authorities = [BAR, FOO]
principal = org.springframework.security.core.userdetails.User@30f425: Username: hoge; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: BAR,FOO
details = null
--You can annotate your own annotations with the @WithMockUser
annotation.
--If you use this self-made annotation for a test method etc., the setting of @WithMockUser
set in the self-made annotation will be inherited.
--This mechanism is called ** meta-annotation **
--When using mock users with the same settings in various tests, it is easier to use meta annotations because the settings are in one place rather than annotating each method with @WithMockUser
.
--Of course, you can use it with @WithUserDetails
as well as @WithMockUser
.
Hello World Implementation
build.gradle
dependencies {
compile 'org.springframework.security:spring-security-web:4.2.1.RELEASE'
compile 'org.springframework.security:spring-security-config:4.2.1.RELEASE'
compile 'org.springframework:spring-webmvc:4.2.1.RELEASE'★ Addition
testCompile 'junit:junit:4.12'
testCompile 'org.springframework.security:spring-security-test:4.2.1.RELEASE'
}
--Adding a dependency of spring-webmvc
because we will add an implementation of Spring MVC
MyMvcController.java
package sample.spring.security.control;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/mvc")
public class MyMvcController {
@GetMapping
public String hello() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
System.out.println("auth.name = " + auth.getName());
return "test";
}
}
--Controller class mapped to GET request to / mvc
--ʻOutputting the name of Authentication`
test-applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:sec="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<bean class="sample.spring.security.control.MyMvcController" />
<sec:http>
<sec:intercept-url pattern="/login" access="permitAll" />
<sec:intercept-url pattern="/**" access="isAuthenticated()" />
<sec:form-login />
<sec:logout />
</sec:http>
<sec:authentication-manager />
</beans>
--Simple declaration for Spring Security and Bean declaration for MyMvcController
MyMvcControllerTest.java
package sample.spring.security.test;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/test-applicationContext.xml")
@WebAppConfiguration
public class MyMvcControllerTest {
@Autowired
private WebApplicationContext context;
private MockMvc mvc;
@Before
public void setUp() throws Exception {
this.mvc = MockMvcBuilders
.webAppContextSetup(this.context)
.apply(SecurityMockMvcConfigurers.springSecurity())
.build();
}
@Test
public void unauthorized() throws Exception {
MvcResult mvcResult = this.mvc.perform(get("/mvc")).andReturn();
this.printResponse("unauthorized", mvcResult);
}
@Test
public void authorized() throws Exception {
MvcResult mvcResult = this.mvc.perform(get("/mvc").with(user("foo"))).andReturn();
this.printResponse("authorized", mvcResult);
}
private void printResponse(String method, MvcResult result) throws Exception {
MockHttpServletResponse response = result.getResponse();
int status = response.getStatus();
String locationHeader = response.getHeader("Location");
System.out.println("[" + method + "]\n" +
"status : " + status + "\n" +
"Location : " + locationHeader);
}
}
--There are two test methods, ʻunauthorized and ʻauthorized
.
--Each access to / mvc
and prints the resulting HTTP status and Location
header
Execution result
auth.name = foo
[authorized]
status : 200
Location : null
[unauthorized]
status : 302
Location : http://localhost/login
--ʻAuthorized can be executed by the controller and the user name is output. --ʻUnauthorized
returns a status of 302
and is prompted to redirect to the login screen
Description
MyMvcControllerTest.java
import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/test-applicationContext.xml")
@WebAppConfiguration
public class MyMvcControllerTest {
@Autowired
private WebApplicationContext context;
private MockMvc mvc;
@Before
public void setUp() throws Exception {
this.mvc = MockMvcBuilders
.webAppContextSetup(this.context)
.apply(SecurityMockMvcConfigurers.springSecurity())
.build();
}
――The implementation of this test preparation is as it was written in the document because I have not studied Spring MVC, so I do not understand much meaning.
--The settings for Spring Security are SecurityMockMvcConfigurers.springSecurity ()
in the setUp ()
method.
--With this setting, it seems that Spring Security Filter
is incorporated in the MVC mock and Spring Security processing works.
MyMvcControllerTest.java
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;.
...
@Test
public void unauthorized() throws Exception {
MvcResult mvcResult = this.mvc.perform(get("/mvc")).andReturn();
this.printResponse("unauthorized", mvcResult);
}
@Test
public void authorized() throws Exception {
MvcResult mvcResult = this.mvc.perform(get("/mvc").with(user("foo"))).andReturn();
this.printResponse("authorized", mvcResult);
}
--get ()
is the static
method of MockMvcRequestBuilders
--ʻUser () is the
staticmethod of
SecurityMockMvcRequestPostProcessors --Specify the name of the login user at runtime --Spring MVC Test has a mechanism to rewrite the request and uses a class that implements the
RequestPostProcessorinterface. --Spring Security provides a number of classes that implement this
RequestPostProcessorto make it easier to configure for Spring Security. --A class called
SecurityMockMvcRequestPostProcessors, which is a collection of
static` factory methods, is provided as a window for using those classes.
CSRF If CSRF protection is enabled, the token must be included in the request. If you call the controller as it is without doing anything, an error will occur because there is no token.
Therefore, there is an API for testing to pass the CSRF token check.
Implementation
test-applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:sec="http://www.springframework.org/schema/security"
...>
<bean class="sample.spring.security.control.MyMvcController" />
<sec:http>
<sec:intercept-url pattern="/login" access="permitAll" />
<sec:intercept-url pattern="/**" access="isAuthenticated()" />
<sec:form-login />
<sec:logout />
<sec:csrf />★ Addition
</sec:http>
<sec:authentication-manager />
</beans>
--Add <csrf>
to enable CSRF protection
MyMvcController.java
package sample.spring.security.control;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/mvc")
public class MyMvcController {
@PostMapping
public String hello() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
System.out.println("auth.name = " + auth.getName());
return "test";
}
}
--Changed method annotation to @PostMapping
--Because CSRF measures are not executed by the GET method
MyMvcControllerTest.java
package sample.spring.security.test;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/test-applicationContext.xml")
@WebAppConfiguration
public class MyMvcControllerTest {
@Autowired
private WebApplicationContext context;
private MockMvc mvc;
@Before
public void setUp() throws Exception {
this.mvc = MockMvcBuilders
.webAppContextSetup(this.context)
.apply(SecurityMockMvcConfigurers.springSecurity())
.build();
}
@Test
public void noToken() throws Exception {
MvcResult mvcResult = this.mvc.perform(
post("/mvc")
.with(user("foo"))
).andReturn();
this.printResponse("noToken", mvcResult);
}
@Test
public void useToken() throws Exception {
MvcResult mvcResult = this.mvc.perform(
post("/mvc")
.with(user("bar"))
.with(csrf())
).andReturn();
this.printResponse("useToken", mvcResult);
}
private void printResponse(String method, MvcResult result) throws Exception {
MockHttpServletResponse response = result.getResponse();
int status = response.getStatus();
String errorMessage = response.getErrorMessage();
System.out.println("[" + method + "]\n" +
"status : " + status + "\n" +
"errorMessage : " + errorMessage);
}
}
Execution result
[noToken]
status : 403
errorMessage : Could not verify the provided CSRF token because your session was not found.
auth.name = bar
[useToken]
status : 200
errorMessage : null
--noToken ()
is caught in the token check of CSRF and an error occurs.
--ʻUseToken () `successfully executed the controller method
Description
MyMvcControllerTest.java
@Test
public void noToken() throws Exception {
MvcResult mvcResult = this.mvc.perform(
post("/mvc")
.with(user("foo"))
).andReturn();
this.printResponse("noToken", mvcResult);
}
@Test
public void useToken() throws Exception {
MvcResult mvcResult = this.mvc.perform(
post("/mvc")
.with(user("bar"))
.with(csrf())
).andReturn();
this.printResponse("useToken", mvcResult);
}
--Use the csrf ()
method to include the CSRF token in the test run-time request.
--This method is also defined in SecurityMockMvcRequestPostProcessors
--If you want to put the token in the header, use csrf (). AsHeader ()
Implementation
MyMvcControllerTest.java
@Test
public void useInvalidToken() throws Exception {
MvcResult mvcResult = this.mvc.perform(
post("/mvc")
.with(user("bar"))
.with(csrf().useInvalidToken())
).andReturn();
this.printResponse("useInvalidToken", mvcResult);
}
Execution result
[useInvalidToken]
status : 403
errorMessage : Invalid CSRF Token 'invalidd19aed27-65e2-4cf6-9456-157e1f29c984' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN'.
Description
-- csrf (). useInvalidToken ()
will set an invalid token
There are two ways to specify the user:
--Method using RequestPostProcessor
--Method using annotations
RequestPostProcessor
A method using the extension point of the request provided by Spring MVC Test. Compared to the specification by annotation, it seems that the feature is that it can be specified dynamically.
Implementation
MyMvcController.java
package sample.spring.security.control;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.stream.Collectors;
@Controller
@RequestMapping("/mvc")
public class MyMvcController {
@GetMapping
public String hello() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String name = auth.getName();
Object credentials = auth.getCredentials();
Object principal = auth.getPrincipal();
String authorities = auth.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.joining(", "));
System.out.println(
"name = " + name + "\n" +
"credentials = " + credentials + "\n" +
"authorities = " + authorities + "\n" +
"principal = " + principal
);
return "test";
}
}
MyMvcControllerTest.java
package sample.spring.security.test;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import java.util.List;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/test-applicationContext.xml")
@WebAppConfiguration
public class MyMvcControllerTest {
private static final String USERNAME = "foo";
private static final String PASSWORD = "test-pass";
private static final List<GrantedAuthority> AUTHORITIES = AuthorityUtils.createAuthorityList("FOO", "BAR");
@Autowired
private WebApplicationContext context;
private MockMvc mvc;
@Before
public void setUp() throws Exception {
this.mvc = MockMvcBuilders
.webAppContextSetup(this.context)
.apply(SecurityMockMvcConfigurers.springSecurity())
.build();
}
@Test
public void withUser() throws Exception {
System.out.println("[withUser]");
this.mvc.perform(
get("/mvc").with(user(USERNAME))
);
}
@Test
public void customized() throws Exception {
System.out.println("[customized]");
this.mvc.perform(
get("/mvc").with(
user(USERNAME)
.password(PASSWORD)
.authorities(AUTHORITIES)
)
);
}
@Test
public void userDetails() throws Exception {
System.out.println("[userDetails]");
UserDetails user = this.createUserDetails();
this.mvc.perform(
get("/mvc").with(user(user))
);
}
@Test
public void withAnonymous() throws Exception {
System.out.println("[withAnonymous]");
this.mvc.perform(
get("/mvc").with(anonymous())
);
}
@Test
public void withAuthentication() throws Exception {
System.out.println("[withAuthentication]");
Authentication auth = this.createAuthentication();
this.mvc.perform(
get("/mvc").with(authentication(auth))
);
}
@Test
public void withSecurityContext() throws Exception {
System.out.println("[withAuthentication]");
Authentication auth = this.createAuthentication();
SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(auth);
this.mvc.perform(
get("/mvc").with(securityContext(context))
);
}
private UserDetails createUserDetails() {
return new User(USERNAME, PASSWORD, AUTHORITIES);
}
private Authentication createAuthentication() {
UserDetails user = this.createUserDetails();
return new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities());
}
}
Execution result
[withUser]
name = foo
credentials = password
authorities = ROLE_USER
principal = org.springframework.security.core.userdetails.User@18cc6: Username: foo; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER
[customized]
name = foo
credentials = test-pass
authorities = BAR, FOO
principal = org.springframework.security.core.userdetails.User@18cc6: Username: foo; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: BAR,FOO
[userDetails]
name = foo
credentials = test-pass
authorities = BAR, FOO
principal = org.springframework.security.core.userdetails.User@18cc6: Username: foo; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: BAR,FOO
[withAnonymous]
name = anonymous
credentials =
authorities = ROLE_ANONYMOUS
principal = anonymous
[withAuthentication]
name = foo
credentials = test-pass
authorities = BAR, FOO
principal = org.springframework.security.core.userdetails.User@18cc6: Username: foo; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: BAR,FOO
[withSecurityContext]
name = foo
credentials = test-pass
authorities = BAR, FOO
principal = org.springframework.security.core.userdetails.User@18cc6: Username: foo; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: BAR,FOO
Description
--If only user (String) is specified, only the user name will be the specified value. --Password is
password --Authority is only
ROLE_USER --You can make detailed settings with
password (String) and ʻauthorities (Collection <? Extends GrantedAuthority>)
after ʻuser (String) --You can also specify roles with
roles (String ...) --If ʻuser (UserDetails)
, you can specify ʻUserDetailsdirectly. --You can specify an anonymous user with ʻanonymous ()
--You can directly specify ʻAuthentication with ʻauthentication (Authentication)
--You can specify SecurityContext
directly withsecurityContext (SecurityContext)
Implementation
MyMvcControllerTest.java
package sample.spring.security.test;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/test-applicationContext.xml")
@WebAppConfiguration
public class MyMvcControllerTest {
@Autowired
private WebApplicationContext context;
private MockMvc mvc;
@Before
public void setUp() throws Exception {
this.mvc = MockMvcBuilders
.webAppContextSetup(this.context)
.apply(springSecurity())
.defaultRequest(get("/")
.with(
user("foo")
.password("test-pass")
.authorities(AuthorityUtils.createAuthorityList("FOO", "BAR"))
)
)
.build();
}
@Test
public void test() throws Exception {
this.mvc.perform(get("/mvc"));
}
}
Execution result
name = foo
credentials = test-pass
authorities = BAR, FOO
principal = org.springframework.security.core.userdetails.User@18cc6: Username: foo; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: BAR,FOO
Description
MyMvcControllerTest.java
@Before
public void setUp() throws Exception {
this.mvc = MockMvcBuilders
.webAppContextSetup(this.context)
.apply(springSecurity())
.defaultRequest(get("/")
.with(
user("foo")
.password("test-pass")
.authorities(AuthorityUtils.createAuthorityList("FOO", "BAR"))
)
)
.build();
}
--If you specify the user information with defaultRequest ()
when creating MockMvc
, you can use it as the default setting.
Implementation
MyMockUserPostProcessors.java
package sample.spring.security.test;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.test.web.servlet.request.RequestPostProcessor;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;
public class MyMockUserPostProcessors {
public static RequestPostProcessor hoge() {
return user("hoge")
.password("test-pass")
.authorities(AuthorityUtils.createAuthorityList("FOO", "BAR"));
}
}
MyMvcControllerTest.java
package sample.spring.security.test;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static sample.spring.security.test.MyMockUserPostProcessors.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/test-applicationContext.xml")
@WebAppConfiguration
public class MyMvcControllerTest {
@Autowired
private WebApplicationContext context;
private MockMvc mvc;
@Before
public void setUp() throws Exception {
this.mvc = MockMvcBuilders
.webAppContextSetup(this.context)
.apply(springSecurity())
.build();
}
@Test
public void test() throws Exception {
this.mvc.perform(get("/mvc").with(hoge()));
}
}
Execution result
name = hoge
credentials = test-pass
authorities = BAR, FOO
principal = org.springframework.security.core.userdetails.User@30f425: Username: hoge; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: BAR,FOO
Description
MyMockUserPostProcessors.java
public static RequestPostProcessor hoge() {
return user("hoge")
.password("test-pass")
.authorities(AuthorityUtils.createAuthorityList("FOO", "BAR"));
}
--If you can call the definition of RequestPostProcessor
of frequently used settings with the static
method, it will be easier to reuse.
The method using annotations such as @WithMockUser
written at the beginning.
Compared to the method using RequestPostProcessor
, ** when dynamic change is not required ** annotations to methods and classes are sufficient, and I feel that it has a more declarative feature.
After that, it is possible to share the method of specifying mock users with tests other than MVC.
Implementation
MyMvcControllerTest.java
package sample.spring.security.test;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/test-applicationContext.xml")
@WebAppConfiguration
public class MyMvcControllerTest {
@Autowired
private WebApplicationContext context;
private MockMvc mvc;
@Before
public void setUp() throws Exception {
this.mvc = MockMvcBuilders
.webAppContextSetup(this.context)
.apply(springSecurity())
.build();
}
@Test
@WithMockUser(username="foo", password="test-pass", authorities={"FOO", "BAR"})
public void test() throws Exception {
this.mvc.perform(get("/mvc"));
}
}
Execution result
name = foo
credentials = test-pass
authorities = BAR, FOO
principal = org.springframework.security.core.userdetails.User@18cc6: Username: foo; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: BAR,FOO
Description
--Since it is the same as how to use it when it is not MVC, there is nothing special to mention.
Implementation
test-applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:sec="http://www.springframework.org/schema/security"
...>
<bean class="sample.spring.security.control.MyMvcController" />
<sec:http>
<sec:intercept-url pattern="/login" access="permitAll" />
<sec:intercept-url pattern="/**" access="isAuthenticated()" />
<sec:form-login />
<sec:logout />
</sec:http>
<sec:authentication-manager>
<sec:authentication-provider>
<sec:user-service>
<sec:user name="user" password="password" authorities="FOO" />
</sec:user-service>
</sec:authentication-provider>
</sec:authentication-manager>
</beans>
MyMvcControllerTest.java
package sample.spring.security.test;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.*;
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/test-applicationContext.xml")
@WebAppConfiguration
public class MyMvcControllerTest {
@Autowired
private WebApplicationContext context;
private MockMvc mvc;
@Before
public void setUp() throws Exception {
this.mvc = MockMvcBuilders
.webAppContextSetup(this.context)
.apply(springSecurity())
.build();
}
@Test
public void test() throws Exception {
MvcResult result = this.mvc.perform(formLogin()).andReturn();
this.printResponse("test", result);
}
private void printResponse(String method, MvcResult result) throws Exception {
MockHttpServletResponse response = result.getResponse();
int status = response.getStatus();
String location = response.getHeader("Location");
System.out.println("[" + method + "]\n" +
"status : " + status + "\n" +
"location : " + location;
}
}
Execution result
[test]
status : 302
location : /
--Successfully logged in and skipped to the root /
which is the default transition destination after login
Description
test-applicationContext.xml
<sec:user name="user" password="password1" authorities="FOO" />
--The user must actually exist, as the normal logic is executed in the Form login.
MyMvcControllerTest.java
@Test
public void test() throws Exception {
MvcResult result = this.mvc.perform(formLogin()).andReturn();
this.printResponse("test", result);
}
--Form login is executed by setting SecurityMockMvcRequestBuilders.formLogin ()
to perform ()
--By default, the following request is executed
--Request path: / login
--Method: POST
--Username: ʻuser --Password:
password --Username parameter name: ʻusername
--Password parameter name: password
Implementation
test-applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:sec="http://www.springframework.org/schema/security"
...>
<bean class="sample.spring.security.control.MyMvcController" />
<sec:http>
<sec:intercept-url pattern="/do-login" access="permitAll" />
<sec:intercept-url pattern="/**" access="isAuthenticated()" />
<sec:form-login login-processing-url="/do-login"
username-parameter="login-id"
password-parameter="pass" />
<sec:logout />
</sec:http>
<sec:authentication-manager>
<sec:authentication-provider>
<sec:user-service>
<sec:user name="user" password="password" authorities="FOO" />
</sec:user-service>
</sec:authentication-provider>
</sec:authentication-manager>
</beans>
--Set login URL and user name / password parameter names to different values from the default
MyMvcControllerTest.java
package sample.spring.security.test;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.*;
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/test-applicationContext.xml")
@WebAppConfiguration
public class MyMvcControllerTest {
@Autowired
private WebApplicationContext context;
private MockMvc mvc;
@Before
public void setUp() throws Exception {
this.mvc = MockMvcBuilders
.webAppContextSetup(this.context)
.apply(springSecurity())
.build();
}
@Test
public void test() throws Exception {
MvcResult result = this.mvc.perform(
formLogin("/do-login")
.userParameter("login-id")
.passwordParam("pass")
.user("user")
.password("password")
).andReturn();
this.printResponse("test", result);
}
private void printResponse(String method, MvcResult result) throws Exception {
...
}
}
Execution result
[test]
status : 302
location : /
Description
MyMvcControllerTest.java
@Test
public void test() throws Exception {
MvcResult result = this.mvc.perform(
formLogin("/do-login")
.userParameter("login-id")
.passwordParam("pass")
.user("user")
.password("password")
).andReturn();
this.printResponse("test", result);
}
--If the login process path is different from the default (/ login
), you can specify the path with the argument offormLogin ()
.
--Can also be specified with loginProcessingUrl ()
--If the parameter name of the user name is different from the default (ʻusername), you can specify it with ʻuserParameter ()
.
--If the password parameter name is different from the default (password
), you can specify it withpasswordParam ()
.
Implementation
MyMvcControllerTest.java
package sample.spring.security.test;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.*;
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/test-applicationContext.xml")
@WebAppConfiguration
public class MyMvcControllerTest {
@Autowired
private WebApplicationContext context;
private MockMvc mvc;
@Before
public void setUp() throws Exception {
this.mvc = MockMvcBuilders
.webAppContextSetup(this.context)
.apply(springSecurity())
.build();
}
@Test
public void test() throws Exception {
MvcResult result = this.mvc.perform(logout()).andReturn();
this.printResponse("test", result);
}
private void printResponse(String method, MvcResult result) throws Exception {
...
}
}
Execution result
[test]
status : 302
location : /login?logout
Description
--Passing SecurityMockMvcRequestBuilders.logout ()
to perform ()
will execute the logout request
--If the logout URL is different from the default (/ logout
), you can specify it as an argument likelogout ("/ other-logout-url")
Implementation
test-applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:sec="http://www.springframework.org/schema/security"
...>
<bean class="sample.spring.security.control.MyMvcController" />
<sec:http>
<sec:intercept-url pattern="/login" access="permitAll" />
<sec:intercept-url pattern="/**" access="isAuthenticated()" />
<sec:form-login />
<sec:logout />
</sec:http>
<sec:authentication-manager>
<sec:authentication-provider>
<sec:user-service>
<sec:user name="foo" password="foo" authorities="FOO, BAR" />
<sec:user name="fizz" password="fizz" authorities="ROLE_FIZZ, ROLE_BUZZ" />
</sec:user-service>
</sec:authentication-provider>
</sec:authentication-manager>
</beans>
--Set FOO
and BAR
privileges for the foo
user
--Set the roles of ROLE_FIZZ
and ROLE_BUZZ
for the fizz
user
MyMvcControllerTest.java
package sample.spring.security.test;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import java.util.List;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.*;
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.*;
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/test-applicationContext.xml")
@WebAppConfiguration
public class MyMvcControllerTest {
@Autowired
private WebApplicationContext context;
private MockMvc mvc;
@Before
public void setUp() throws Exception {
this.mvc = MockMvcBuilders
.webAppContextSetup(this.context)
.apply(springSecurity())
.build();
}
@Test
public void test_unauthenticated() throws Exception {
this.mvc.perform(formLogin().user("foo").password("invalid"))
.andExpect(unauthenticated());
}
@Test
public void test_authenticated() throws Exception {
this.mvc.perform(formLogin().user("foo").password("foo"))
.andExpect(authenticated());
}
@Test
public void test_withUsername() throws Exception {
this.mvc.perform(formLogin().user("foo").password("foo"))
.andExpect(authenticated().withUsername("foo"));
}
@Test
public void test_withAuthorities() throws Exception {
List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList("FOO", "BAR");
this.mvc.perform(formLogin().user("foo").password("foo"))
.andExpect(authenticated().withAuthorities(authorities));
}
@Test
public void test_combine() throws Exception {
List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList("FOO", "BAR");
this.mvc.perform(formLogin().user("foo").password("foo"))
.andExpect(authenticated().withUsername("foo").withAuthorities(authorities));
}
@Test
public void test_withRoles() throws Exception {
this.mvc.perform(formLogin().user("fizz").password("fizz"))
.andExpect(authenticated().withRoles("FIZZ", "BUZZ"));
}
}
Execution result
All tests succeed
Description
MyMvcControllerTest.java
@Test
public void test_unauthenticated() throws Exception {
this.mvc.perform(formLogin().user("foo").password("invalid"))
.andExpect(unauthenticated());
}
--You can verify the authentication result by using ʻandExpect ()after
perform (). --
SecurityMockMvcResultMatchers provides
static` methods and use them.
Method name | Verification content |
---|---|
unauthenticated() |
Verify that you are not authenticated |
authenticated() |
Verify that you are authenticated to this withUsername() Detailed verification is possible by continuing such as |
withUsername(String) |
Validate username |
withAuthorities(Collection<? extends GrantedAuthority>) |
Validate the granted authority |
withRoles(String...) |
Validate the assigned role |
MyMvcControllerTest.java
@Test
public void test_combine() throws Exception {
List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList("FOO", "BAR");
this.mvc.perform(formLogin().user("foo").password("foo"))
.andExpect(authenticated().withUsername("foo").withAuthorities(authorities));
}
--Multiple withUsername ()
and withAuthorities ()
can be combined
Recommended Posts