Springboot集成CSRF防攻击过程

 更新时间:2025年11月07日 16:16:39   作者:hao_kkkkk  
本文介绍了Springboot集成CSRF防攻击的方法,通过设置域名或路径白名单来过滤未知链接,从而达到防护目的,主要涉及两个类:CSRFFilterConfigUtils防护配置工具类和CsrfFilter防护过滤类

Springboot集成CSRF防攻击

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;
    }
}

总结

以上代码仅供学习交流使用,代码中涉及到真实项目信息的内容我都做了相应修改

当然,这些仅为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Java的内存泄漏和性能瓶颈解读

    Java的内存泄漏和性能瓶颈解读

    这篇文章主要介绍了Java的内存泄漏和性能瓶颈,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-06-06
  • Java使用UTF-8或GBK编码后还是乱码问题的解决办法

    Java使用UTF-8或GBK编码后还是乱码问题的解决办法

    在java中处理字符时,经常会发生乱码,下面这篇文章主要给大家介绍了关于Java使用UTF-8或GBK编码后还是乱码问题的解决办法,文中通过图文介绍的非常详细,需要的朋友可以参考下
    2023-05-05
  • Java通过在主循环中判断Boolean来停止线程的方法示例

    Java通过在主循环中判断Boolean来停止线程的方法示例

    这篇文章主要介绍了Java通过在主循环中判断Boolean来停止线程的方法,结合具体实例形式分析了java针对线程的判断与停止操作相关实现技巧,需要的朋友可以参考下
    2017-04-04
  • mybatis-plus Wrapper条件构造器updateForSet更新方式

    mybatis-plus Wrapper条件构造器updateForSet更新方式

    这篇文章主要介绍了mybatis-plus Wrapper条件构造器updateForSet更新方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • JAXB解析xml转换成类的实现方式

    JAXB解析xml转换成类的实现方式

    本文主要介绍了如何使用JAXB将XML配置项转换为Java类,JAXB提供了多种注解,如@XmlRootElement、@XmlElement、@XmlElementWrapper、@XmlAttribute等,可以方便地将XML元素映射为Java对象,并且可以控制生成的XML结构,同时,文章也提到了一些需要注意的问题
    2025-11-11
  • 用java实现杨辉三角的示例代码

    用java实现杨辉三角的示例代码

    本篇文章主要介绍了用java实现杨辉三角的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-01-01
  • Spring Boot 实现敏感词及特殊字符过滤处理

    Spring Boot 实现敏感词及特殊字符过滤处理

    这篇文章主要介绍了SpringBoot实现敏感词及特殊字符过滤处理方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-06-06
  • Java Spring boot日期和时间统一设置三种方法

    Java Spring boot日期和时间统一设置三种方法

    时间和日期的统一设置在项目中经常是会遇到的,下面这篇文章主要给大家介绍了关于Java Spring boot日期和时间统一设置的三种方法,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-08-08
  • Maven私服仓库Nexus配置小结

    Maven私服仓库Nexus配置小结

    Maven 私服是一种特殊的Maven远程仓库,它是架设在局域网内的仓库服务,本文就来介绍一下Maven私服仓库Nexus配置小结,具有一定的参考价值,感兴趣的可以了解一下
    2024-08-08
  • MyBatis-Plus中如何使用ResultMap的方法示例

    MyBatis-Plus中如何使用ResultMap的方法示例

    本文主要介绍了MyBatis-Plus中如何使用ResultMap,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-11-11

最新评论