2015-02-11 224 views
3

我使用Freemarker与Spring Boot并进行mvc单元测试。在我的freemarker模板我对CSRF令牌这样一个隐藏的输入字段:Spring Boot,Freemarker,MVC单元测试,Csrf

<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/> 

然后我也有一个MVC单元测试:

 @RunWith(SpringJUnit4ClassRunner.class) 
@SpringApplicationConfiguration(classes = MyApplication.class) 
@WebAppConfiguration 
@ActiveProfiles("test") 
public class MvcTests { 

    @Autowired 
    WebApplicationContext ctx; 

    MockMvc mvc; 

    HttpSessionCsrfTokenRepository httpSessionCsrfTokenRepository; 

    @Before 
    public void setUp() throws Exception { 
     mvc = webAppContextSetup(ctx).build(); 
     httpSessionCsrfTokenRepository = new HttpSessionCsrfTokenRepository(); 
    } 


    @Test 
    public void testInValidVoucherNumber() throws Exception { 
     CsrfToken csrfToken = httpSessionCsrfTokenRepository.generateToken(new MockHttpServletRequest()); 
     mvc.perform(post("/voucher/claim") 
       .sessionAttr("_csrf.parameterName", "_csrf") 
       .sessionAttr("_csrf.token", csrfToken.getToken()) 
       .contentType(MediaType.APPLICATION_FORM_URLENCODED) 
       .param("code", "ABC") 
       .param("_csrf", csrfToken.getToken())) 
       .andExpect(status().isOk()) 
       .andExpect(view().name("claim-voucher")) 
       .andExpect(content().string(containsString("Code is invalid"))); 
    } 
} 

当我运行单元测试,我得到以下的freemarker错误:

testInValidVoucherNumber(MvcTests) Time elapsed: 0.904 sec <<< ERROR! 
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is freemarker.core.InvalidReferenceException: The following has evaluated to null or missing: 
==> _csrf [in template "claim-voucher.ftl" at line 25, column 34] 

---- 
Tip: If the failing expression is known to be legally refer to something that's null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing</#if>. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)?? 
---- 

---- 
FTL stack trace ("~" means nesting-related): 
    - Failed at: ${_csrf.parameterName} [in template "claim-voucher.ftl" at line 25, column 32] 
    ~ Reached through: #nested [in template "layout.ftl" in macro "basic" at line 16, column 9] 

这里是控制器:

@RequestMapping(value = "/voucher/claim", method = RequestMethod.POST) 
public String checkVoucherCode(@Valid ClaimVoucherForm claimVoucherForm, Model model) { 
    //Business logic 
} 

似乎freemarker无法访问csrf parameterName和csrf标记。有没有办法在不改变控制器的情况下包含csrf信息,或者单元测试是否存在问题?

回答

0

相反的:

.sessionAttr("_csrf.parameterName", "_csrf") 
.sessionAttr("_csrf.token", csrfToken.getToken()) 

尝试:

.sessionAttr("_csrf", csrfToken) 

当你写${_csrf.parameterName},FreeMarker的外观为模型变量“_csrf”上的字段“parameterName”。不适用于模型变量“_csrf.parameterName”。

也就是说,奇怪的是,您必须在测试中将这些参数添加为会话属性。 Spring Security的CsrfFilter不应该为你做这件事吗?

+0

这很奇怪,我之前使用过thymeleaf,而且我不必将csrf标记添加到html中,然后我切换到Freemarker,因为thymeleaf给了我一些问题。从那时起,我不得不添加csrf标签,否则在将表单发布到服务器时会出现错误。 – 2015-02-18 07:58:55

+0

这是因为Spring的JSP/Thymeleaf窗体标签自动为您添加隐藏字段:https://github.com/spring-projects/spring-framework/blob/6062e15572eefa73954a69c109cc90f068486065/spring-webmvc/src/main/java/org/springframework/ web/servlet/tags/form/FormTag.java#L484-L512和https:// github。com/thymeleaf/thymeleaf-spring/blob/2e752f705e7be87ef7d077d90ff822d248d9033b/thymeleaf-spring4/src/main/java/org/thymeleaf/spring4/processor/attr/SpringActionAttrProcessor.java#L96-L122。 – 2015-02-18 09:24:58

+0

谢谢你的工作! – 2015-02-18 14:57:51

1

会话属性在模型中可用,前缀为Session.。您的模板需要更新为如下所示:

<input type="hidden" name="${Session._csrf.parameterName}" value="${Session._csrf.token}"/> 

您还在测试中错误地配置会话属性。 _csrf.token表示Freemarker将查找名为_csrf的对象,然后在该对象上查找名为token的属性。你需要指定一个名为_csrf包含parameterNametoken一个会话属性:

Map<String, Object> csrf = new HashMap<String, Object>(); 
csrf.put("parameterName", "_csrf"); 
csrf.put("token", csrfToken.getToken()); 
mvc.perform(post("/voucher/claim") 
      .sessionAttr("_csrf", csrf) 
+0

没有工作我得到了以下错误: – 2015-02-13 12:20:14

+0

freemarker.core.InvalidReferenceException:以下评估为空或丢失: ==> Session._csrf [在模板“claim-voucher.ftl”在第31行,列34] – 2015-02-13 12:20:51

0

我解决了使用

@Import({CsrfFilter.class, HttpSessionCsrfTokenRepository.class}) 

在我的测试类的顶部,而无需使用任何.sessionAttr()调用了同样的问题。