CSRF 就是跨域请求伪造,是一种常见的web攻击方式,解决思路也非常简单,主要是设置域名或路径白名单,对于未知的链接予以过滤,从而达到防护目的。
总共两个类,一个CSRFFilterConfigUtils防护配置工具类,主要作用是配置防护开关、请求路径白名单以及请求域名白名单;一个是CsrfFilter防护过滤类,该类实质是一个拦截器,拦截所有用户请求,匹配路径和域名,符合条件的通过,不符合条件的拦截掉;
以下为实际代码:
CSRFFilterConfigUtils 防护配置工具类
- import org.apache.commons.lang.StringUtils;
- import org.springframework.beans.factory.annotation.Value;
- import org.springframework.stereotype.Component;
-
- /**
- * @Auther: GMY
- * @Date: 2022/09/16/9:54
- * @Description: CSRF防护配置工具类
- */
- @Component
- public class CSRFFilterConfigUtils {
-
- /**
- * 跨站点请求路径白名单,通过英文逗号分隔。在application.properties配置
- */
- public static String csrfWhitePaths;
-
- /**
- * 跨站点请求域名白名单,通过英文逗号分隔。在application.properties配置
- */
- public static String csrfWhiteDomains;
-
- /**
- * csrf攻击防护开关配置
- */
- public static Boolean openCsrfProtect;
-
- /**
- * @param
- * @return java.lang.Boolean
- * @author GMY
- * @date 2022/9/16 10:13
- * @description csrf攻击防护开关配置,默认为开启
- */
- public static Boolean getOpenCsrfProtect() {
- return openCsrfProtect == null ? true : openCsrfProtect;
- }
-
- /**
- * @param
- * @return java.lang.String[]
- * @author GMY
- * @date 2022/9/16 10:07
- * @description 获取请求路径白名单
- */
- public static String[] getCsrfWhitePaths() {
- if (StringUtils.isNotEmpty(csrfWhitePaths)) {
- return csrfWhitePaths.split(",");
- }
- return null;
- }
-
- /**
- * @param
- * @return java.lang.String[]
- * @author GMY
- * @date 2022/9/16 10:09
- * @description 获取请求域名白名单
- */
- public static String[] getCsrfWhiteDomains() {
- if (StringUtils.isNotEmpty(csrfWhiteDomains)) {
- return csrfWhiteDomains.split(",");
- }
- return null;
- }
-
-
- @Value("${csrf.white.paths}")
- public static void setCsrfWhitePaths(String csrfWhitePaths) {
- CSRFFilterConfigUtils.csrfWhitePaths = csrfWhitePaths;
- }
-
- @Value("${csrf.white.domains}")
- public static void setCsrfWhiteDomains(String csrfWhiteDomains) {
- CSRFFilterConfigUtils.csrfWhiteDomains = csrfWhiteDomains;
- }
-
- @Value("${open.csrf.protect}")
- public void setOpenCsrfProtect(Boolean openCsrfProtect) {
- CSRFFilterConfigUtils.openCsrfProtect = openCsrfProtect;
- }
- }
CsrfFilter 防护过滤类
- import cn.hutool.json.JSONUtil;
- import org.apache.commons.lang.StringUtils;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.context.annotation.Configuration;
- import javax.servlet.*;
- import javax.servlet.annotation.WebFilter;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.io.IOException;
- import java.net.URL;
-
- /**
- * @Auther: GMY
- * @Date: 2022/09/15/19:54
- * @Description:
- */
- @WebFilter(urlPatterns = "/*",filterName = "csrfFilter")
- @Configuration
- public class CsrfFilter implements Filter {
-
- // 后台日志打印
- private Logger log = LoggerFactory.getLogger(CsrfFilter.class);
-
- @Override
- public void init(FilterConfig filterConfig) throws ServletException {
-
- }
-
- /**
- * @param servletRequest
- * @param servletResponse
- * @param filterChain
- * @return void
- * @author GMY
- * @date 2022/9/16 9:51
- * @description 执行CRSF过滤操作
- */
- @Override
- public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
- HttpServletRequest req = (HttpServletRequest) servletRequest;
- HttpServletResponse res = (HttpServletResponse) servletResponse;
- // 判断CSRF防护是否开启,如果没开启则直接略过过滤操作
- if (!CSRFFilterConfigUtils.getOpenCsrfProtect()) {
- filterChain.doFilter(servletRequest, servletResponse);
- } else {
- String referer = req.getHeader("Referer");
- if (!StringUtils.isBlank(referer)) {
- // 获取Referer参数中的地址和端口
- String refererHostAndPort = getHostAndPort(req,referer);
- // 获取RequestURL参数中的地址和端口
- String requestHostAndPort = getHostAndPort(req,null);
- // 同域名和同端口,即同一个域的系统,通过
- if (requestHostAndPort.equalsIgnoreCase(refererHostAndPort)) {
- filterChain.doFilter(servletRequest, servletResponse);
- }else {
- // 如果不同域名或端口,继续判断域名是否在白名单中,如果在白名单中则通过
- if(isCsrfWhiteDomains(refererHostAndPort)) {
- filterChain.doFilter(servletRequest, servletResponse);
- return;
- }
- // 获取RequestURL参数中的路径信息
- String path = new URL(req.getRequestURL().toString()).getPath();
- log.info("request请求路径 path = " + path);
- // 将路径中的域名去除,只保留具体路径
- String actionPath = path.replaceAll(servletRequest.getServletContext().getContextPath(), "");
- // 判断路径是否在访问路径白名单中,如果在白名单中,则通过,继续后续执行
- if(isCsrfWhitePaths(actionPath)) {
- filterChain.doFilter(servletRequest, servletResponse);
- return;
- }
- log.warn("csrf跨站点伪造请求已经被拦截:");
- log.warn("requestURL = " + req.getRequestURL().toString());
- log.warn("referer = " + referer);
- res.sendRedirect(req.getContextPath() + "/illegal");
- return;
- }
- }else{
- filterChain.doFilter(servletRequest, servletResponse);
- }
- }
- }
-
- /**
- * @param request
- * @param referer
- * @return java.lang.String
- * @author GMY
- * @date 2022/9/16 9:34
- * @description 获取请求地址和端口
- */
- protected String getHostAndPort(HttpServletRequest request, String referer) throws IOException {
- URL url;
- if (StringUtils.isNotEmpty(referer)) {
- url = new URL(referer);
- } else {
- url = new URL(request.getRequestURL().toString());
- }
- String requestHostAndPort;
- if(url.getPort() == -1) {
- requestHostAndPort = url.getHost();
- }else {
- requestHostAndPort = url.getHost() + ":" + url.getPort();
- }
- return requestHostAndPort;
-
- }
-
- @Override
- public void destroy() {
-
- }
-
- /**
- * @param path
- * @return boolean
- * @author GMY
- * @date 2022/9/16 9:52
- * @description 判断请求路径是否在路径白名单中
- */
- private boolean isCsrfWhitePaths(String path) {
-
- if(CSRFFilterConfigUtils.getCsrfWhitePaths() != null && CSRFFilterConfigUtils.getCsrfWhitePaths().length > 0) {
- for (String csrfWhitePath : CSRFFilterConfigUtils.getCsrfWhitePaths()) {
- if(!StringUtils.isBlank(csrfWhitePath)) {
- if(csrfWhitePath.equals(path)) {
- log.info("跨站点请求所有路径白名单:csrfWhitePaths = " + JSONUtil.toJsonStr(CSRFFilterConfigUtils.getCsrfWhitePaths()));
- log.info("符合跨站点请求路径白名单:path = " + path);
- return true;
- }
- }
- }
- }
- return false;
- }
-
- /**
- * @param refererHostAndPort
- * @return boolean
- * @author GMY
- * @date 2022/9/16 9:52
- * @description 判断请求域名是否在域名白名单中
- */
- private boolean isCsrfWhiteDomains(String refererHostAndPort) {
-
- if(CSRFFilterConfigUtils.getCsrfWhiteDomains() != null && CSRFFilterConfigUtils.getCsrfWhiteDomains().length > 0) {
- for (String csrfWhiteDomain : CSRFFilterConfigUtils.getCsrfWhiteDomains()) {
- if(!StringUtils.isBlank(csrfWhiteDomain)) {
- if(csrfWhiteDomain.equals(refererHostAndPort)) {
- log.info("跨站点请求所有【域名】]白名单:csrfWhiteDomains = " + JSONUtil.toJsonStr(CSRFFilterConfigUtils.getCsrfWhiteDomains()));
- log.info("符合跨站点请求【域名】白名单:refererHost = " + refererHostAndPort);
- return true;
- }
- }
- }
- log.info("跨站点请求非法【域名】:refererHost = " + refererHostAndPort);
- }
- return false;
- }
- }
以上代码仅供学习交流使用,代码中涉及到真实项目信息的内容我都做了相应修改