首页 > 解决方案 > 如何在 Spring Boot 中为包含匿名用户表单的任何页面处理会话创建和添加隐藏的输入 csrf 令牌?

问题描述

我介绍问题:

当我启动应用程序并输入网址“/home”时。主页显示但不正确(模板组织得不好),我收到异常TemplateInputException。过了一会儿,如果我刷新主页和其他页面,它会恢复正常,但如果我转到“/login”,然后我注销会将我重定向到主页视图,同样的问题会再次出现。

Stacktrace 控制台:

org.thymeleaf.exceptions.TemplateInputException:模板解析时出错(模板:“类路径资源[templates/home.html]”)...

原因:org.attoparser.ParseException:在 org.attoparser.MarkupParser.parseDocument(MarkupParser.java :393) ~[attoparser-2.0.4.RELEASE.jar:2.0.4.RELEASE]...

原因:org.thymeleaf.exceptions.TemplateProcessingException:执行处理器“org.thymeleaf.spring4.processor.SpringActionTagProcessor”时出错(模板:“home” - 第 2494 行,第 10 列)

原因:java.lang.IllegalStateException:在 org.apache.catalina.connector.Request.doGetSession(Request.java:2995) ~[tomcat-embed-core-8.5.14.罐子:8.5.14]

...

在 org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository.saveToken(HttpSessionCsrfTokenRepository.java:63) ~[spring-security-web-4.2.0.RELEASE.jar:4.2.0.RELEASE] 在 org.springframework.security。 web.csrf.LazyCsrfTokenRepository$SaveOnAccessCsrfToken.saveTokenIfNecessary(LazyCsrfTokenRepository.java:176) ~[spring-security-web-4.2.0.RELEASE.jar:4.2.0.RELEASE] at org.springframework.security.web.csrf.LazyCsrfTokenRepository $SaveOnAccessCsrfToken.getToken(LazyCsrfTokenRepository.java:128) ~[spring-security-web-4.2.0.RELEASE.jar:4.2.0.RELEASE] at org.springframework.security.web.servlet.support.csrf.CsrfRequestDataValueProcessor。getExtraHiddenFields (CsrfRequestDataValueProcessor.java:71) ~[spring-security-web-4.2.0.RELEASE.jar:4.2.0.RELEASE] ...

代码来源:

问题出在 home.html 的联系表中,这一行th:action="@{/home/contact}" th:object="${mailForm}"::

<form id="contact-form" method="post" action="/home/contact}" 
    th:action="@{/home/contact}" th:object="${mailForm}"
    role="form">
    <!-- <input type="hidden" name="${_csrf.parameterName}" 
    value="${_csrf.token}" /> -->
    <input type="text" name="senderName" th:field="*{senderName}"> 
    <input type="text" name="senderLastName" th:field="*{senderLastName}">  
    <input type="email" name="senderEmail" th:field="*{senderEmail}">            
    <textarea name="message" th:field="*{message}"></textarea>
    <button type="submit">Send Message</button>
</form>

我认为这是 csrf 令牌的问题。我试图<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />在我的表单中添加这一行,并且默认情况下 Spring Security 启用了 csrf 保护,但它不起作用。

调用服务发送邮件的Controller:

@Controller
public class HomeController {
    @Autowired
    private EmailService emailService;
    @Autowired
    private MailValidator mailValidator;
    
    // some other code like @InitBinder methode ...
    
    // method to post mailForm
    @PostMapping("/home/contact")
    public String contactUsHome(@Valid @ModelAttribute("mailForm") final MailForm mailForm, BindingResult bindingResult)
            throws MessagingException {

        if (bindingResult.hasErrors()) {
            return HOME_VIEW;
        } else {
            mailForm.setRecipientEmail(recipientEmail);
            Mail mail = DTOUtil.map(mailForm, Mail.class);
            emailService.sendSimpleMail(mail);
            return REDIRECT_HOME_VIEW;
        }
    }
}

标签: springspring-mvcspring-bootspring-securitythymeleaf

解决方案


这是解决“无法创建会话和 CSRF 令牌”问题的方法。

在 spring 安全配置类中,我刚刚添加了这一行.and().csrf().csrfTokenRepository(..),一切正常。

@Override
protected void configure(HttpSecurity http) throws Exception {
     http.authorizeRequests()
     //some another line of code...
     .and().csrf().csrfTokenRepository(new HttpSessionCsrfTokenRepository())
}

推荐阅读