我对如何进行CSRF保护感到困惑 . 我有单独的前端(angularjs)和后端(Spring) . 它们部署在完全独立的位置,并通过REST进行通信 .
我的问题如下 . Angular拒绝发送我的CSRF cookie跨域 - 我可以发送的是CSRF头 . 我已经尝试将 withCredentials
添加到我的后端的角度和CORS过滤器,并设置xsrf标头和cookie,如here under Usage所述 .
任何想法我可能做错了什么?如果你想要我的代码的某些特定部分,请发布,我会发送 .
@添加相关代码:
CORSFilter
public class CORSFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest request = (HttpServletRequest) req;
response.setHeader("Access-Control-Allow-Origin", "http://localhost:9000");
response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with,origin,content-type,accept,X-XSRF-TOKEN, authorization, customer-id, X-AUTH-TOKEN");
response.setHeader("Access-Control-Expose-Headers", "employee_name, employee_id, employee_customer_id, X-AUTH-TOKEN");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Credentials", "true");
if (request.getMethod()!="OPTIONS") {
chain.doFilter(req, res);
} else {
}
}
CSRF过滤器
public class StatelessCSRFFilter extends OncePerRequestFilter {
private static final String CSRF_TOKEN = "CSRF-TOKEN";
private static final String X_CSRF_TOKEN = "X-XSRF-TOKEN";
private final RequestMatcher requireCsrfProtectionMatcher = new DefaultRequiresCsrfMatcher();
private final AccessDeniedHandler accessDeniedHandler = new AccessDeniedHandlerImpl();
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
if (requireCsrfProtectionMatcher.matches(request)) {
final String csrfTokenValue = request.getHeader(X_CSRF_TOKEN);
final Cookie[] cookies = request.getCookies();
String csrfCookieValue = null;
if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals(CSRF_TOKEN)) {
csrfCookieValue = cookie.getValue();
}
}
}
if (csrfTokenValue == null || !csrfTokenValue.equals(csrfCookieValue)) {
accessDeniedHandler.handle(request, response, new AccessDeniedException(
"Missing or non-matching CSRF-token"));
return;
}
}
filterChain.doFilter(request, response);
}
public static final class DefaultRequiresCsrfMatcher implements RequestMatcher {
private final Pattern allowedMethods = Pattern.compile("^(GET|HEAD|TRACE|OPTIONS)$");
@Override
public boolean matches(HttpServletRequest request) {
return !allowedMethods.matcher(request.getMethod()).matches();
}
}
app.js
(...)
$httpProvider.defaults.xsrfHeaderName = 'X-CSRF-TOKEN';
$httpProvider.defaults.xsrfCookieName = 'CSRF-TOKEN';
$httpProvider.interceptors.push('InterceptorCsrf');
$httpProvider.defaults.withCredentials = true;
(...)
InterceptorCsrf.js
angular.module('EnterprisePortalApp')
.factory('InterceptorCsrf',function($cookies, $cookieStorage){
function b(a){return a?(a^Math.random()*16>>a/4).toString(16):([1e16]+1e16).replace(/[01]/g,b)};
return {
//With each request generate new csrf token
request: function(config) {
$cookieStorage.put("CSRF-TOKEN", b());
config.headers['X-XSRF-TOKEN'] = $cookies.get('CSRF-TOKEN');
return config;
}
}
});
2 回答
您的代码块似乎没问题 . 你有没有尝试过
"Access-Control-Allow-Origin", "http://localhost:9000"
更改为*
.BTW it's a bug in chrome pointing localhost with its port无法修复(SO discussion) .
此外,您可以尝试提供不同的域名(而不是localhost,您可能使用nginx代理设置等,这可能是一些棘手的)休息服务器和客户端主机 .
根据这种情况的额外信息:
如果对REST服务使用基于令牌的身份验证,则无需另外实施csrf保护 .
如果用户需要在每次请求此休息服务时发送其访问令牌(例如
jwt
),那么您的服务将受到csrf的保护,并且还具有与csrf保护类似的方法 .User gets token->request messages with token->decode token on backend->getuserid
(基本)并使他的过程像这样基于令牌的请求过程 . 在这种情况下,如果用户没有做任何事情 .使用以下代码为我工作
服务器端,我编写了CSRF和CORS过滤器,如官方Spring Angular指南中所述 .
客户端,我不得不编写一个$ http拦截器,如下所示,因为AngularJS doesn't automatically add是跨域请求的标头 .