使用百里香叶的春季安全的简单示例

嗨,我正在尝试遵循一个简单的示例,关于做一个简单的登录表单页面,我在这个页面中找到http://docs.spring.io/autorepo/docs/spring-security/4.0.x/guides/form.html

问题是,每次我尝试登录时,我都会收到此错误,并收到此错误:Expected CSRF token not found. Has your session expired?

当我遇到此错误时,我按下资源管理器中的后退按钮,然后尝试第二次登录,当我这样做时,我得到这个错误:HTTP 403 - Invalid CSRF Token 'null' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN'

在教程页面中是以下消息:We use Thymeleaf to automatically add the CSRF token to our form. If we were not using Thymleaf or Spring MVCs taglib we could also manually add the CSRF token using <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>

“所以我也使用百里香酚,所以我没有将该标签添加到我的页面”

我找到了另一个解决方案,它的工作原理,这个解决方案正在将其添加到我的安全配置类中,这个解决方案可以工作,但我想这样做是在我的页面中禁用csrf保护,我不想禁用这种类型的保护。.csrf().disable()

这是我的安全配置类:

@Configuration
@EnableWebSecurity
public class ConfigSecurity extends WebSecurityConfigurerAdapter {

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth
            .inMemoryAuthentication()
                .withUser("user").password("password").roles("USER");
    }


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

        //.csrf().disable() is commented because i dont want disable this kind of protection 
        .authorizeRequests()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
            .logout()                                    
                .permitAll();
    }
}

我的安全首字母缩写 :

public class InitSecurity extends AbstractSecurityWebApplicationInitializer {

    public InicializarSecurity() {
        super(ConfigSecurity .class);

    }
}

我的 app-config 类,其中我有我的百里香叶配置

@EnableWebMvc
@ComponentScan(basePackages = {"com.myApp.R10"})
@Configuration
public class ConfigApp extends WebMvcConfigurerAdapter{

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/css/**").addResourceLocations("/css/**");
        registry.addResourceHandler("/img/**").addResourceLocations("/img/**");
        registry.addResourceHandler("/js/**").addResourceLocations("/js/**");
        registry.addResourceHandler("/sound/**").addResourceLocations("/sound/**");
        registry.addResourceHandler("/fonts/**").addResourceLocations("/fonts/**");
    }

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    @Bean
      public MessageSource messageSource() {
        ReloadableResourceBundleMessageSource messageSource = new       ReloadableResourceBundleMessageSource();
        messageSource.setBasenames("classpath:messages/messages");
        messageSource.setUseCodeAsDefaultMessage(true);
        messageSource.setDefaultEncoding("UTF-8");
        messageSource.setCacheSeconds(0);// # -1 : never reload, 0 always reload
        return messageSource;
    }
//  THYMELEAF

        @Bean 
        public ServletContextTemplateResolver templateResolver() {
            ServletContextTemplateResolver resolver = new ServletContextTemplateResolver();
            resolver.setPrefix("/WEB-INF/views/pagLogin/");
            resolver.setSuffix(".html");
            resolver.setTemplateMode("HTML5");
            resolver.setOrder(0);
            resolver.setCacheable(false);
            return resolver;
        }

        @Bean 
        public SpringTemplateEngine templateEngine() {
            SpringTemplateEngine engine  =  new SpringTemplateEngine();
            engine.setTemplateResolver( templateResolver() );
            engine.setMessageSource( messageSource() );



            return engine;
        }

        @Bean 
        public ThymeleafViewResolver thymeleafViewResolver() {
            ThymeleafViewResolver resolver  =  new ThymeleafViewResolver();

            resolver.setTemplateEngine( templateEngine() );
            resolver.setOrder(1);

            resolver.setCache( false );
            return resolver;
        }

        @Bean
        public SpringResourceTemplateResolver thymeleafSpringResource() {
            SpringResourceTemplateResolver vista = new SpringResourceTemplateResolver();
            vista.setTemplateMode("HTML5");
            return vista;
        }
}

我的应用配置初始值设定项

public class InicializarApp extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return null;
    }
@Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[] { ConfigApp .class };
    }

    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

    @Override
    protected Filter[] getServletFilters() {
        return new Filter[] { new HiddenHttpMethodFilter() };
    }
}

我的登录控制器类

@Controller
public class ControllerLogin {



    @RequestMapping(value = "/login", method = RequestMethod.GET)
    public String pageLogin(Model model) {



         return "login";
    }

我的家庭控制器类

@Controller
public class HomeController {

        @RequestMapping(value = "/", method = RequestMethod.GET)
        public String home(Model model) {


        return "home";
        }


}

我的登录名.html

<html xmlns:th="http://www.thymeleaf.org" xmlns:tiles="http://www.thymeleaf.org">
  <head>
    <title tiles:fragment="title">Messages : Create</title>
  </head>
  <body>
    <div tiles:fragment="content">
        <form name="f" th:action="@{/login}" method="post">               
            <fieldset>
                <legend>Please Login</legend>
                <div th:if="${param.error}" class="alert alert-error">    
                    Invalid username and password.
                </div>
                <div th:if="${param.logout}" class="alert alert-success"> 
                    You have been logged out.
                </div>

                <label for="username">Username</label>
                    <input type="text" id="username" name="username"/>        
                <label for="password">Password</label>
                    <input type="password" id="password" name="password"/>    

                <div class="form-actions">
                    <button type="submit" class="btn">Log in</button>
                </div>

                <!-- THIS IS COMMENTED it dont work beacuse i am already using thymeleaf <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>  -->


            </fieldset>
        </form>
    </div>


  </body>
</html>

我的家.html页面仅在我登录后显示,如果在我的安全配置类中放置.csrf().disable(),我登录的唯一方法,但我不想禁用该保护,如果我没有将其放在我的安全配置类中,我会得到我在这个问题开始时提到的错误。


答案 1

来自 Spring 安全文档

默认情况下,CSRF 保护在 Java 配置下处于启用状态。如果要禁用 CSRF,可以在下面看到相应的 Java 配置。请参阅 csrf() 的 Javadoc,了解有关 CSRF 保护配置方式的其他自定义。

并且,当启用 CSRF 保护时

最后一步是确保在所有 PATCH、POST、PUT 和 DELETE 方法中都包含 CSRF 令牌。

在您的情况下:

  • 默认情况下,您启用了 CSRF 保护(因为您使用的是 Java 配置),
  • 您正在使用 HTTP POST 提交登录表单,并且
  • 未在登录表单中包含 CSRF 令牌。因此,您的登录请求在提交时被拒绝,因为 CSRF 保护过滤器在传入请求中找不到 CSRF 令牌。

您已经确定了可能的解决方案:

  1. 禁用 CSRF 保护作为 ;或http.csrf().disable()
  2. 将 CSRF 令牌作为隐藏参数包含在登录表单中。

由于您使用的是 Thymeleaf,因此您必须在登录页面的 HTML 模板中执行以下操作:

<form name="f" th:action="@{/login}" method="post">               
  <fieldset>

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

    ...
  </fieldset>
</form>

请注意,您必须使用 th:action 而不是 HTML action,因为 Thymeleaf CSRF 处理器只会与前者一起使用。

您可以将表单提交方法更改为仅为了解决问题,但不建议这样做,因为用户将在表单中提交敏感信息。GET

我通常会创建一个 Thymeleaf 片段,然后将其用于具有表单的所有页面中,以便为包含 CSRF 令牌的表单生成标记。这减少了整个应用中的样板代码。


使用而不是启用带有百里香叶标签的CSRF令牌的自动注入。还可以代替Spring 3.2+和Thymeleaf 2.1+来强制Thymeleaf自动将CSRF令牌作为隐藏字段(来源Spring JIRA)。@EnableWebMvcSecurity@EnableWebSecurity<form th:action><form action>


答案 2

以下是完全按照OP想要的方式实现它的解决方案:

  1. 替换为@EnableWebMvcSecurity(这就是OP缺少的内容)@EnableWebSecurity
  2. 在标签上使用 th:操作<form>

当您使用Spring Security注册时,当您使用thymeleaf时,它的方法将额外的隐藏字段添加到表单中。CSRF是额外的隐藏字段。@EnableWebMvcSecurityCsrfRequestDataValueProcessorth:actiongetExtraHiddenFields

自Spring Security 4.0以来,@EnableWebMvcSecurity已被弃用,只需要@EnableWebSecurity。_csrf保护将继续自动应用