2014-02-13 44 views
9

我有一個彈簧mvc(3.2.5)應用程序與彈簧安全(3.2)。爲什麼我收到MockMvc和JUnit的錯誤403?

我配置我SecurityConfig.class用這種方法:

@Override 
protected void configure(HttpSecurity http) throws Exception { 

    http.authorizeRequests().antMatchers("/*").permitAll().and() 
      .formLogin().successHandler(successHandler) 
      .defaultSuccessUrl("/") 
      .failureHandler(failureHandler).failureUrl("/login?error=true") 
      .permitAll().and().logout() 
      .permitAll(); 

    http.authorizeRequests().antMatchers("/resources/**").permitAll(); 

    http.authorizeRequests().antMatchers("/welcome").permitAll(); 

    http.authorizeRequests().antMatchers("/secure/*").authenticated(); 
    http.authorizeRequests().antMatchers("/admin/**").hasRole("ADMIN").anyRequest().authenticated(); 
} 

使用Spring Security(3.2),我啓用了CSRF。我認爲讓它啓用是一個好主意。

我控制器SignInController含有2種方法使用參數:

編輯:params中添加action=

@RequestMapping(value = "/signup") 
    public ModelAndView signup() { 

     boolean auth = SecurityContextHolder.getContext().getAuthentication() == null ? false 
       : SecurityContextHolder.getContext().getAuthentication() 
         .isAuthenticated() 
         && (SecurityContextHolder.getContext() 
           .getAuthentication().getPrincipal() instanceof User); 

     ModelAndView result = null; 

     if (auth) { 
      result = new ModelAndView("redirect:" + "/"); 
     } else { 
      UserForm user = new UserForm(); 
      result = new ModelAndView("registration", "userForm", user); 
     } 
     return result; 
    } 

    @RequestMapping(value = "/register", params = "action=signup") 
    public ModelAndView registration(
      @ModelAttribute(value = "userForm") @Valid UserForm userForm, 
      BindingResult result, HttpServletRequest request) { 

     if (result.hasErrors()) { 
      return new ModelAndView("registration"); 
     } 

     Member member = profileFacade.registerNewUser(userForm); 

     return new ModelAndView("registration", "member", member); 
    } 

    @RequestMapping(value = "/register", params = "action=cancel") 
    public ModelAndView cancelRegistration() { 
     return new ModelAndView("redirect:" + "/"); 
    } 

終於,我有JUnit測試:

@RunWith(SpringJUnit4ClassRunner.class) 
    @WebAppConfiguration 
    @ContextConfiguration(classes = { WebConfiguration.class, 
     JpaConfiguration.class, LoggingConfiguration.class, 
     SecurityConfig.class, DataSourceEmbeddedConfiguration.class, 
     DataSourceMySqlConfig.class, BaseValidatorConfiguration.class }) 
    @TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true) 
    @ActiveProfiles("dev") 
    public class SignInControllerTest { 

     @Autowired 
     private WebApplicationContext webApplicationContext; 
     @Autowired 
     private MockHttpSession session; 
     @Autowired 
     private MockHttpServletRequest request; 
     @Autowired 
     private FilterChainProxy springSecurityFilterChain; 

     private MockMvc mockMvc; 

     @Before 
     public void setUp() throws ServletException { 

      SecurityContextHolderAwareRequestFilter scharf = new SecurityContextHolderAwareRequestFilter(); 
      scharf.afterPropertiesSet(); 

      this.mockMvc = MockMvcBuilders 
        .webAppContextSetup(this.webApplicationContext) 
        .addFilters(springSecurityFilterChain).dispatchOptions(true).build(); 

      SecurityContextHolder.getContext().setAuthentication(null); 
     } 
     @Test 
     public void signup() throws Exception { 
      mockMvc.perform(get("/signup")).andExpect(status().isOk()) 
        .andExpect(model().attributeExists("userForm")); 
     } 

     @Test 
     @Transactional 
     @Rollback(true) 
     public void register() throws Exception { 

      UserForm form = new UserForm(); 
      form.setEmail("[email protected]"); 
      form.setUsername("aokije"); 
      form.setPassword("klo,ksff"); 
      form.setConfirmedPassword("klo,ksff"); 

      mockMvc.perform(post("/register").param("action", "signup")).andExpect(status().isOk()); 
     } 

    } 

編輯 :更新mockMvc.perform,因爲它工作正常第i個http.csrf().disable()在SecurityConfig.class

測試註冊運行完美,但寄存器返回一個錯誤403 我嘗試了很多東西,但我總是收到此錯誤。

當我在瀏覽器中嘗試http://localhost:8080/register?signup時,它工作正常。

_ 編輯 _

日誌:

2014-02-13 22:00:14,695 [ExpressionBasedFilterInvocationSecurityMetadataSource] processMap Adding web access control expression 'permitAll', for org.springframework.security.config.ann[email protected]52ee705c 
2014-02-13 22:00:14,696 [ExpressionBasedFilterInvocationSecurityMetadataSource] processMap Adding web access control expression 'permitAll', for org.springframework.security.config.ann[email protected]2412d28d 
2014-02-13 22:00:14,697 [ExpressionBasedFilterInvocationSecurityMetadataSource] processMap Adding web access control expression 'permitAll', for org.springframework.security.config.ann[email protected]4fbd397b 
2014-02-13 22:00:14,697 [ExpressionBasedFilterInvocationSecurityMetadataSource] processMap Adding web access control expression 'permitAll', for Ant [pattern='/logout'] 
2014-02-13 22:00:14,698 [ExpressionBasedFilterInvocationSecurityMetadataSource] processMap Adding web access control expression 'permitAll', for org.springframework.security.config.ann[email protected]1008e323 
2014-02-13 22:00:14,699 [ExpressionBasedFilterInvocationSecurityMetadataSource] processMap Adding web access control expression 'permitAll', for Ant [pattern='/*'] 
2014-02-13 22:00:14,700 [ExpressionBasedFilterInvocationSecurityMetadataSource] processMap Adding web access control expression 'permitAll', for Ant [pattern='/resources/**'] 
2014-02-13 22:00:14,700 [ExpressionBasedFilterInvocationSecurityMetadataSource] processMap Adding web access control expression 'permitAll', for Ant [pattern='/welcome'] 
2014-02-13 22:00:14,700 [ExpressionBasedFilterInvocationSecurityMetadataSource] processMap Adding web access control expression 'authenticated', for Ant [pattern='/secure/*'] 
2014-02-13 22:00:14,701 [ExpressionBasedFilterInvocationSecurityMetadataSource] processMap Adding web access control expression 'hasRole('ROLE_ADMIN')', for Ant [pattern='/admin/**'] 
2014-02-13 22:00:14,701 [ExpressionBasedFilterInvocationSecurityMetadataSource] processMap Adding web access control expression 'authenticated', for [email protected]1 
2014-02-13 22:00:14,703 [FilterSecurityInterceptor] afterPropertiesSet Validated configuration attributes 
2014-02-13 22:00:14,704 [FilterSecurityInterceptor] afterPropertiesSet Validated configuration attributes 
2014-02-13 22:00:14,734 [DefaultSecurityFilterChain] <init> Creating filter chain: [email protected]1, [org.springframework.secu[email protected]10174779, org.spring[email protected]68736a7e, [email protected]d0d, [email protected], org.[email protected]430e85e7, org.springframework.s[email protected]55eda087, org.springframework.[email protected]290c7ca, org.sp[email protected]6dd90afc, org.springframework.[email protected]12eb6a0f, org.springfram[email protected]6855612f, o[email protected]410a11a2, org[email protected]59e15580, org.springfr[email protected]2257a0] 
2014-02-13 22:00:14,859 [FilterChainProxy] doFilter /register at position 1 of 13 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter' 
2014-02-13 22:00:14,863 [FilterChainProxy] doFilter /register at position 2 of 13 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter' 
2014-02-13 22:00:14,863 [HttpSessionSecurityContextRepository] readSecurityContextFromSession HttpSession returned null object for SPRING_SECURITY_CONTEXT 
2014-02-13 22:00:14,863 [HttpSessionSecurityContextRepository] loadContext No SecurityContext was available from the HttpSession: [email protected] A new one will be created. 
2014-02-13 22:00:14,864 [FilterChainProxy] doFilter /register at position 3 of 13 in additional filter chain; firing Filter: 'HeaderWriterFilter' 
2014-02-13 22:00:14,865 [HstsHeaderWriter] writeHeaders Not injecting HSTS header since it did not match the requestMatcher org.springframework.se[email protected]5ab39e58 
2014-02-13 22:00:14,865 [FilterChainProxy] doFilter /register at position 4 of 13 in additional filter chain; firing Filter: 'CsrfFilter' 
2014-02-13 22:00:14,866 [CsrfFilter] doFilterInternal Invalid CSRF token found for http://localhost/register 
2014-02-13 22:00:14,866 [HttpSessionSecurityContextRepository] saveContext SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession. 
2014-02-13 22:00:14,866 [SecurityContextPersistenceFilter] doFilter SecurityContextHolder now cleared, as request processing completed 

你能幫助我嗎?

非常感謝

編輯

最後,我在另一個類(註釋)的錯誤。我解決這個問題:

HttpSessionCsrfTokenRepository httpSessionCsrfTokenRepository = new HttpSessionCsrfTokenRepository(); 
     CsrfToken csrfToken = httpSessionCsrfTokenRepository 
       .generateToken(request); 

     Map map = new HashMap(); 
     map.put("userForm", form); 
     map.put("org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository.CSRF_TOKEN", 
       csrfToken); 
     this.mockMvc 
       .perform(
         post("/register") 
           .param("signup", "") 
           .param("_csrf", csrfToken.getToken()) 
           .sessionAttrs(map)).andExpect(status().isOk()); 

params csrf和sessionAttrs是強制性的。

回答

4

發佈請求需要將csrf標記添加到表單中。所以,你必須通過它,而測試,代碼:(「它的工作原理在我的機器上的」 :))

HttpSessionCsrfTokenRepository httpSessionCsrfTokenRepository = new HttpSessionCsrfTokenRepository(); 
CsrfToken csrfToken = httpSessionCsrfTokenRepository.generateToken(new MockHttpServletRequest()); 
this.mockMvc.perform(
      post("yourpath") 
        .sessionAttr(TOKEN_ATTR_NAME, csrfToken)... 

第二件事:你確定註冊「?方法處理您的POST請求心不是RequestMapping配置爲」 GET 「 默認?(我可能是錯她)

+0

謝謝,但它不工作。 我試過這個:它不工作。我嘗試這樣做: 'mockMvc.perform(後( 「/註冊」)參數( 「行動」, 「註冊」)會議(會議).sessionAttr( 「_ CSRF」,csrfToken)。)andExpect( \t \t \t。 \t status()。isOk()); ' 我收到了同樣的錯誤。 如果停用CSRF與http.csrf()。禁止()在我的SecurityConfig,它工作正常(無.sessionAttr()) –

+0

嘗試使用 「org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository.CSRF_TOKEN 「; 改爲「_csrf」,忘記發帖了; ) – freakman

+1

我無法訪問常量CSRF_TOKEN。它不存在於課堂上。所有其他常量都是私有的。 –

18

我知道這個問題是很老了,但是這是谷歌的第一個結果爲某些查詢的一個,我相信這種做法要好得多,這是described on spring.io blog

1)您可以創建mockMvc使用Spring Security支持更容易,所以你setUp()變得更短:

@Before 
public void setUp() throws Exception { 
    mockMvc = MockMvcBuilders 
      .webAppContextSetup(webApplicationContext) 
      .apply(springSecurity()) 
      .build(); 
} 

2)您可以使用org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf()來填充正確的CSRF測試請求令牌是這樣的:

mockMvc.perform(post("/register") 
       .with(csrf()) 
       .param("action", "signup")) 
    .andExpect(status().isOk()); 
+1

真棒,只是救了我。當你在安全配置中有'http.csrf()。disable()'時,它是必需的。 –

相關問題