Thymeleaf的Spring Security简单示例

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

问题是我每次尝试登录时都会收到此错误我收到此错误: Expected CSRF token not found. Has your session expired? 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 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

“因为我也使用百里香,我没有把这个标签添加到我的页面”

我发现另一个解决方案,它的工作原理,这个解决方案是将此添加到我的安全配置类.csrf().disable()这个解决方案有效,但我想这样做是为了在我的页面中禁用csrf保护,我不想禁用这种保护。

这是我的security-config类:

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

我的app-config初始化程序

 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"; } } 

我的login.html

   Messages : Create   
Please Login
Invalid username and password.
You have been logged out.
<!-- THIS IS COMMENTED it dont work beacuse i am already using thymeleaf -->

我的home.html页面只显示我登录后,我可以登录的唯一方法是在我的安全配置类中放置.csrf()。disable()但我不想禁用该保护,如果我不放在我的安全配置类中,我得到了我在这个问题开头提到的错误。

来自Spring Security文档

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

并且,启用CSRF保护时

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

在你的情况下:

  • 您默认启用了CSRF保护(因为您使用的是Java配置),
  • 您正在使用HTTP POST提交登录表单
  • 不包括登录表单中的CSRF令牌。 因此,您的登录请求在提交时被拒绝,因为CSRF保护filter无法在传入请求中找到CSRF令牌。

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

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

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

 
...

请注意,您必须使用th:action而不是HTML action因为Thymeleaf CSRF处理器将仅使用前者启动。

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

我通常创建一个Thymeleaf片段,然后在所有带有表单的页面中使用它来为包含CSRF令牌的表单生成标记。 这减少了整个应用程序的样板代码。


使用@EnableWebMvcSecurity而不是@EnableWebSecurity来启用使用Thymeleaf标记自动注入CSRF令牌。 同时使用

而不是使用Spring 3.2+和Thymeleaf 2.1+的

来强制Thymeleaf自动将CSRF令牌包含为隐藏字段(源Spring JIRA )。

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

  1. @EnableWebSecurity替换@EnableWebSecurity (这就是缺少的OP)

  2. 标签上使用th:action

当您使用@EnableWebMvcSecurity Spring Security会注册CsrfRequestDataValueProcessor ,当您使用th:action thymeleaf会使用它的getExtraHiddenFields方法向getExtraHiddenFields添加额外的隐藏字段。 而csrf是额外的隐藏字段。

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

你需要添加Thymleaf的Spring Security Dialect。

1.)将Spring Security Dialect模块添加到类路径中。

Maven示例:

  org.thymeleaf.extras thymeleaf-extras-springsecurity3 2.1.2.RELEASE  

2.)将SpringSecurityDialect对象添加到SpringTemplateEngine

 import org.thymeleaf.extras.springsecurity3.dialect.SpringSecurityDialect; templateEngine.addDialect(new SpringSecurityDialect()); //add this line in your config 

资料来源: Spring in Action第4版

也许小的信息和平可以帮助任何人:也必须将forms归结为th:action 。 仅仅归因于纯HTML action将不起作用,并且不会自动添加隐藏的CSRF输入字段。

无法在任何地方找到信息的和平,并花了2小时的研究。 我将表单与action="#"一起归结,并通过java脚本设置相应的值。 在向表单添加th:action="@{#}"之前,未自动添加CSRF令牌输入字段。 现在充当魅力。

仅在添加以下内容后才为我工作:

 protected void configure(HttpSecurity http) throws Exception { ... http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()); ... }