遵循 有关使用Spring Security Test为连接在Spring Security后面的 Spring MVC 应用程序编写测试的文档。
这是一个普通的 spring-boot 应用程序,采用典型的 spring-security 接线。这是主要的Application.java
@SpringBootApplication
public class Application {
private static final Logger log = LoggerFactory.getLogger(Application.class);
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
这是弹簧安全性的接线:
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
@Qualifier("customUserDetailsService")
UserDetailsService userDetailsService;
@Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/","/sign_up").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.csrf()
.and()
.exceptionHandling()
.accessDeniedPage("/access_denied")
.and()
.logout()
.permitAll();
}
}
如您所见,除了“/”和“/sign_up”之外的所有请求都需要进行身份验证。我通过部署应用程序验证了身份验证方案可以正常工作。
现在是有趣的部分:编写 spring mvc 测试。我提供的链接提供了一些编写此类测试的好方法,其中spring-security-test框架允许插入模拟用户/安全上下文。我采取的方法
- 定义一个模拟 UserDetails 接口,以及
- 使用 SecurityContextFactory 创建一个 SecurityContext,它
- 应该在测试启动期间插入到应用程序上下文中。
1.的代码如下:
@WithSecurityContext(factory=WithMockUserDetailsSecurityContextFactory.class)
public @interface WithMockUserDetails {
String firstName() default "apil";
String lastName() default "tamang";
String password() default "test";
long id() default 999;
String email() default "apil@test.com";
}
2.的代码如下:
final class WithMockUserDetailsSecurityContextFactory
implements WithSecurityContextFactory<WithMockUserDetails>{
@Override
public SecurityContext createSecurityContext(WithMockUserDetails mockUserDetails) {
/*
* Use an anonymous implementation for 'UserDetails' to return a
* mock authentication object, which is then set to the SecurityContext
* for the test runs.
*/
UserDetails principal=new UserDetails() {
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
//another anonmyous interface implementation.
GrantedAuthority auth=new GrantedAuthority() {
@Override
public String getAuthority() {
return "ROLE_USER";
}
};
List<GrantedAuthority> authorities=new ArrayList<>();
authorities.add(auth);
return authorities;
}
@Override
public String getPassword() {
return mockUserDetails.password();
}
@Override
public String getUsername() {
return mockUserDetails.email();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
};
Authentication authentication=new
UsernamePasswordAuthenticationToken(principal,principal.getPassword(),principal.getAuthorities());
SecurityContext context= SecurityContextHolder.createEmptyContext();
context.setAuthentication(authentication);
return context;
}
}
最后,这是测试类:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {Application.class})
@WebAppConfiguration
public class UserControllerTest {
@Autowired
private WebApplicationContext context;
@Autowired
private Filter springSecurityFilterChain;
private MockMvc mvc;
@Before
public void setup(){
mvc= MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
.build();
}
@Test
public void testRootIsOk() throws Exception {
mvc.perform(get("/"))
.andExpect(status().isOk());
}
@Test
public void expectRedirectToLogin() throws Exception {
mvc.perform(get("/testConnect"))
.andExpect(redirectedUrl("http://localhost/login"));
}
@Test
@WithMockUserDetails
public void testAuthenticationOkay() throws Exception {
mvc.perform(get("/testConnect"))
.andExpect(content().string("good request."));
}
}
测试运行的输出:
- 测试 1 通过。
- 测试 2 通过。
- 测试 3 失败。预期的输出,但得到了 <>。
很可能测试 3 失败,因为“SecurityContext”从未得到适当填充。根据文档,它应该可以工作。不知道我错过了什么。任何帮助深表感谢。