重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
今天就跟大家聊聊有关springboot中怎么利用security自定义CSRF防御,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。
创新互联专注于未央网站建设服务及定制,我们拥有丰富的企业做网站经验。 热诚为您提供未央营销型网站建设,未央网站制作、未央网页设计、未央网站官网定制、成都微信小程序服务,打造未央网络公司原创品牌,更为您提供未央网站排名全网营销落地服务。
查看csrfFilter源码,会先去HttpSessionCsrfTokenRepository.loadToken加载CsrfToken ,其实就是从session中获取。
public CsrfToken loadToken(HttpServletRequest request) { HttpSession session = request.getSession(false); if (session == null) { return null; } return (CsrfToken) session.getAttribute(this.sessionAttributeName); }
如果不存在,会创建一个CsrfToken 并放入session
public void saveToken(CsrfToken token, HttpServletRequest request, HttpServletResponse response) { if (token == null) { HttpSession session = request.getSession(false); if (session != null) { session.removeAttribute(this.sessionAttributeName); } } else { HttpSession session = request.getSession(); session.setAttribute(this.sessionAttributeName, token); } }
之后会从request中取token,与从session中取出的token对比,所以这里开启csrf认证后,这四种请求是不验证csrf的”GET”, “HEAD”, “TRACE”, “OPTIONS”,常用的POST请求,每次都要么在请求头上加上X-CSRF-TOKEN:csrf值,或者在POST中加入参数_csrf:csrf值,这样才能取出request中的csrf和session中的对比。
String actualToken = request.getHeader(csrfToken.getHeaderName()); if (actualToken == null) { actualToken = request.getParameter(csrfToken.getParameterName()); } if (!csrfToken.getToken().equals(actualToken)) { if (this.logger.isDebugEnabled()) { this.logger.debug("Invalid CSRF token found for " + UrlUtils.buildFullRequestUrl(request)); } if (missingToken) { this.accessDeniedHandler.handle(request, response, new MissingCsrfTokenException(actualToken)); }else { this.accessDeniedHandler.handle(request, response, new InvalidCsrfTokenException(csrfToken, actualToken)); } return; }
如果request中取出的csrf和session中取出的不相等,会进入accessDeniedHandler.handle,这个accessDeniedHandler是AccessDeniedHandlerImpl,里面方法如下:
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { if(!response.isCommitted()) { if(this.errorPage != null) { request.setAttribute("SPRING_SECURITY_403_EXCEPTION", accessDeniedException); response.setStatus(403); RequestDispatcher dispatcher = request.getRequestDispatcher(this.errorPage); dispatcher.forward(request, response); } else { response.sendError(403, accessDeniedException.getMessage()); } } }
如果设置了errorPage会服务器内部转发到该路径,之后还是会经过security的各个filter进行登陆操作,最终登陆成功。这不是我要的,,所以需要自定义一个accessDeniedHandler。代码如下,如果不是登陆请求的 csrf不匹配,都退出当前用户,登陆用户不做csrf校验。
@Component public class CsrfAccessDeniedHandler implements AccessDeniedHandler { private SecurityContextLogoutHandler logoutHandler = new SecurityContextLogoutHandler(); @Autowired private AjaxLogoutSuccessHandler ajaxLogoutSuccessHandler; @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); logoutHandler.logout(request, response, auth); ajaxLogoutSuccessHandler.onLogoutSuccess(request, response, auth); } }
然后在securityConfig里配置
@Override protected void configure(HttpSecurity http) throws Exception { // 允许iframe http.headers().frameOptions().sameOrigin(); //登陆页面不做csrf校验 http.csrf().ignoringAntMatchers("/login"); //异常处理 http.exceptionHandling(). accessDeniedHandler(csrfAccessDeniedHandler) }
看完上述内容,你们对springboot中怎么利用security自定义CSRF防御有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注创新互联行业资讯频道,感谢大家的支持。