将CSRF添加到现有和正在运行的CORS配置时遇到问题。
每次触发POST,PUT或DELETE时都会收到我当前令牌不正确的错误(找到无效的CSRF令牌' edff86dc-093a-4df9-8218-e5343506bdf9'请求参数' _csrf'或标题' X-CSRF-TOKEN'。)。
但是当我比较它们时,它不会由令牌引起。此外,如果我在此之后触发GET(例如PUT),则再次发送令牌并接受。
所以我假设我的安全配置可能有问题,但我不知道我错过了什么。
安全配置:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(new CorsFilter(), ChannelProcessingFilter.class);
http.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/*/**").permitAll()
.antMatchers("/logout", "/admin/**").authenticated();
http.csrf().ignoringAntMatchers("/guestbook/**");
http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);
http.formLogin().successHandler(authenticationSuccessHandler);
http.logout().logoutSuccessHandler(logoutSuccessHandler);
http.addFilterAfter(new CsrfTokenResponseHeaderBindingFilter(), CsrfFilter.class);
}
令牌过滤器:
public class CsrfTokenResponseHeaderBindingFilter extends OncePerRequestFilter {
protected static final String REQUEST_ATTRIBUTE_NAME = "_csrf";
protected static final String RESPONSE_HEADER_NAME = "X-CSRF-HEADER";
protected static final String RESPONSE_PARAM_NAME = "X-CSRF-PARAM";
protected static final String RESPONSE_TOKEN_NAME = "X-CSRF-TOKEN";
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, javax.servlet.FilterChain filterChain) throws ServletException, IOException {
CsrfToken token = (CsrfToken) request.getAttribute(REQUEST_ATTRIBUTE_NAME);
System.out.println(token.getToken());
if (token != null) {
response.setHeader(RESPONSE_HEADER_NAME, token.getHeaderName());
response.setHeader(RESPONSE_PARAM_NAME, token.getParameterName());
response.setHeader(RESPONSE_TOKEN_NAME , token.getToken());
}
filterChain.doFilter(request, response);
}
}
,例如cors过滤器:
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest request = (HttpServletRequest) req;
String origin = request.getHeader("Origin");
response.addHeader("Access-Control-Allow-Origin", origin);
response.setHeader("Access-Control-Allow-Credentials", "true");
response.addHeader("Access-Control-Max-Age", "10");
response.addHeader("Access-Control-Expose-Headers", "X-CSRF-TOKEN");
response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
String headers = request.getHeader("Access-Control-Request-Headers");
if (!StringUtils.isEmpty(headers )) {
response.addHeader("Access-Control-Allow-Headers", headers );
}
if (request.getMethod().equals("OPTIONS")) {
try {
response.getWriter().print("OK");
response.getWriter().flush();
} catch (IOException e) {
e.printStackTrace();
}
} else{
chain.doFilter(request, response);
}
}
问题不仅在我登录时发生。如果我不会在留言簿路径上禁用csrf,那么也可能没有POST。
我希望任何人都可以给我一个提示。
问候
答案 0 :(得分:0)
所以我终于解决了我的问题。经过大量的搜索和错误后,我发现了CsrfProtectionMatcher,它可用于在不同的路径上启用CSRF。
无论如何,这对我来说非常困惑,因为我认为默认情况下每次请求都会始终启用CSRF。因此,只要我在“/ admin”路径上应用CsrfProtectionMatcher(允许所有可能的方法,指定为null)就可以了。 requireCsrfProtectionMatcher on docs.spring.io,detailed article
此外,我现在可以使用更简单的配置,但尽管我的旧配置也可以。
旧的CsrfProtectionMatcher:
case $OPTION in
# the following matches
# - single-digit numbers 0-9
# - two-digit numbers starting with either 1 or 2
# - the number 30
[0-9]|[12][0-9]|30)
IMAGE=${options[$(($OPTION-1))]%.tar}
;;
*)
echo "invalid option" 1>&2
exit 1
esac
更简单的配置:
protected void configure(HttpSecurity http) throws Exception {
RequestMatcher csrfRequestMatcher = new RequestMatcher() {
private RegexRequestMatcher requestMatcher =
new RegexRequestMatcher("/admin", null);
public boolean matches(HttpServletRequest request) {
if(requestMatcher.matches(request))
return true;
return false;
}
};
http.addFilterBefore(new CorsFilter(), ChannelProcessingFilter.class);
http
.csrf()
.requireCsrfProtectionMatcher(csrfRequestMatcher);
http.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/*/**").permitAll()
.antMatchers("/login", "/**/**/**").permitAll()
.antMatchers("/logout", "/admin/**").authenticated();
http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);
http.formLogin().successHandler(authenticationSuccessHandler);
http.logout().logoutSuccessHandler(logoutSuccessHandler);
http.addFilterAfter(new CsrfTokenResponseHeaderBindingFilter(), CsrfFilter.class);
}
我不得不承认我仍然不知道为什么必须明确启用CSRF。如果有人对此有答案,请告诉我。