Spring Security(三十五):Part III. Testing
This section describes the testing support provided by Spring Security.
To use the Spring Security test support, you must include spring-security-test-4.2.10.RELEASE.jar
as a dependency of your project.
11. Testing Method Security
This section demonstrates how to use Spring Security’s Test support to test method based security. We first introduce a MessageService
that requires the user to be authenticated in order to access it.
public class HelloMessageService implements MessageService { @PreAuthorize("authenticated")
public String getMessage() {
Authentication authentication = SecurityContextHolder.getContext()
.getAuthentication();
return "Hello " + authentication;
}
}
The result of getMessage
is a String saying "Hello" to the current Spring Security Authentication
. An example of the output is displayed below.
Hello org.springframework.security.authentication.UsernamePasswordAuthenticationToken@ca25360: Principal: org.springframework.security.core.userdetails.User@36ebcb: Username: user; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_USER
11.1 Security Test Setup
Before we can use Spring Security Test support, we must perform some setup. An example can be seen below:
@RunWith(SpringJUnit4ClassRunner.class) 1
@ContextConfiguration 2
public class WithMockUserTests {
This is a basic example of how to setup Spring Security Test. The highlights are:
1、@RunWith
instructs the spring-test module that it should create an ApplicationContext
. This is no different than using the existing Spring Test support. For additional information, refer to the Spring Reference2、@ContextConfiguration
instructs the spring-test the configuration to use to create the ApplicationContext
. Since no configuration is specified, the default configuration locations will be tried. This is no different than using the existing Spring Test support. For additional information, refer to the Spring ReferenceWithSecurityContextTestExecutionListener
which will ensure our tests are ran with the correct user. It does this by populating the SecurityContextHolder
prior to running our tests. After the test is done, it will clear out the SecurityContextHolder
. If you only need Spring Security related support, you can replace @ContextConfiguration
with @SecurityTestExecutionListeners
.@PreAuthorize
annotation to our HelloMessageService
and so it requires an authenticated user to invoke it. If we ran the following test, we would expect the following test will pass:@Test(expected = AuthenticationCredentialsNotFoundException.class)
public void getMessageUnauthenticated() {
messageService.getMessage();
}
11.2 @WithMockUser
The question is "How could we most easily run the test as a specific user?" The answer is to use @WithMockUser
. The following test will be run as a user with the username "user", the password "password", and the roles "ROLE_USER".
@Test
@WithMockUser
public void getMessageWithMockUser() {
String message = messageService.getMessage();
...
}
Specifically the following is true:
- The user with the username "user" does not have to exist since we are mocking the user
- 用户名为“user”的用户不必存在,因为我们正在模拟用户
- The
Authentication
that is populated in theSecurityContext
is of typeUsernamePasswordAuthenticationToken
- SecurityContext中填充的身份验证的类型为UsernamePasswordAuthenticationToken
- The principal on the
Authentication
is Spring Security’sUser
object - 身份验证的主体是Spring Security的User对象
- The
User
will have the username of "user", the password "password", and a singleGrantedAuthority
named "ROLE_USER" is used. - 用户将拥有“user”的用户名,密码“password”,并使用名为“ROLE_USER”的单个GrantedAuthority。
Our example is nice because we are able to leverage a lot of defaults. What if we wanted to run the test with a different username? The following test would run with the username "customUser". Again, the user does not need to actually exist.
@Test
@WithMockUser("customUsername")
public void getMessageWithMockUserCustomUsername() {
String message = messageService.getMessage();
...
}
We can also easily customize the roles. For example, this test will be invoked with the username "admin" and the roles "ROLE_USER" and "ROLE_ADMIN".
@Test
@WithMockUser(username="admin",roles={"USER","ADMIN"})
public void getMessageWithMockUserCustomUser() {
String message = messageService.getMessage();
...
}
If we do not want the value to automatically be prefixed with ROLE_ we can leverage the authorities attribute. For example, this test will be invoked with the username "admin" and the authorities "USER" and "ADMIN".
@Test
@WithMockUser(username = "admin", authorities = { "ADMIN", "USER" })
public void getMessageWithMockUserCustomAuthorities() {
String message = messageService.getMessage();
...
}
Of course it can be a bit tedious placing the annotation on every test method. Instead, we can place the annotation at the class level and every test will use the specified user. For example, the following would run every test with a user with the username "admin", the password "password", and the roles "ROLE_USER" and "ROLE_ADMIN".
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@WithMockUser(username="admin",roles={"USER","ADMIN"})
public class WithMockUserTests {
11.3 @WithAnonymousUser
Using @WithAnonymousUser
allows running as an anonymous user. This is especially convenient when you wish to run most of your tests with a specific user, but want to run a few tests as an anonymous user. For example, the following will run withMockUser1 and withMockUser2 using @WithMockUser and anonymous as an anonymous user.
@RunWith(SpringJUnit4ClassRunner.class)
@WithMockUser
public class WithUserClassLevelAuthenticationTests { @Test
public void withMockUser1() {
} @Test
public void withMockUser2() {
} @Test
@WithAnonymousUser
public void anonymous() throws Exception {
// override default to run as anonymous user
}
}
11.4 @WithUserDetails
While @WithMockUser
is a very convenient way to get started, it may not work in all instances. For example, it is common for applications to expect that the Authentication
principal be of a specific type. This is done so that the application can refer to the principal as the custom type and reduce coupling on Spring Security.
UserDetailsService
that returns an object that implements both UserDetails
and the custom type. For situations like this, it is useful to create the test user using the custom UserDetailsService
. That is exactly what @WithUserDetails
does.UserDetailsService
exposed as a bean, the following test will be invoked with an Authentication
of type UsernamePasswordAuthenticationToken
and a principal that is returned from the UserDetailsService
with the username of "user".@Test
@WithUserDetails
public void getMessageWithUserDetails() {
String message = messageService.getMessage();
...
}
We can also customize the username used to lookup the user from our UserDetailsService
. For example, this test would be executed with a principal that is returned from the UserDetailsService
with the username of "customUsername".
@Test
@WithUserDetails("customUsername")
public void getMessageWithUserDetailsCustomUsername() {
String message = messageService.getMessage();
...
}
We can also provide an explicit bean name to look up the UserDetailsService
. For example, this test would look up the username of "customUsername" using the UserDetailsService
with the bean name "myUserDetailsService".
@Test
@WithUserDetails(value="customUsername", userDetailsServiceBeanName="myUserDetailsService")
public void getMessageWithUserDetailsServiceBeanName() {
String message = messageService.getMessage();
...
}
Like @WithMockUser
we can also place our annotation at the class level so that every test uses the same user. However unlike @WithMockUser
, @WithUserDetails
requires the user to exist.
11.5 @WithSecurityContext
We have seen that @WithMockUser
is an excellent choice if we are not using a custom Authentication
principal. Next we discovered that @WithUserDetails
would allow us to use a custom UserDetailsService
to create our Authentication
principal but required the user to exist. We will now see an option that allows the most flexibility.
@WithSecurityContext
to create any SecurityContext
we want. For example, we might create an annotation named @WithMockCustomUser
as shown below:@Retention(RetentionPolicy.RUNTIME)
@WithSecurityContext(factory = WithMockCustomUserSecurityContextFactory.class)
public @interface WithMockCustomUser { String username() default "rob"; String name() default "Rob Winch";
}
You can see that @WithMockCustomUser
is annotated with the @WithSecurityContext
annotation. This is what signals to Spring Security Test support that we intend to create a SecurityContext
for the test. The @WithSecurityContext
annotation requires we specify a SecurityContextFactory
that will create a new SecurityContext
given our @WithMockCustomUser
annotation. You can find our WithMockCustomUserSecurityContextFactory
implementation below:
public class WithMockCustomUserSecurityContextFactory
implements WithSecurityContextFactory<WithMockCustomUser> {
@Override
public SecurityContext createSecurityContext(WithMockCustomUser customUser) {
SecurityContext context = SecurityContextHolder.createEmptyContext(); CustomUserDetails principal =
new CustomUserDetails(customUser.name(), customUser.username());
Authentication auth =
new UsernamePasswordAuthenticationToken(principal, "password", principal.getAuthorities());
context.setAuthentication(auth);
return context;
}
}
We can now annotate a test class or a test method with our new annotation and Spring Security’s WithSecurityContextTestExecutionListener
will ensure that our SecurityContext
is populated appropriately.
WithSecurityContextFactory
implementations, it is nice to know that they can be annotated with standard Spring annotations. For example, the WithUserDetailsSecurityContextFactory
uses the @Autowired
annotation to acquire the UserDetailsService
:final class WithUserDetailsSecurityContextFactory
implements WithSecurityContextFactory<WithUserDetails> { private UserDetailsService userDetailsService; @Autowired
public WithUserDetailsSecurityContextFactory(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
} public SecurityContext createSecurityContext(WithUserDetails withUser) {
String username = withUser.value();
Assert.hasLength(username, "value() must be non-empty String");
UserDetails principal = userDetailsService.loadUserByUsername(username);
Authentication authentication = new UsernamePasswordAuthenticationToken(principal, principal.getPassword(), principal.getAuthorities());
SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(authentication);
return context;
}
}
11.6 Test Meta Annotations
If you reuse the same user within your tests often, it is not ideal to have to repeatedly specify the attributes. For example, if there are many tests related to an administrative user with the username "admin" and the roles ROLE_USER
and ROLE_ADMIN
you would have to write:
@WithMockUser(username="admin",roles={"USER","ADMIN"})
Rather than repeating this everywhere, we can use a meta annotation. For example, we could create a meta annotation named WithMockAdmin
:
@Retention(RetentionPolicy.RUNTIME)
@WithMockUser(value="rob",roles="ADMIN")
public @interface WithMockAdmin { }
Now we can use @WithMockAdmin
in the same way as the more verbose @WithMockUser
.
Meta annotations work with any of the testing annotations described above. For example, this means we could create a meta annotation for @WithUserDetails("admin")
as well.
Spring Security(三十五):Part III. Testing的更多相关文章
- Spring Security(十五):5.6 Authentication
Thus far we have only taken a look at the most basic authentication configuration. Let’s take a look ...
- 精选Spring Boot三十五道必知必会知识点
Spring Boot 是微服务中最好的 Java 框架. 我们建议你能够成为一名 Spring Boot 的专家.本文精选了三十五个常见的Spring Boot知识点,祝你一臂之力! 问题一 Spr ...
- NeHe OpenGL教程 第三十五课:播放AVI
转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...
- spring boot / cloud (十五) 分布式调度中心进阶
spring boot / cloud (十五) 分布式调度中心进阶 在<spring boot / cloud (十) 使用quartz搭建调度中心>这篇文章中介绍了如何在spring ...
- JAVA之旅(三十五)——完结篇,终于把JAVA写完了,真感概呐!
JAVA之旅(三十五)--完结篇,终于把JAVA写完了,真感概呐! 这篇博文只是用来水经验的,写这个系列是因为我自己的java本身也不是特别好,所以重温了一下,但是手比较痒于是就写出了这三十多篇博客了 ...
- Java进阶(三十五)java int与integer的区别
Java进阶(三十五)java int与Integer的区别 前言 int与Integer的区别从大的方面来说就是基本数据类型与其包装类的区别: int 是基本类型,直接存数值,而Integer是对象 ...
- Gradle 1.12用户指南翻译——第三十五章. Sonar 插件
本文由CSDN博客万一博主翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...
- SQL注入之Sqli-labs系列第三十四关(基于宽字符逃逸POST注入)和三十五关
开始挑战第三十四关和第三十五关(Bypass add addslashes) 0x1查看源码 本关是post型的注入漏洞,同样的也是将post过来的内容进行了 ' \ 的处理. if(isset($_ ...
- “全栈2019”Java多线程第三十五章:如何获取线程被等待的时间?
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- Python进阶(三十五)-Fiddler命令行和HTTP断点调试
Python进阶(三十五)-Fiddler命令行和HTTP断点调试 一. Fiddler内置命令 上一节(使用Fiddler进行抓包分析)中,介绍到,在web session(与我们通常所说的se ...
随机推荐
- 简单的SQL注入
Topic Link http://ctf5.shiyanbar.com/423/web/ 1)测试输入1的时候正常显示 2) 单引号测试输入1'发现存在漏洞(判断为字符型注入) 3) 联合注入测试( ...
- Java开发知识之Java面相对象
Java开发知识之Java面相对象上 一丶什么是面相对象 了解什么什么是面相对象.那么首先要了解什么是面相过程. 面相过程的意思就是. 什么事情都亲力亲为. 比如上一讲的排序算法. 我们自己写的. 这 ...
- python算法-选择排序
核心算法:固定位置,选择元素,即:先从序列中,找到最小的元素,放在第一个位置,之后找到第二小的元素,放在第二个元素,以此类推,就可以完成整个排序工作了. 代码示例如下: x=[6,3,2,7,4,9, ...
- C# 通过进程名/进程Id 操作窗口/程序
1. 判断窗口是否存在 private bool IsWindowExist(IntPtr handle) { ) != IntPtr.Zero) && IsWindowVisible ...
- (1)Maven快速入门_1maven安装
Maven 快速入门 1.1 Maven官网下载 windows 系统 下载 下图红色框选处. 下载到本地解压, 创建一个本地maven仓库的目录 maven_lib 配置Maven的环境变量 ...
- 【.Net Core】获取绝对路径、相对路径
一.绝对路径 1.获取应用程序运行当前目录Directory.GetCurrentDirectory(). System.IO命名空间中存在Directory类,提供了获取应用程序运行当前目录的静态方 ...
- 如何使用纯CSS制作特效导航条?
先上张图,如何使用纯 CSS 制作如下效果? 在继续阅读下文之前,你可以先缓一缓.尝试思考一下上面的效果或者动手尝试一下,不借助 JS ,能否巧妙的实现上述效果. OK,继续.这个效果是我在业务开发的 ...
- PhpStorm代码编辑区竖线的用途以及如何去掉
相信经常用PhpStorm的童鞋都知道代码区有这么一条竖线,但是知道这个竖线究竟是干嘛的么? 相传是古时代的编辑器是没有自动折行的功能的,而且终端分辨率的问题,一行只能显示80个字符,因此很多上古时代 ...
- 动态BGP和静态BGP的含义与区别
1.在华为云上选购虚拟机时,会让用户选择动态BGP还是静态BGP, 全动态BGP可根据设定的寻路协议第一时间自动优化网络结构,以保持客户使用的网络持续稳定.高效. 静态BGP中的网络结构发生变化,运营 ...
- android中的相对路径
转载请标明出处:https://www.cnblogs.com/tangZH/p/9939655.html 1.同个文件夹访问 D:\Java\main\A.java D:\Java\main\B. ...