Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/spring/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/spring-mvc/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在SpringSecurity3.2.0.RELEASE中,如何在一个纯HTML的页面中获取CSRF令牌,而不使用标记库_Spring_Spring Mvc_Spring Security - Fatal编程技术网

在SpringSecurity3.2.0.RELEASE中,如何在一个纯HTML的页面中获取CSRF令牌,而不使用标记库

在SpringSecurity3.2.0.RELEASE中,如何在一个纯HTML的页面中获取CSRF令牌,而不使用标记库,spring,spring-mvc,spring-security,Spring,Spring Mvc,Spring Security,今天,我从SpringSecurity3.1.4升级到了新的3.2.0版本,其中包含java配置。CSRF在默认情况下是打开的,我知道我可以在覆盖的configure方法中使用“http.CSRF().disable()”禁用它。但是,假设我不想禁用它,但我需要在登录页面上使用CSRF令牌,其中没有使用JSP标记库或Spring标记库 我的登录页面纯粹是HTML,我在我使用Yeoman生成的主干应用程序中使用它。我如何将HttpSession中包含的CSRF令牌包含在表单中或作为头中,这样我就不

今天,我从SpringSecurity3.1.4升级到了新的3.2.0版本,其中包含java配置。CSRF在默认情况下是打开的,我知道我可以在覆盖的configure方法中使用“http.CSRF().disable()”禁用它。但是,假设我不想禁用它,但我需要在登录页面上使用CSRF令牌,其中没有使用JSP标记库或Spring标记库


我的登录页面纯粹是HTML,我在我使用Yeoman生成的主干应用程序中使用它。我如何将HttpSession中包含的CSRF令牌包含在表单中或作为头中,这样我就不会得到“未找到预期的CSRF令牌。您的会话是否已过期?”异常?

您可以使用中所述的名为_CSRF的请求属性获取CSRF。要将CSRF添加到HTML页面,需要使用JavaScript获取需要包含在请求中的令牌

将令牌作为头返回比将其作为JSON返回正文更安全,因为正文中的JSON可以由外部域获取。例如,您的JavaScript可以请求由以下程序处理的URL:

CsrfToken token = (CsrfToken) request.getAttribute("_csrf");
// Spring Security will allow the Token to be included in this header name
response.setHeader("X-CSRF-HEADER", token.getHeaderName());
// Spring Security will allow the token to be included in this parameter name
response.setHeader("X-CSRF-PARAM", token.getParameterName());
// this is the value of the token to be included as either a header or an HTTP parameter
response.setHeader("X-CSRF-TOKEN", token.getToken());

然后,JavaScript将从响应头中获取头名称或参数名称以及令牌,并将其添加到登录请求中。

尽管@rob winch是正确的,但我建议从会话中获取令牌。如果Spring Security使用
CsrfAuthenticationStrategy
SessionManagementFilter
中生成新令牌,它会将其设置为Session,但不会根据请求进行设置。因此,您可能最终使用错误的csrf令牌

public static final String DEFAULT_CSRF_TOKEN_ATTR_NAME = HttpSessionCsrfTokenRepository.class.getName().concat(".CSRF_TOKEN");
CsrfToken sessionToken = (CsrfToken) request.getSession().getAttribute(DEFAULT_CSRF_TOKEN_ATTR_NAME);
注意:我正在使用CORS和AngularJS

注意²:我发现保持AngularJS处理CSRF的方式会很有趣

我没有使用基于答案的方法(特别是@Rob Winch的方法),而是使用了中描述的方法

除此之外,我还必须添加
访问控制允许头:…,X-CSRF-TOKEN
(由于CORS)

实际上,我发现这个方法比向响应中添加头更干净

代码如下:

HttpHeaderFilter.java

@Component("httpHeaderFilter")
public class HttpHeaderFilter extends OncePerRequestFilter {
    @Autowired
    private List<HttpHeaderProvider> providerList;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        providerList.forEach(e -> e.filter(request, response));

        if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) {
            response.setStatus(HttpStatus.OK.value());
        }
        else {
            filterChain.doFilter(request, response);
        }
    }
}
public interface HttpHeaderProvider {
    void filter(HttpServletRequest request, HttpServletResponse response);
}
@Component
public class CsrfHttpHeaderProvider implements HttpHeaderProvider {
    @Override
    public void filter(HttpServletRequest request, HttpServletResponse response) {
        response.addHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, "X-CSRF-TOKEN");
    }
}
@Component("csrfTokenFilter")
public class CsrfTokenFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        CsrfToken csrf = (CsrfToken)request.getAttribute(CsrfToken.class.getName());

        if (csrf != null) {
            Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");

            String token = csrf.getToken();

            if (cookie == null || token != null && !token.equals(cookie.getValue())) {
                cookie = new Cookie("XSRF-TOKEN", token);
                cookie.setPath("/");

                response.addCookie(cookie);
            }
        }

        filterChain.doFilter(request, response);
    }
}
CsrfHttpHeaderProvider.java

@Component("httpHeaderFilter")
public class HttpHeaderFilter extends OncePerRequestFilter {
    @Autowired
    private List<HttpHeaderProvider> providerList;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        providerList.forEach(e -> e.filter(request, response));

        if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) {
            response.setStatus(HttpStatus.OK.value());
        }
        else {
            filterChain.doFilter(request, response);
        }
    }
}
public interface HttpHeaderProvider {
    void filter(HttpServletRequest request, HttpServletResponse response);
}
@Component
public class CsrfHttpHeaderProvider implements HttpHeaderProvider {
    @Override
    public void filter(HttpServletRequest request, HttpServletResponse response) {
        response.addHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, "X-CSRF-TOKEN");
    }
}
@Component("csrfTokenFilter")
public class CsrfTokenFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        CsrfToken csrf = (CsrfToken)request.getAttribute(CsrfToken.class.getName());

        if (csrf != null) {
            Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");

            String token = csrf.getToken();

            if (cookie == null || token != null && !token.equals(cookie.getValue())) {
                cookie = new Cookie("XSRF-TOKEN", token);
                cookie.setPath("/");

                response.addCookie(cookie);
            }
        }

        filterChain.doFilter(request, response);
    }
}
CsrfTokenFilter.java

@Component("httpHeaderFilter")
public class HttpHeaderFilter extends OncePerRequestFilter {
    @Autowired
    private List<HttpHeaderProvider> providerList;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        providerList.forEach(e -> e.filter(request, response));

        if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) {
            response.setStatus(HttpStatus.OK.value());
        }
        else {
            filterChain.doFilter(request, response);
        }
    }
}
public interface HttpHeaderProvider {
    void filter(HttpServletRequest request, HttpServletResponse response);
}
@Component
public class CsrfHttpHeaderProvider implements HttpHeaderProvider {
    @Override
    public void filter(HttpServletRequest request, HttpServletResponse response) {
        response.addHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, "X-CSRF-TOKEN");
    }
}
@Component("csrfTokenFilter")
public class CsrfTokenFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        CsrfToken csrf = (CsrfToken)request.getAttribute(CsrfToken.class.getName());

        if (csrf != null) {
            Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");

            String token = csrf.getToken();

            if (cookie == null || token != null && !token.equals(cookie.getValue())) {
                cookie = new Cookie("XSRF-TOKEN", token);
                cookie.setPath("/");

                response.addCookie(cookie);
            }
        }

        filterChain.doFilter(request, response);
    }
}
web.xml

...
<filter>
    <filter-name>httpHeaderFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    <async-supported>true</async-supported>
</filter>
<filter-mapping>
    <filter-name>httpHeaderFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
...
...
<custom-filter ref="csrfTokenFilter" after="CSRF_FILTER"/>
...
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<title>Spring Security Example </title>
</head>
<body>
<div th:if="${param.error}"> Invalid username and password. </div>
<div th:if="${param.logout}"> You have been logged out. </div>
<form th:action="@{/login}" method="post">
<div><label> User Name : <input type="text" name="username"/> </label></div>
<div><label> Password: <input type="password" name="password"/> </label></div>
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>
<div><input type="submit" value="Sign In"/></div>
</form>
</body>
</html>

我使用带弹簧靴的thymeleaf。我也有同样的问题。我诊断通过浏览器查看返回的html源时出现问题。应该是这样的:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<title>Spring Security Example </title>
</head>
<body>
<form method="post" action="/login">
<div><label> User Name : <input type="text" name="username" /> </label></div>
<div><label> Password: <input type="password" name="password" /> </label></div>
<input type="hidden" name="_csrf" value=<!--"aaef0ba0-1c75-4434-b6cf-62c975dcc8ba"--> />
<div><input type="submit" value="Sign In" /></div>
</form>
</body>
</html>

在登录页面的响应头中提供CSRF令牌有意义吗?因此,当登录页面被加载时,响应在头中包含令牌所需的值。然后,我可以在表单登录提交中使用该标题。这是一种安全的方法吗?这个博客似乎比我的答案要复杂得多。您喜欢您的解决方案的原因是什么?我还添加了一个JIRA,以改进此领域的文档。请看,我在SpringSecurity3.2中使用AngularJS,并在我的index.html中使用了元标记,正如您所指出的。I stll get“未找到预期的CSRF令牌。您的会话是否已过期?”,是否有办法在ChromeTools/Firebug中验证Spring是否真的生成了CSRF令牌?确保您的标记在元标记中包含该令牌。您还应该验证您的HttpSession在创建令牌和使用令牌的请求之间没有变化。我在头部使用了
,但我没有看到占位符被csrf令牌替换。另外,我如何确保http会话在登录前后不会发生更改。在我看来,http会话似乎是在登录后创建/更改的。这个问题要求使用纯html,而不是带有th:value=“${{u csrf.token}”的JSP。如果要使用HttpSessionSrftokenRepository#loadToken(),可能会更干净。您不应该使用cookies来存储令牌。正如亚历山德罗之前所说,不要通过cookie发送令牌。CSRF中的全部要点是,攻击者让受害者发送一个请求,该请求将自动使用受害者cookies和会话id。令牌是一个秘密,不应自动发送,而应作为隐藏表单字段或标头发送。把它放在饼干里完全违背了目的。