本文介绍了为什么我收到MockMvc和JUnit的错误403?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个带有弹簧安全性(3.2)的spring mvc(3.2.5)应用程序.

我使用以下方法配置了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安全性(3.2),我启用了CSRF.我认为启用它是一个好主意.

我的控制器SignInController包含2个带有params的方法:

EDIT :在参数中添加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@email.com");
            form.setUsername("aokije");
            form.setPassword("klo,ksff");
            form.setConfirmedPassword("klo,ksff");

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

    }

EDIT :由于其在SecurityConfig.class中的http.csrf().disable()正常工作,因此更新了mockMvc.perform.

测试 注册 运行完美,但 注册 返回错误403.我尝试了很多事情,但始终收到此错误.

当我在浏览器中尝试http://localhost:8080/register?signup时,它工作正常.

_ EDIT _

日志:

2014-02-13 22:00:14,695 [ExpressionBasedFilterInvocationSecurityMetadataSource] processMap Adding web access control expression 'permitAll', for org.springframework.security.config.annotation.web.configurers.PermitAllSupport$ExactUrlRequestMatcher@52ee705c 
2014-02-13 22:00:14,696 [ExpressionBasedFilterInvocationSecurityMetadataSource] processMap Adding web access control expression 'permitAll', for org.springframework.security.config.annotation.web.configurers.PermitAllSupport$ExactUrlRequestMatcher@2412d28d 
2014-02-13 22:00:14,697 [ExpressionBasedFilterInvocationSecurityMetadataSource] processMap Adding web access control expression 'permitAll', for org.springframework.security.config.annotation.web.configurers.PermitAllSupport$ExactUrlRequestMatcher@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.annotation.web.configurers.PermitAllSupport$ExactUrlRequestMatcher@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 org.springframework.security.web.util.matcher.AnyRequestMatcher@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: org.springframework.security.web.util.matcher.AnyRequestMatcher@1, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@10174779, org.springframework.security.web.context.SecurityContextPersistenceFilter@68736a7e, org.springframework.security.web.header.HeaderWriterFilter@728e5d0d, org.springframework.security.web.csrf.CsrfFilter@6e7a918b, org.springframework.security.web.authentication.logout.LogoutFilter@430e85e7, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@55eda087, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@290c7ca, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@6dd90afc, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@12eb6a0f, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@6855612f, org.springframework.security.web.session.SessionManagementFilter@410a11a2, org.springframework.security.web.access.ExceptionTranslationFilter@59e15580, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@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: org.springframework.mock.web.MockHttpSession@4c4b529f. 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.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@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());

参数csrf和sessionAttrs是必需的.

解决方案

发布请求需要将CSRF令牌添加到表单中.因此,您必须在测试时通过它,代码为:(它在我的机器上工作":))

 String TOKEN_ATTR_NAME = "org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository.CSRF_TOKEN";

// ...

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

this.mockMvc.perform(
                post("yourpath")
                    .sessionAttr(TOKEN_ATTR_NAME, csrfToken)
                    .param(csrfToken.getParamName(), csrfToken.getToken())...
 

第二件事:您确定注册"方法可以处理您的发帖请求吗?默认情况下,是否没有为"GET"配置RequestMapping?(我在这里可能是错误的)

I have a spring mvc (3.2.5) application with spring security (3.2).

I configured my SecurityConfig.class with this method :

@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();
} 

With Spring security (3.2) I have CSRF enabled. I think it is a good idea to let it enabled.

My controller SignInController contains 2 methods with params :

EDIT : adding action= in params

@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:" + "/");
    }

and finally, I have JUnit test :

@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@email.com");
            form.setUsername("aokije");
            form.setPassword("klo,ksff");
            form.setConfirmedPassword("klo,ksff");

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

    }

EDIT : update mockMvc.perform because it is working fine with http.csrf().disable() in SecurityConfig.class

Test signup run perfectly but register return an error 403.I tried a lot of things but I received always this error.

When I try http://localhost:8080/register?signup in a browser, it is working fine.

_EDIT_

Logs :

2014-02-13 22:00:14,695 [ExpressionBasedFilterInvocationSecurityMetadataSource] processMap Adding web access control expression 'permitAll', for org.springframework.security.config.annotation.web.configurers.PermitAllSupport$ExactUrlRequestMatcher@52ee705c 
2014-02-13 22:00:14,696 [ExpressionBasedFilterInvocationSecurityMetadataSource] processMap Adding web access control expression 'permitAll', for org.springframework.security.config.annotation.web.configurers.PermitAllSupport$ExactUrlRequestMatcher@2412d28d 
2014-02-13 22:00:14,697 [ExpressionBasedFilterInvocationSecurityMetadataSource] processMap Adding web access control expression 'permitAll', for org.springframework.security.config.annotation.web.configurers.PermitAllSupport$ExactUrlRequestMatcher@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.annotation.web.configurers.PermitAllSupport$ExactUrlRequestMatcher@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 org.springframework.security.web.util.matcher.AnyRequestMatcher@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: org.springframework.security.web.util.matcher.AnyRequestMatcher@1, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@10174779, org.springframework.security.web.context.SecurityContextPersistenceFilter@68736a7e, org.springframework.security.web.header.HeaderWriterFilter@728e5d0d, org.springframework.security.web.csrf.CsrfFilter@6e7a918b, org.springframework.security.web.authentication.logout.LogoutFilter@430e85e7, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@55eda087, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@290c7ca, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@6dd90afc, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@12eb6a0f, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@6855612f, org.springframework.security.web.session.SessionManagementFilter@410a11a2, org.springframework.security.web.access.ExceptionTranslationFilter@59e15580, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@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: org.springframework.mock.web.MockHttpSession@4c4b529f. 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.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@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 

Could you help me ?

Thanks a lot

EDIT

Finally, I had a bug in another class (annotation). I fix with this :

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 and sessionAttrs are mandatory.

解决方案

Post requests need the CSRF token to be added to the form. So you have to pass it while testing, code: ("it works on my machine" :))

String TOKEN_ATTR_NAME = "org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository.CSRF_TOKEN";

// ...

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

this.mockMvc.perform(
                post("yourpath")
                    .sessionAttr(TOKEN_ATTR_NAME, csrfToken)
                    .param(csrfToken.getParamName(), csrfToken.getToken())...

2nd thing: are you sure that registration" method handles your post request? Isn't RequestMapping configured for "GET" by default? (I may be wrong here)

这篇关于为什么我收到MockMvc和JUnit的错误403?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-20 05:42