我有一个解决方法,似乎可以完全解决spring-boot:1.1.4,spring-
security:3.2.4和thymeleaf:2.1.3的问题(尽管有点麻烦)。
这是修改后的单元测试类:
@RunWith(SpringJUnit4ClassRunner.class)@SpringApplicationConfiguration(classes = Application.class)@WebAppConfigurationpublic class AppControllersTest { @Autowired public WebApplicationContext context; @Autowired private FilterChainProxy springSecurityFilter; private MockMvc mockMvc; @Before public void setup() { assertNotNull(context); assertNotNull(springSecurityFilter); // Process mock annotations MockitoAnnotations.initMocks(this); // Setup Spring test in webapp-mode (same config as spring-boot) this.mockMvc = MockMvcBuilders.webAppContextSetup(context) .addFilters(springSecurityFilter) .build(); context.getServletContext().setAttribute( WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, context); }...这里的魔力是将强制为
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE实际的Web应用程序上下文(我注入了该上下文)。这允许实际的sec:属性起作用,但是在我尝试设置权限以使用户登录未通过的第二次测试中(看起来用户仍然匿名)。
更新
缺少了一些东西(我认为这是弹簧安全性工作方式上的空白),但是很幸运,它很容易解决(尽管有点麻烦)。请参阅此以获取有关该问题的更多详细信息:SpringTest&Security:如何模拟身份验证?
我需要添加一个为测试创建模拟会话的方法。此方法将设置安全性
Principal/
Authentication并强制将/
SecurityContext插入
HttpSession其中,然后可以将其添加到测试请求中(请参见下面的测试代码段和
NamedOAuthPrincipal类示例)。
public MockHttpSession makeAuthSession(String username, String... roles) { if (StringUtils.isEmpty(username)) { username = "azeckoski"; } MockHttpSession session = new MockHttpSession(); session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, SecurityContextHolder.getContext()); Collection<GrantedAuthority> authorities = new HashSet<>(); if (roles != null && roles.length > 0) { for (String role : roles) { authorities.add(new SimpleGrantedAuthority(role)); } } //Authentication authToken = new UsernamePasswordAuthenticationToken("azeckoski", "password", authorities); // causes a NPE when it tries to access the Principal Principal principal = new NamedOAuthPrincipal(username, authorities, "key", "signature", "HMAC-SHA-1", "signaturebase", "token"); Authentication authToken = new UsernamePasswordAuthenticationToken(principal, null, authorities); SecurityContextHolder.getContext().setAuthentication(authToken); return session;}创建类的类
Principal(通过ConsumerCredentials支持OAuth)。如果不使用OAuth,则可以跳过ConsumerCredentials部分,而只需实现Principal(但应返回GrantedAuthority的集合)。
public static class NamedOAuthPrincipal extends ConsumerCredentials implements Principal { public String name; public Collection<GrantedAuthority> authorities; public NamedOAuthPrincipal(String name, Collection<GrantedAuthority> authorities, String consumerKey, String signature, String signatureMethod, String signaturebaseString, String token) { super(consumerKey, signature, signatureMethod, signaturebaseString, token); this.name = name; this.authorities = authorities; } @Override public String getName() { return name; } public Collection<? extends GrantedAuthority> getAuthorities() { return authorities; }}然后像这样修改测试(以创建会话,然后在模拟请求上进行设置):
@Testpublic void testLoadRootWithAuth() throws Exception { // Test basic home controller request with a session and logged in user MockHttpSession session = makeAuthSession("azeckoski", "ROLE_USER"); MvcResult result = this.mockMvc.perform(get("/").session(session)) .andExpect(status().isOk()) .andExpect(content().contentTypeCompatibleWith(MediaType.TEXT_HTML)) .andReturn(); String content = result.getResponse().getContentAsString(); assertNotNull(content); assertTrue(content.contains("Hello Spring Boot"));}


