最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring Cloud Gateway網(wǎng)關(guān)XSS過濾方式

 更新時(shí)間:2021年10月13日 09:35:50   作者:千年的心  
這篇文章主要介紹了Spring Cloud Gateway網(wǎng)關(guān)XSS過濾方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

XSS是一種經(jīng)常出現(xiàn)在web應(yīng)用中的計(jì)算機(jī)安全漏洞,具體信息請(qǐng)自行Google。本文只分享在Spring Cloud Gateway中執(zhí)行通用的XSS防范。首次作文,全是代碼,若有遺漏不明之處,請(qǐng)各位看官原諒指點(diǎn)。

使用版本

  • Spring Cloud版本為 Greenwich.SR4
  • Spring Boot版本為 2.1.11.RELEASE

1.創(chuàng)建一個(gè)Filter

特別注意的是在處理完成之后需要重新構(gòu)造請(qǐng)求,否則后續(xù)業(yè)務(wù)無法獲得參數(shù)。

import io.netty.buffer.ByteBufAllocator;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.NettyDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.DigestUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import javax.validation.constraints.NotEmpty;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Optional;
/**
 * XSS過濾
 *
 * @author lieber
 */
@Component
@Slf4j
@ConfigurationProperties("config.xss")
@Data
public class XssFilter implements GlobalFilter, Ordered {
    private List<XssWhiteUrl> whiteUrls;
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        URI uri = request.getURI();
        String method = request.getMethodValue();
        // 判斷是否在白名單中
        if (this.white(uri.getPath(), method)) {
            return chain.filter(exchange);
        }
        // 只攔截POST和PUT請(qǐng)求
        if ((HttpMethod.POST.name().equals(method) || HttpMethod.PUT.name().equals(method))) {
            return DataBufferUtils.join(request.getBody())
                    .flatMap(dataBuffer -> {
                        // 取出body中的參數(shù)
                        byte[] oldBytes = new byte[dataBuffer.readableByteCount()];
                        dataBuffer.read(oldBytes);
                        String bodyString = new String(oldBytes, StandardCharsets.UTF_8);
                        log.debug("原請(qǐng)求參數(shù)為:{}", bodyString);
                        // 執(zhí)行XSS清理
                        bodyString = XssUtil.INSTANCE.cleanXss(bodyString);
                        log.debug("修改后參數(shù)為:{}", bodyString);
                        ServerHttpRequest newRequest = request.mutate().uri(uri).build();
                        // 重新構(gòu)造body
                        byte[] newBytes = bodyString.getBytes(StandardCharsets.UTF_8);
                        DataBuffer bodyDataBuffer = toDataBuffer(newBytes);
                        Flux<DataBuffer> bodyFlux = Flux.just(bodyDataBuffer);
                        // 重新構(gòu)造header
                        HttpHeaders headers = new HttpHeaders();
                        headers.putAll(request.getHeaders());
                        // 由于修改了傳遞參數(shù),需要重新設(shè)置CONTENT_LENGTH,長(zhǎng)度是字節(jié)長(zhǎng)度,不是字符串長(zhǎng)度
                        int length = newBytes.length;
                        headers.remove(HttpHeaders.CONTENT_LENGTH);
                        headers.setContentLength(length);
                        headers.set(HttpHeaders.CONTENT_TYPE, "application/json;charset=utf8");
                        // 重寫ServerHttpRequestDecorator,修改了body和header,重寫getBody和getHeaders方法
                        newRequest = new ServerHttpRequestDecorator(newRequest) {
                            @Override
                            public Flux<DataBuffer> getBody() {
                                return bodyFlux;
                            }
                            @Override
                            public HttpHeaders getHeaders() {
                                return headers;
                            }
                        };
                        return chain.filter(exchange.mutate().request(newRequest).build());
                    });
        } else {
            return chain.filter(exchange);
        }
    }
    /**
     * 是否是白名單
     *
     * @param url    路由
     * @param method 請(qǐng)求方式
     * @return true/false
     */
    private boolean white(String url, String method) {
        return whiteUrls != null && whiteUrls.contains(XssWhiteUrl.builder().url(url).method(method).build());
    }
    /**
     * 字節(jié)數(shù)組轉(zhuǎn)DataBuffer
     *
     * @param bytes 字節(jié)數(shù)組
     * @return DataBuffer
     */
    private DataBuffer toDataBuffer(byte[] bytes) {
        NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);
        DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length);
        buffer.write(bytes);
        return buffer;
    }
    public static final int ORDER = 10;
    @Override
    public int getOrder() {
        return ORDER;
    }
    @Data
    @Validated
    @AllArgsConstructor
    @NoArgsConstructor
    private static class XssWhiteUrl {
        @NotEmpty
        private String url;
        @NotEmpty
        private String method;
    }
}

2. 處理XSS字符串

這里大范圍采用Jsoup處理,然后根據(jù)自己的業(yè)務(wù)做了一部分定制。較為特殊的是,我們將字符串中含有'</'標(biāo)示為這段文本是富文本。在清除xss攻擊字符串方法時(shí)優(yōu)化空間較大。

import com.alibaba.fastjson.JSONObject;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.safety.Whitelist;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
 * xss攔截工具類
 *
 * @author lieber
 */
public enum XssUtil {
    /**
     * 實(shí)例
     */
    INSTANCE;
    private final static String RICH_TEXT = "</";
    /**
     * 自定義白名單
     */
    private final static Whitelist CUSTOM_WHITELIST = Whitelist.relaxed()
            .addAttributes("video", "width", "height", "controls", "alt", "src")
            .addAttributes(":all", "style", "class");
    /**
     * jsoup不格式化代碼
     */
    private final static Document.OutputSettings OUTPUT_SETTINGS = new Document.OutputSettings().prettyPrint(false);
    /**
     * 清除json對(duì)象中的xss攻擊字符
     *
     * @param val json對(duì)象字符串
     * @return 清除后的json對(duì)象字符串
     */
    private String cleanObj(String val) {
        JSONObject jsonObject = JSONObject.parseObject(val);
        for (Map.Entry<String, Object> entry : jsonObject.entrySet()) {
            if (entry.getValue() != null && entry.getValue() instanceof String) {
                String str = (String) entry.getValue();
                str = this.cleanXss(str);
                entry.setValue(str);
            }
        }
        return jsonObject.toJSONString();
    }
    /**
     * 清除json數(shù)組中的xss攻擊字符
     *
     * @param val json數(shù)組字符串
     * @return 清除后的json數(shù)組字符串
     */
    private String cleanArr(String val) {
        List<String> list = JSONObject.parseArray(val, String.class);
        List<String> result = new ArrayList<>(list.size());
        for (String str : list) {
            str = this.cleanXss(str);
            result.add(str);
        }
        return JSONObject.toJSONString(result);
    }
    /**
     * 清除xss攻擊字符串,此處優(yōu)化空間較大
     *
     * @param str 字符串
     * @return 清除后無害的字符串
     */
    public String cleanXss(String str) {
        if (JsonUtil.INSTANCE.isJsonObj(str)) {
            str = this.cleanObj(str);
        } else if (JsonUtil.INSTANCE.isJsonArr(str)) {
            str = this.cleanArr(str);
        } else {
            boolean richText = this.richText(str);
            if (!richText) {
                str = str.trim();
                str = str.replaceAll(" +", " ");
            }
            String afterClean = Jsoup.clean(str, "", CUSTOM_WHITELIST, OUTPUT_SETTINGS);
            if (paramError(richText, afterClean, str)) {
                throw new BizRunTimeException(ApiCode.PARAM_ERROR, "參數(shù)包含特殊字符");
            }
            str = richText ? afterClean : this.backSpecialStr(afterClean);
        }
        return str;
    }
    /**
     * 判斷是否是富文本
     *
     * @param str 待判斷字符串
     * @return true/false
     */
    private boolean richText(String str) {
        return str.contains(RICH_TEXT);
    }
    /**
     * 判斷是否參數(shù)錯(cuò)誤
     *
     * @param richText   是否富文本
     * @param afterClean 清理后字符
     * @param str        原字符串
     * @return true/false
     */
    private boolean paramError(boolean richText, String afterClean, String str) {
        // 如果包含富文本字符,那么不是參數(shù)錯(cuò)誤
        if (richText) {
            return false;
        }
        // 如果清理后的字符和清理前的字符匹配,那么不是參數(shù)錯(cuò)誤
        if (Objects.equals(str, afterClean)) {
            return false;
        }
        // 如果僅僅包含可以通過的特殊字符,那么不是參數(shù)錯(cuò)誤
        if (Objects.equals(str, this.backSpecialStr(afterClean))) {
            return false;
        }
        // 如果還有......
        return true;
    }
    /**
     * 轉(zhuǎn)義回特殊字符
     *
     * @param str 已經(jīng)通過轉(zhuǎn)義字符
     * @return 轉(zhuǎn)義后特殊字符
     */
    private String backSpecialStr(String str) {
        return str.replaceAll("&amp;", "&");
    }
}

3.其它使用到的工具

import com.alibaba.fastjson.JSONObject;
import org.springframework.util.StringUtils;
/**
 * JSON處理工具類
 *
 * @author lieber
 */
public enum JsonUtil {
    /**
     * 實(shí)例
     */
    INSTANCE;
    /**
     * json對(duì)象字符串開始標(biāo)記
     */
    private final static String JSON_OBJECT_START = "{";
    /**
     * json對(duì)象字符串結(jié)束標(biāo)記
     */
    private final static String JSON_OBJECT_END = "}";
    /**
     * json數(shù)組字符串開始標(biāo)記
     */
    private final static String JSON_ARRAY_START = "[";
    /**
     * json數(shù)組字符串結(jié)束標(biāo)記
     */
    private final static String JSON_ARRAY_END = "]";
    /**
     * 判斷字符串是否json對(duì)象字符串
     *
     * @param val 字符串
     * @return true/false
     */
    public boolean isJsonObj(String val) {
        if (StringUtils.isEmpty(val)) {
            return false;
        }
        val = val.trim();
        if (val.startsWith(JSON_OBJECT_START) && val.endsWith(JSON_OBJECT_END)) {
            try {
                JSONObject.parseObject(val);
                return true;
            } catch (Exception e) {
                return false;
            }
        }
        return false;
    }
    /**
     * 判斷字符串是否json數(shù)組字符串
     *
     * @param val 字符串
     * @return true/false
     */
    public boolean isJsonArr(String val) {
        if (StringUtils.isEmpty(val)) {
            return false;
        }
        val = val.trim();
        if (StringUtils.isEmpty(val)) {
            return false;
        }
        val = val.trim();
        if (val.startsWith(JSON_ARRAY_START) && val.endsWith(JSON_ARRAY_END)) {
            try {
                JSONObject.parseArray(val);
                return true;
            } catch (Exception e) {
                return false;
            }
        }
        return false;
    }
    /**
     * 判斷對(duì)象是否是json對(duì)象
     *
     * @param obj 待判斷對(duì)象
     * @return true/false
     */
    public boolean isJsonObj(Object obj) {
        String str = JSONObject.toJSONString(obj);
        return this.isJsonObj(str);
    }
    /**
     * 判斷字符串是否json字符串
     *
     * @param str 字符串
     * @return true/false
     */
    public boolean isJson(String str) {
        if (StringUtils.isEmpty(str)) {
            return false;
        }
        return this.isJsonObj(str) || this.isJsonArr(str);
    }
}

大功告成。

----------------手動(dòng)分隔----------------

修改

感謝@chang_p_x的指正,在第一步創(chuàng)建Filter時(shí)有問題,原因是使用了新舊代碼的問題,現(xiàn)已經(jīng)將元代碼放在正文,新代碼如下

@Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        URI uri = request.getURI();
        String method = request.getMethodValue();
        if (this.white(uri.getPath(), method)) {
            return chain.filter(exchange);
        }
        if ((HttpMethod.POST.name().equals(method) || HttpMethod.PUT.name().equals(method))) {
            return DataBufferUtils.join(request.getBody()).flatMap(d -> Mono.just(Optional.of(d))).defaultIfEmpty(Optional.empty())
                    .flatMap(optional -> {
                        // 取出body中的參數(shù)
                        String bodyString = "";
                        if (optional.isPresent()) {
                            byte[] oldBytes = new byte[optional.get().readableByteCount()];
                            optional.get().read(oldBytes);
                            bodyString = new String(oldBytes, StandardCharsets.UTF_8);
                        }
                        HttpHeaders httpHeaders = request.getHeaders();
                        // 執(zhí)行XSS清理
                        log.debug("{} - [{}:{}] XSS處理前參數(shù):{}", method, uri.getPath(), bodyString);
                        bodyString = XssUtil.INSTANCE.cleanXss(bodyString);
                        log.info("{} - [{}:{}] 參數(shù):{}", method, uri.getPath(), bodyString);
                        
                        ServerHttpRequest newRequest = request.mutate().uri(uri).build();
                        // 重新構(gòu)造body
                        byte[] newBytes = bodyString.getBytes(StandardCharsets.UTF_8);
                        DataBuffer bodyDataBuffer = toDataBuffer(newBytes);
                        Flux<DataBuffer> bodyFlux = Flux.just(bodyDataBuffer);
                        // 重新構(gòu)造header
                        HttpHeaders headers = new HttpHeaders();
                        headers.putAll(httpHeaders);
                        // 由于修改了傳遞參數(shù),需要重新設(shè)置CONTENT_LENGTH,長(zhǎng)度是字節(jié)長(zhǎng)度,不是字符串長(zhǎng)度
                        int length = newBytes.length;
                        headers.remove(HttpHeaders.CONTENT_LENGTH);
                        headers.setContentLength(length);
                        headers.set(HttpHeaders.CONTENT_TYPE, "application/json;charset=utf8");
                        // 重寫ServerHttpRequestDecorator,修改了body和header,重寫getBody和getHeaders方法
                        newRequest = new ServerHttpRequestDecorator(newRequest) {
                            @Override
                            public Flux<DataBuffer> getBody() {
                                return bodyFlux;
                            }
                            @Override
                            public HttpHeaders getHeaders() {
                                return headers;
                            }
                        };
                        return chain.filter(exchange.mutate().request(newRequest).build());
                    });
        } else {
            return chain.filter(exchange);
        }
    }

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Struts2截取字符串代碼介紹

    Struts2截取字符串代碼介紹

    這篇文章主要介紹了Struts2截取字符串代碼介紹,介紹了基本的截取方法,以及截取帶html標(biāo)簽字符串的方法,需要的朋友可以了解下。
    2017-09-09
  • 利用IDEA工具修改Maven多模塊項(xiàng)目標(biāo)識(shí)包名全過程記錄

    利用IDEA工具修改Maven多模塊項(xiàng)目標(biāo)識(shí)包名全過程記錄

    當(dāng)我們?yōu)榧追椒?wù)提供軟件開發(fā)服務(wù)時(shí),需要按照甲方的要求去修改軟件的標(biāo)識(shí),對(duì)于Maven項(xiàng)目來說就對(duì)應(yīng)著groupId,一般地寫對(duì)方公司的域名,如com.example,接下來通過本文給大家分享IDEA修改Maven多模塊項(xiàng)目標(biāo)識(shí)包名,感興趣的朋友一起看看吧
    2022-09-09
  • 新版SpringSecurity5.x使用與配置詳解

    新版SpringSecurity5.x使用與配置詳解

    Spring Security是一個(gè)強(qiáng)大且高度可定制的身份驗(yàn)證和訪問控制框架,本文主要介紹了新版SpringSecurity5.x使用與配置詳解,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-08-08
  • Jackson使用示例-Bean、XML、Json之間相互轉(zhuǎn)換

    Jackson使用示例-Bean、XML、Json之間相互轉(zhuǎn)換

    Jackson是一個(gè)強(qiáng)大工具,可用于Json、XML、實(shí)體之間的相互轉(zhuǎn)換,JacksonXmlElementWrapper用于指定List等集合類,外圍標(biāo)簽名,JacksonXmlProperty指定包裝標(biāo)簽名,或者指定標(biāo)簽內(nèi)部屬性名,JacksonXmlRootElement指定生成xml根標(biāo)簽的名字,JacksonXmlText指定當(dāng)前這個(gè)值
    2024-05-05
  • java中sdk與jdk的區(qū)別詳細(xì)解析

    java中sdk與jdk的區(qū)別詳細(xì)解析

    以下是對(duì)java中sdk與jdk的區(qū)別進(jìn)行了詳細(xì)的分析介紹,需要的朋友可以過來參考下
    2013-08-08
  • 詳解@AliasFor注解的使用與注意事項(xiàng)

    詳解@AliasFor注解的使用與注意事項(xiàng)

    @AliasFor注解是在spring源碼當(dāng)中提供的,見名知義,他是為了別名而自定義的注解,下面我們來看看它的使用與注意事項(xiàng),感興趣的小伙伴可以了解一下
    2023-08-08
  • JAVA OutputStreamWriter流的實(shí)現(xiàn)

    JAVA OutputStreamWriter流的實(shí)現(xiàn)

    OutputStreamWriter是從字符流到字節(jié)流的橋接,它使用的字符集可以通過名稱指定,也可以明確指定,或者可以接受平臺(tái)的默認(rèn)字符集,本文詳細(xì)的介紹了JAVA OutputStreamWriter流的使用,感興趣的可以了解一下
    2021-06-06
  • Spring自定義注解配置簡(jiǎn)單日志示例

    Spring自定義注解配置簡(jiǎn)單日志示例

    這篇文章主要介紹了Spring自定義注解配置簡(jiǎn)單日志示例,注解可以增強(qiáng)我們的java代碼,同時(shí)利用反射技術(shù)可以擴(kuò)充實(shí)現(xiàn)很多功能,它們被廣泛應(yīng)用于三大框架底層,需要的朋友可以參考下
    2023-05-05
  • 詳解spring applicationContext.xml 配置文件

    詳解spring applicationContext.xml 配置文件

    本篇文章主要介紹了詳解spring applicationContext.xml 配置文件 ,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-02-02
  • 在springboot中攔截器Filter中注入bean失敗問題及解決

    在springboot中攔截器Filter中注入bean失敗問題及解決

    這篇文章主要介紹了在springboot中攔截器Filter中注入bean失敗問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-05-05

最新評(píng)論

玛曲县| 武汉市| 梁河县| 云安县| 石门县| 忻城县| 舞钢市| 乐平市| 于都县| 和林格尔县| 科尔| 孝感市| 邓州市| 治县。| 开原市| 玉环县| 连山| 凤翔县| 贵溪市| 资源县| 资兴市| 万盛区| 仪征市| 舒兰市| 河池市| 灵武市| 襄汾县| 五大连池市| 临夏市| 松原市| 宜章县| 泸溪县| 阜平县| 清苑县| 武川县| 蓝田县| 留坝县| 大安市| 无极县| 涟水县| 西城区|