如何解决在 Spring 引导和 Spring 安全启用 CSRF 后无法正常工作的登录问题?

How can I fix the login issue which is not working after CSRF is enabed with Spring Boot and Spring Security?

我有一个 Spring Boot 应用程序。我正在使用 Spring 安全。当我尝试启用 CSRF 时,登录功能失败。它在启用 spring 安全性之前有效。请找到附件和我为启用 CSRF 所做的以下步骤。我该如何解决这个问题?

安全配置

http
    .authorizeRequests()
    .antMatchers("/ui/static/assets/**").permitAll()
     .antMatchers("/register","/forgotPassword").permitAll()
    .anyRequest().authenticated()
    .and()
    .formLogin()
    .loginPage("/login")
    .permitAll()
    .defaultSuccessUrl("/addDocument")
    .failureHandler(customAuthenticationFailureHandler)
    .and().exceptionHandling().accessDeniedPage("/Access_Denied")
    .and().logout().permitAll().invalidateHttpSession(true);

login.jsp

<form action="${pageContext.servletContext.contextPath}/login" class="form-horizontal" method="post" id="formLogin" data-parsley-validate="">
    <sec:csrfInput />
    <input class="form-control input-lg" type="email" name="username" id="username"  placeholder="Enter your email" data-parsley-required="true">
    <input class="form-control input-lg" type="password" id="pwd" name="password" placeholder="Enter your password" data-parsley-required="true">
    <button class="btn w-lg btn-rounded btn-lg btn-primary waves-effect waves-light" id="signInBtn" type="submit" value="Next" >Sign In
</form>

addDocument.jsp

<form:form method="POST" action="${pageContext.servletContext.contextPath}/submitDocument" id="fileUploadForm" enctype="multipart/form-data" modelAttribute="documentSignature">
    <form:hidden path="rewrite" value="true" />
    <sec:csrfInput/>

    <div class="form-group row">
        <label class="control-label col-md-3">Upload Document <span class="text-danger">*</span></label>
        <div class="controls col-md-9">
            <div class="form-group">
                  <input type="file" class="filestyle" id="fileUpload" name="file" data-buttonname="btn-primary" data-iconname="fa fa-upload">
            </div>
        </div>
    </div>

    <div class="form-group row">
         <label class="col-lg-12 control-label ">(*) Mandatory</label>
    </div>

    <div class="actions clearfix">
        <!-- <input class="btn btn-primary customButton" id="btnAddDocument" type="submit" value="Next" >
        <i class="fas fa-spinner fa-spin" id="loadingBtn" style="display:none;"></i>     -->
        <button class="btn customButton btn-primary waves-effect waves-light" id="btnAddDocument" type="submit" value="Next">Next
        <i class="fas fa-spinner fa-spin" id="loadingBtn" style="display:none;"></i></button>
    </div>
</form:form>

如果您要定义自定义登录页面,则:
loginPage("/showLoginPage") 中,您应该传递 url 以映射到您的控制器。并且您需要添加 loginProcessingUrl("/authenticate") 以提交请求。 (你可以看到 javadoc about FormLoginConfigurer.loginPage()
例子:你的可能 登录控制器

   @Controller
    public class LoginController {

        @GetMapping("/showLoginPage")
        public String showLoginPage() {
            return "login";  // your login jsp page
        }
    }

在安全会议中

protected void configure(HttpSecurity http) throws Exception {
http. 
...
.loginPage("/showLoginPage")
.loginProcessingUrl("/authenticate")

在 JSP 页中 login.jsp:

 <form:form action="${pageContext.request.contextPath}/authenticate" method="post">   
    <%-- authenticate=loginProcessingUrl-->
    ...  
</form:form>

如果启用 CSRF,您必须在要登录的页面中包含 _csrf.token 或 logout.In 您的 login.jsp 我没有看到被包括在内,这可能是导致问题的原因。

从官方文档来看,以下是两种添加CSRF的方式header:

在您的 JSP 代码中,您可以将其包含在您的表单中,例如:

If you are using Spring MVC form:form tag, the CsrfToken is automatically included for you using the CsrfRequestDataValueProcessor.

<c:url var="logoutUrl" value="/logout"/>
<form action="${logoutUrl}"
    method="post">
  <input type="submit"
    value="Log out" />
  <input type="hidden"
    name="${_csrf.parameterName}"
    value="${_csrf.token}"/>
</form

或者,如果您更喜欢对请求使用 Ajax,则可以手动将 CSRF header 添加到 Ajax 请求。在这种情况下,您可以将 CSRF 令牌作为元标记添加到 HTML 中,并使用 jQuery 或 JavaScript 从 html 中获取要添加到 Ajax请求。

<html>
  <head>
    <meta name="_csrf" content="${_csrf.token}"/>
    <!-- default header name is X-CSRF-TOKEN -->
    <meta name="_csrf_header" content="${_csrf.headerName}"/>
    ...
  </head>


$(function () {
    var token = $("meta[name='_csrf']").attr("content");
    var header = $("meta[name='_csrf_header']").attr("content");
    $(document).ajaxSend(function(e, xhr, options) {
        xhr.setRequestHeader(header, token);
    });
});

以上代码摘自the official documentation.