java HTTP 状态 403 - 在请求参数“_csrf”或标头“X-CSRF-TOKEN”上发现无效的 CSRF 令牌“9ee6949c-c5dc-4d4b-9d55-46b75abc2994”
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/37241354/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
HTTP Status 403 - Invalid CSRF Token '9ee6949c-c5dc-4d4b-9d55-46b75abc2994' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN'
提问by brazuka
I am developing a web application which has a front-end with angularjs and a back-end with spring-security and jersey.
我正在开发一个 Web 应用程序,它有一个带有 angularjs 的前端和一个带有 spring-security 和 jersey 的后端。
I am trying to implement spring-security. I can authenticate the user. But I stucked at the logout point. I am sending the X-CSRF-TOKEN within a value, but it seems that spring-security is refusing it.
我正在尝试实施 spring-security。我可以验证用户。但我卡在注销点。我在一个值内发送 X-CSRF-TOKEN,但似乎 spring-security 拒绝它。
web.xml
网页.xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>M2Carros</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:applicationContext.xml
classpath:spring-security.xml
</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<servlet>
<servlet-name>jersey-serlvet</servlet-name>
<servlet-class>
com.sun.jersey.spi.spring.container.servlet.SpringServlet
</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>br.com.m2carros</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.spi.container.ContainerRequestFilters</param-name>
<param-value>com.sun.jersey.api.container.filter.LoggingFilter</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
<param-value>com.sun.jersey.api.container.filter.LoggingFilter</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>jersey-serlvet</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
<!-- Spring Security -->
<filter>
<filter-name>
springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy
</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>CorsFilter</filter-name>
<filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
<init-param>
<param-name>cors.allowed.headers</param-name>
<param-value>Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization</param-value>
</init-param>
<init-param>
<param-name>cors.exposed.headers</param-name>
<param-value>Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization,X-CSRF-TOKEN</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CorsFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
spring-security.xml
spring-security.xml
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.2.xsd">
<!-- enable use-expressions -->
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/" access="permitAll" />
<intercept-url pattern="/index.html" access="permitAll" />
<intercept-url pattern="/api/user" access="isAuthenticated()" />
<!-- enable csrf protection -->
<csrf/>
</http>
<!-- Select users and user_roles from database -->
<authentication-manager>
<authentication-provider>
<!-- <password-encoder hash="md5" /> -->
<jdbc-user-service data-source-ref="dataSource"
users-by-username-query=
"select username,password, enabled from usuario where username=?"
authorities-by-username-query=
"select username, role from user_roles where username =? " />
</authentication-provider>
</authentication-manager>
</beans:beans>
app.js (ommited routes)
app.js(省略路由)
$httpProvider.defaults.headers.common["X-Requested-With"] = 'XMLHttpRequest';
var csrfHeaderName = 'X-CSRF-TOKEN';
$httpProvider.interceptors.push(function() {
return {
response: function(response) {
console.log(response.headers());
console.log(response.headers(csrfHeaderName));
if(response.headers(csrfHeaderName) != null){
$httpProvider.defaults.headers.common[csrfHeaderName] = response.headers(csrfHeaderName);
}
return response;
}
}
});
appCtrl.js
应用程序控件.js
angular.module('m2App').controller('appCtrl', function($rootScope, $scope, $http, $location){
var serverUrl = 'http://localhost:8080/m2carros/api';
var authenticate = function(credentials, callback) {
var headers = credentials ? {authorization : "Basic "
+ btoa(credentials.username + ":" + credentials.password)
} : {};
$http.get(serverUrl+'/user', {headers : headers}).then(function(response) {
if (response.data.principal != undefined && response.data.principal.username) {
$rootScope.authenticated = true;
console.log("is authenticated ? "+$rootScope.authenticated);
} else {
$rootScope.authenticated = false;
console.log("is authenticated ? "+$rootScope.authenticated);
}
callback && callback();
}, function() {
$rootScope.authenticated = false;
console.log("is authenticated ? "+$rootScope.authenticated);
callback && callback();
});
}
authenticate();
$scope.credentials = {};
$scope.login = function() {
authenticate($scope.credentials, function() {
if ($rootScope.authenticated) {
$location.path("/");
console.log("Redirecionando usuario autenticado para /")
self.error = false;
} else {
$location.path("/login");
self.error = true;
}
});
};
$rootScope.logout = function() {
$http.post('logout', {}).then(function() {
$rootScope.authenticated = false;
$location.path("/");
});
}
});
回答by SkyWalker
XSRF is a technique by which an unauthorized site can gain your user's private data. Angular provides a mechanism to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie (by default, XSRF-TOKEN) and sets it as an HTTP header (X-XSRF-TOKEN).
XSRF 是一种未经授权的站点可以通过它获取您用户的私人数据的技术。Angular 提供了一种机制来对抗 XSRF。执行 XHR 请求时,$http 服务从 cookie(默认为 XSRF-TOKEN)中读取令牌并将其设置为 HTTP 标头 (X-XSRF-TOKEN)。
If you set appropriate cookie, then it ensures that angular will take care of the header internally
如果您设置了适当的 cookie,那么它可以确保angular 将在内部处理标头
So on that case, you need to check that server config won't need a new token each request
因此,在这种情况下,您需要检查服务器配置是否不需要每个请求的新令牌
You need to send the csrf
token when you submit your form. You need to add the following line in your HTML form:
您需要csrf
在提交表单时发送令牌。您需要在 HTML 表单中添加以下行:
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
Resource Link:
资源链接:
As CodeMedsuggested to add
正如CodeMed建议添加
.antMatchers("/send-pin").permitAll()
in SecurityConfiguration
class. He got some issue as stated below:
在SecurityConfiguration
课堂上。他遇到了一些问题,如下所述:
To examine the Network tab of the Firefox debug tools, which showed that the following two cookies were sent with the request: JSESSIONID:"99192501E7CEA0EDEF853BD666AF3C35" and XSRF-TOKEN:"b50afb87-e15c-4bef-93ca-7c2fdf145fd8", even though the server log for the same request still boiled down to Invalid CSRF token found for http://localhost:9000/send-pin. This caused me to examine why the sent token was being rejected, and a few minutes later I noticed the missing antmatchers(...) for the url pattern, leading to this answer.
检查 Firefox 调试工具的“网络”选项卡,其中显示以下两个 cookie 与请求一起发送:JSESSIONID:"99192501E7CEA0EDEF853BD666AF3C35"和 XSRF-TOKEN:"b50afb87-e15c-4bef-93ca-7fedf28f" server同一请求的日志仍然归结为为http://localhost:9000/send-pin找到的无效 CSRF 令牌。这让我开始检查为什么发送的令牌被拒绝,几分钟后我注意到 url 模式缺少 antmatchers(...),导致了这个答案。
This change caused SecurityConfiguration.configure(...)
method to now look like:
此更改导致SecurityConfiguration.configure(...)
方法现在看起来像:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().and().authorizeRequests()
.antMatchers("/send-pin").permitAll()
.antMatchers("/check-pin").permitAll()
.antMatchers("/index.html", "/", "/login", "/someotherrurl")
.permitAll().anyRequest().authenticated().and().csrf()
.csrfTokenRepository(csrfTokenRepository()).and()
.addFilterAfter(csrfHeaderFilter(), CsrfFilter.class);
}
Resource Link:
资源链接:
回答by brazuka
For those who are facing the same problem. Here is my solution.
对于那些面临同样问题的人。这是我的解决方案。
- First I wasn't able to get the cookie using $cookies.get(). It's because that the cookie that I was returning from the backend didn't had a path configured.
- After the authentication where I get a token I make many other http requests and I was discarding the tokens returned by these requests. So, when Spring Security compared the tokens It used the authentication token which was invalid and not the token from the last request.
- 首先,我无法使用 $cookies.get() 获取 cookie。这是因为我从后端返回的 cookie 没有配置路径。
- 在获得令牌的身份验证之后,我发出了许多其他 http 请求,并且丢弃了这些请求返回的令牌。因此,当 Spring Security 比较令牌时,它使用了无效的身份验证令牌,而不是来自上次请求的令牌。
Hope this help someone.
希望这有助于某人。