我正在开发一个 Web 应用程序,该应用程序的前端使用 angularjs,后端使用 spring-security 和 jersey。
我正在尝试实现 spring-security。我可以验证用户身份。但我卡在了注销点。我正在发送一个值内的 X-CSRF-TOKEN,但 spring-security 似乎拒绝它。
web.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
<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(省略的路由)
$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
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("/");
});
}
});
最佳答案
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).
如果您设置了适当的 cookie,那么它可以确保 Angular 将在内部处理 header
因此,在这种情况下,您需要检查服务器配置是否每个请求都需要新 token
您需要在提交表单时发送 csrf
token 。您需要在 HTML 表单中添加以下行:
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
资源链接:
如CodeMed建议添加
.antMatchers("/send-pin").permitAll()
在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.
此更改导致 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);
}
资源链接:
关于java - HTTP 状态 403 - 在请求参数 '9ee6949c-c5dc-4d4b-9d55-46b75abc2994' 或 header '_csrf' 上发现无效的 CSRF token 'X-CSRF-TOKEN',我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37241354/