java springboot 通过Referer防止跨站点请求伪造

防止跨站点请求伪造

验证“Referer”头的值,并对每个提交的表单使用 one-time-nonce

在application.yml 中添加配置

# 防止 跨站点请求伪造, RefererFilter.java
security:
  csrf:
    # 开启过滤
    enable: true
    # 白名单接口 每个接口用 , 分隔
    excludes: 

我使用的是若依框架, 在common的filter包下添加 RefererFilter 类

package com.vxdata.common.filter;

import com.vxdata.common.utils.StringUtils;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


//@Configuration
@ConditionalOnProperty(prefix = "security.csrf", name = "enable", havingValue = "true")
@Configuration
@WebFilter(filterName = "refererFilter", urlPatterns = "/*")
@Data
public class RefererFilter implements Filter {
    
    
    public static final Logger logger = LoggerFactory.getLogger(RefererFilter.class);

    /**
     * 过滤器配置对象
     */
    FilterConfig filterConfig = null;

    /**
     * 是否启用
     */
    @Value("${security.csrf.enable:false}")
    private boolean enable = true;

    /**
     * 忽略的URL
     */
    @Value("${security.csrf.excludes:}")
    private String excludes;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    
    
        this.filterConfig = filterConfig;
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
    
    
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        // 不启用或者已忽略的URL不拦截
        if (!enable || isExcludeUrl(request.getServletPath())) {
    
    
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        }

        String referer = request.getHeader("Referer");
        String serverName = request.getServerName();

        // 判断是否存在外链请求本站
        if (referer == null || !referer.contains(serverName)) {
    
    
            logger.error("Referer过滤器 服务器:{} 当前域名:{}", serverName, referer);
            HttpServletResponse resp = (HttpServletResponse) servletResponse;
            resp.setStatus(HttpStatus.NOT_FOUND.value());
            return;
        } else {
    
    
            filterChain.doFilter(servletRequest, servletResponse);
        }
    }

    @Override
    public void destroy() {
    
    
        this.filterConfig = null;
    }

    /**
     * 判断是否为忽略的URL
     * <p>
     * <p>
     * URL路径
     *
     * @return true-忽略,false-过滤
     */
    private boolean isExcludeUrl(String url) {
    
    
        if (StringUtils.isBlank(excludes)) {
    
    
            return false;
        }
        List<String> urls = Arrays.asList(excludes.trim().split(","));
//        return urls.stream().filter(StringUtils::isNotBlank).map(pattern -> Pattern.compile("^" + pattern.trim()))
//                .map(p -> p.matcher(url)).anyMatch(Matcher::find);
        for (String uri : urls) {
    
    
            // 正则验证
            Pattern p = Pattern.compile("^" + uri);
            if (p.matcher(url).find()) {
    
    
                return true;
            }
        }
        return false;
    }
}

获取 referer 为null, 无法获取 referer 导致过滤失败

正常情况前端请求接口的时候, 请求表头中会有一个 referer 属性, 会携带当前前端页面路由信息
我们根据这个路由信息判断是不是我们的前端来访问接口的
在这里插入图片描述

如果前端访问时候没有这个referer 信息
在这里插入图片描述
浏览器打开检查, 查看页面元素, 看head标签中是否配置了 no-referrer
我这里删掉 < meta name=“referrer” content=“no-referrer”> 后就有 referer 了
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_44154912/article/details/127439461