Thymeleaf的Spring Security简单示例

时间:2014-09-05 19:27:57

标签: java security spring-mvc spring-security thymeleaf

您好我正在尝试按照我在此页面中找到一个简单的登录表单页面的简单示例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().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

<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>

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

5 个答案:

答案 0 :(得分:39)

来自Spring Security documentation

  

默认情况下,使用Java配置启用CSRF保护。如果你   想要禁用CSRF,相应的Java配置即可   见下文。有关其他信息,请参阅csrf()的Javadoc   有关如何配置CSRF保护的自定义。

并且,当启用CSRF保护时

  

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

在你的情况下:

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

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

  1. 禁用CSRF保护为http.csrf().disable();或
  2. 在登录表单中包含CSRF令牌作为隐藏参数。
  3. 由于您使用的是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处理器只能使用前者启动。< / p>

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

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


    使用@EnableWebMvcSecurity代替@EnableWebSecurity启用自动注入带有Thymeleaf标记的CSRF令牌。同时使用<form th:action>代替<form action>使用Spring 3.2+和Thymeleaf 2.1+来强制Thymeleaf自动将CSRF令牌包含为隐藏字段(来源Spring JIRA)。

答案 1 :(得分:15)

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

  1. @EnableWebSecurity替换为@EnableWebMvcSecurity(这就是缺少的OP)
  2. <form>代码
  3. 上使用th:action

    当您使用@EnableWebMvcSecurity Spring Security注册CsrfRequestDataValueProcessor时,当您使用th:action时,百万美分使用它的getExtraHiddenFields方法向表单添加额外的隐藏字段。并且csrf是额外的隐藏字段。

    Since Spring Security 4.0,已弃用@EnableWebMvcSecurity,只需要@EnableWebSecurity。 The _csrf protection continues to apply automatically

答案 2 :(得分:3)

您需要添加Thymleaf的Spring Security Dialect。

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

Maven示例:

<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity3</artifactId>
    <version>2.1.2.RELEASE</version>
</dependency>

2.)将SpringSecurityDialect对象添加到SpringTemplateEngine

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

来源:Spring in Action 4th Edition

答案 3 :(得分:1)

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

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

答案 4 :(得分:0)

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

protected void configure(HttpSecurity http) throws Exception {
    ...

    http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
    ...
}