2015-02-11 219 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()調用了同樣的問題。