SQL注入,跨站脚本攻击(XSS),跨站请求伪造(CSRF)
ApiRequestGlobalFilter api请求全局过滤器
ApiRequestXssSqllFilter api请求XssSql过滤器
ApiResponseGlobalFilter api返回全部过滤器
VerifyCondition 验证
CorsConfigration 跨域配置
WhiteListProperties 白名单配置
ManageGatewayProperties 网关配置
Exception
error 处理
jsonException 处理
package com.box.manage.gateway.filter;
import com.box.common.core.constant.RequestConstant;
import com.box.common.core.enums.ErrorCodeEnum;
import com.box.common.core.exception.AuthException;
import com.box.common.core.utils.GatewayUtils;
import com.box.common.core.utils.JwtUtils;
import com.box.common.core.utils.StringUtils;
import com.box.redis.service.RedisService;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import javax.annotation.Resource;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.function.Consumer;
/**
* 授权认证 https://blog.csdn.net/zzxzzxhao/article/details/83381876
*
* @author yuan.dingwang
* @date 2020年11月26日 15:53
*/
@Slf4j
@Component
class ApiRequestGlobalFilter implements GlobalFilter, Ordered {
final static String METHOD = "POST";
@Autowired
RedisService redisService;
@Resource
VerifyCondition verify;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token = exchange.getRequest().getHeaders().getFirst(RequestConstant.HEADER_ACCESS_TOKEN);
log.info("ApiRequestGlobalFilter token:{}",token);
//platform用于区分不同的端
String platform = exchange.getRequest().getHeaders().getFirst(RequestConstant.HEADER_PLATFORM);
if (StringUtils.isBlank(platform)) {
platform = exchange.getRequest().getQueryParams().getFirst(RequestConstant.HEADER_PLATFORM);
}
String requestUrl = exchange.getRequest().getPath().toString();
String subject = "";
//回调地址直接过滤
if (requestUrl.contains("/v2/api-docs") || requestUrl.contains("/threeParties/notice/")) {
Consumer<HttpHeaders> headersConsumer = httpHeaders -> {
httpHeaders.set(RequestConstant.HEADER_SAFETY_LABEL_NAME,RequestConstant.HEADER_SAFETY_LABEL_VAL);
// httpHeaders.setContentType(MediaType.APPLICATION_JSON);
};
exchange.mutate().request(exchange.getRequest().mutate()
.headers(headersConsumer).build()).build();
return chain.filter(exchange);
}
//验证平台端编码是否正确
if (verify.checkPlatform(platform)) {
return GatewayUtils.responseError(exchange, ErrorCodeEnum.AUTH_PLATFORM);
}
//白名单验证
if (!verify.checkWhiteIsLogin(requestUrl)) {
//验证token是否有效
if (verify.checkToken(token, Integer.valueOf(platform))) {
log.info("ApiRequestGlobalFilter checkToken");
return GatewayUtils.responseError(exchange, ErrorCodeEnum.AUTH_TOKEN_INVALID);
}
//验证用户是在别的地方登陆
if (verify.checkRedis(token, Integer.valueOf(platform))) {
return GatewayUtils.responseError(exchange, ErrorCodeEnum.AUTH_TOKEN_SWITCH);
}
try {
Claims claims = JwtUtils.infoJWT(token, Integer.valueOf(platform));
subject = URLEncoder.encode(claims.getSubject(), "utf-8");
} catch (UnsupportedEncodingException e) {
log.error("[{}] 请求参数:{}, 网关解析参数encode错误:{}", requestUrl, token, e);
return GatewayUtils.responseError(exchange, ErrorCodeEnum.SERVER);
}
}
if (!verify.checkWhiteIsPer(requestUrl)) {
//验证用户是否有权限访问
if (!verify.checkPermissions(token, Integer.valueOf(platform), requestUrl)) {
return GatewayUtils.responseError(exchange, ErrorCodeEnum.AUTH_PERMISSIONS);
}
}
exchange.mutate().request(exchange.getRequest().mutate().header(RequestConstant.HEADER_USER_LOGIN_INFO, subject).build()).build();
exchange.mutate().request(exchange.getRequest().mutate().header(RequestConstant.HEADER_ACCESS_TOKEN, token).build()).build();
exchange.mutate().request(exchange.getRequest().mutate().header(RequestConstant.HEADER_PLATFORM, platform).build()).build();
exchange.mutate().request(exchange.getRequest().mutate().header(RequestConstant.HEADER_SAFETY_LABEL_NAME,
RequestConstant.HEADER_SAFETY_LABEL_VAL).build()).build();
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 1;
}
}
package com.box.manage.gateway.filter;
import com.alibaba.fastjson.JSONObject;
import com.box.common.core.enums.ErrorCodeEnum;
import com.box.common.core.rt.Result;
import com.box.common.core.utils.*;
import io.netty.buffer.ByteBufAllocator;
import lombok.extern.slf4j.Slf4j;
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.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Optional;
/**
* 文件描述
*
* @author yuan.dingwang
* @date 2021年02月26日 15:35
*/
@Slf4j
@Component
public class ApiRequestXssSqllFilter implements GlobalFilter, Ordered {
private static final String START_TIME = "startTime";
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("----自定义网关全局过滤器生效----");
ServerHttpRequest serverHttpRequest = exchange.getRequest();
HttpMethod method = serverHttpRequest.getMethod();
String contentType = serverHttpRequest.getHeaders().getFirst(HttpHeaders.CONTENT_TYPE);
URI uri = exchange.getRequest().getURI();
//记录请求开始时间
exchange.getAttributes().put(START_TIME, SystemClockUtils.millisClock().now());
// 排除流文件类型,比如上传的文件contentType.contains("multipart/form-data")
if (GatewayUtils.isUploadFile(serverHttpRequest.getHeaders().getContentType())) {
log.info("{} - [{}] 文件上传请求放行", method, uri.getPath());
return chain.filter(exchange);
}
//过滤get请求
if (method == HttpMethod.GET) {
String rawQuery = uri.getRawQuery();
log.info("{} - [{}] 请求参数:{}", method, uri.getPath(), rawQuery);
if (StringUtils.isBlank(rawQuery)) {
return chain.filter(exchange);
}
// 执行sql注入校验清理
boolean chkRet = false;
try {
rawQuery = XssCleanRuleUtils.xssGetClean(rawQuery);
chkRet = SqLinjectionRuleUtils.getRequestSqlKeyWordsCheck(rawQuery);
} catch (UnsupportedEncodingException e) {
log.error("{} - [{}] 请求参数:{}, 网关解析参数错误:{}", method, uri.getPath(), rawQuery, e);
return GatewayUtils.responseError(exchange, ErrorCodeEnum.SERVER);
}
//如果存在sql注入,直接拦截请求
if (chkRet) {
log.info("请求【" + uri.getRawPath() + uri.getRawQuery() + "】参数中包含不允许sql的关键词, 请求拒绝");
return GatewayUtils.responseError(exchange, ErrorCodeEnum.CHECK_SQL_ILLEGAL);
}
//透传参数,不对参数做任何处理
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
Long startTime = exchange.getAttribute(START_TIME);
if (startTime != null) {
Long executeTime = (SystemClockUtils.millisClock().now() - startTime);
log.info("【{}】 耗时:{} ms", exchange.getRequest().getURI().getRawPath(), executeTime);
}
}));
} else if (method == HttpMethod.POST) {
//post请求时,如果是文件上传之类的请求,不修改请求消息体
return DataBufferUtils.join(serverHttpRequest.getBody()).flatMap(d -> Mono.just(Optional.of(d))).defaultIfEmpty(
Optional.empty())
.flatMap(optional -> {
// 取出body中的参数
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 = serverHttpRequest.getHeaders();
// 执行XSS清理
boolean chkRet = false;
log.info("{} - [{}] 请求参数:{}", method, uri.getPath(), bodyString);
if (MediaType.APPLICATION_JSON_VALUE.equals(contentType)) {
//如果MediaType是json才执行json方式验证
bodyString = XssCleanRuleUtils.xssPostClean(bodyString);
chkRet = SqLinjectionRuleUtils.postRequestSqlKeyWordsCheck(bodyString);
} else {
//form表单方式,需要走get请求
try {
bodyString = XssCleanRuleUtils.xssGetClean(bodyString);
chkRet = SqLinjectionRuleUtils.getRequestSqlKeyWordsCheck(bodyString);
} catch (UnsupportedEncodingException e) {
log.error("{} - [{}] 请求参数:{}, 网关解析参数错误:{}", method, uri.getPath(), bodyString, e);
return GatewayUtils.responseError(exchange, ErrorCodeEnum.SERVER);
}
}
// 如果存在sql注入,直接拦截请求
if (chkRet) {
log.info("{} - [{}] 参数:{}, 包含不允许sql的关键词,请求拒绝", method, uri.getPath(), bodyString);
return GatewayUtils.responseError(exchange, ErrorCodeEnum.CHECK_SQL_ILLEGAL);
}
ServerHttpRequest newRequest = serverHttpRequest.mutate().uri(uri).build();
// 重新构造body
byte[] newBytes = bodyString.getBytes(StandardCharsets.UTF_8);
DataBuffer bodyDataBuffer = GatewayUtils.toDataBuffer(newBytes);
Flux<DataBuffer> bodyFlux = Flux.just(bodyDataBuffer);
// 重新构造header
HttpHeaders headers = new HttpHeaders();
headers.putAll(httpHeaders);
// 由于修改了传递参数,需要重新设置CONTENT_LENGTH,长度是字节长度,不是字符串长度
int length = newBytes.length;
headers.remove(HttpHeaders.CONTENT_LENGTH);
headers.setContentLength(length);
headers.set(HttpHeaders.CONTENT_TYPE, serverHttpRequest.getHeaders().getFirst(HttpHeaders.CONTENT_TYPE));
// 重写ServerHttpRequestDecorator,修改了body和header,重写getBody和getHeaders方法
newRequest = new ServerHttpRequestDecorator(newRequest) {
@Override
public Flux<DataBuffer> getBody() {
return bodyFlux;
}
@Override
public HttpHeaders getHeaders() {
return headers;
}
};
exchange.getResponse();
return chain.filter(exchange.mutate().request(newRequest).build()).then(Mono.fromRunnable(() -> {
Long startTime = exchange.getAttribute(START_TIME);
if (startTime != null) {
Long executeTime = (SystemClockUtils.millisClock().now() - startTime);
log.info("【{}】 耗时:{} ms", exchange.getRequest().getURI().getRawPath(), executeTime);
}
}));
});
} else {
return GatewayUtils.responseError(exchange, ErrorCodeEnum.SERVER_NOT_METHOD);
}
}
@Override
public int getOrder() {
return 0;
}
}
package com.box.manage.gateway.filter;
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
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.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.net.URI;
import java.nio.charset.Charset;
/**
* 文件描述
*
* @author yuan.dingwang
* @date 2021年04月14日 10:26
*/
@Slf4j
@Component
public class ApoResponseGlobalFilter implements GlobalFilter, Ordered {
@Override
public int getOrder() {
// -1 is response write filter, must be called before that
return -2;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpResponse originalResponse = exchange.getResponse();
DataBufferFactory bufferFactory = originalResponse.bufferFactory();
HttpMethod method = exchange.getRequest().getMethod();
URI uri = exchange.getRequest().getURI();
ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
@Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
if (body instanceof Flux) {
Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
return super.writeWith(fluxBody.map(dataBuffer -> {
// probably should reuse buffers
byte[] content = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(content);
//释放掉内存
DataBufferUtils.release(dataBuffer);
String data = new String(content, Charset.forName("UTF-8"));
log.info("{} - [{}] 响应数据:{}", method, uri.getPath(), data);
//TODO,data就是response的值,想修改、查看就随意而为了
byte[] uppedContent = new String(content, Charset.forName("UTF-8")).getBytes();
return bufferFactory.wrap(uppedContent);
}));
}
// if body is not a flux. never got there.
return super.writeWith(body);
}
};
return chain.filter(exchange.mutate().response(decoratedResponse).build());
}
}
package com.box.manage.gateway.filter;
import com.box.common.core.constant.CacheKeyConstant;
import com.box.common.core.enums.PlatformEnum;
import com.box.common.core.utils.JwtUtils;
import com.box.common.core.utils.StringUtils;
import com.box.manage.gateway.config.WhiteListProperties;
import com.box.redis.service.RedisService;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* 验证类
*
* @author yuan.dingwang
* @date 2020年12月16日 16:54
*/
@Component
public class VerifyCondition {
@Autowired
RedisService redisService;
@Resource
WhiteListProperties whiteListProperties;
/**
* 验证平台端访问参数
*
* @param platform
* @return
*/
public boolean checkPlatform(String platform) {
//验证平台端编码是否正确
if (StringUtils.isBlank(platform)) {
return true;
}
//验证平台端编码是否正确
if (!PlatformEnum.isCheck(Integer.valueOf(platform))) {
return true;
}
return false;
}
/**
* 不需要登录的地址
*
* @param path
* @return
*/
public boolean checkWhiteIsLogin(String path) {
return whiteListProperties.loginVerify(path);
}
/**
* 不需要登录的地址
*
* @param path
* @return
*/
public boolean checkWhiteIsPer(String path) {
return whiteListProperties.perVerify(path);
}
/**
* 验证token是否有效
*
* @param token
* @return
*/
public boolean checkToken(String token, Integer platform) {
//验证token是否有效
if (StringUtils.isBlank(token)) {
return true;
}
//验证token是否有效
if (!JwtUtils.checkJWT(token, platform)) {
return true;
}
//验证用户是否过期
Claims claims = JwtUtils.infoJWT(token, platform);
if (StringUtils.isNull(claims) || StringUtils.isNull(claims.getId())
|| StringUtils.isNull(claims.get("roles"))) {
return true;
}
return false;
}
/**
* 验证用户是否在别处登录
*
* @param token
* @return
*/
public boolean checkRedis(String token, Integer platform) {
Claims claims = JwtUtils.infoJWT(token, platform);
//验证用户是否过期
StringBuffer key = new StringBuffer(CacheKeyConstant.CECHE_MANAGE_USER_TOKEN).append(platform).append(":").append(claims.getId());
Object dbToken = redisService.get(key.toString());
if (StringUtils.isNull(dbToken)) {
return true;
}
//验证用户是在别的地方登陆
if (!token.equals(dbToken.toString())) {
return true;
}
return false;
}
/**
* 验证权限
*
* @param token
* @param platform
* @param path
* @return
* /merchants/merchants/manage/getMerchantByPage 20210528000000191240
*/
public boolean checkPermissions(String token, Integer platform, String path) {
path = newPath(path);
Claims claims = JwtUtils.infoJWT(token, platform);
String roles = claims.get("roles").toString();
StringBuffer key = new StringBuffer(CacheKeyConstant.CECHE_MANAGE_PERMISSION).append(platform);
Object dbRoles = redisService.hget(key.toString(), path);
String[] roleArray = roles.split(",");
for (String role : roleArray) {
if (roles.equals("000000")){
//系统端超级管理员
return true;
}
}
if (StringUtils.isNull(dbRoles)) {
return false;
}
//当前用户角色id是否在dbRoles内
Boolean bool = false;
for (String role : roleArray) {
if (dbRoles.toString().contains(role)) {
bool = true;
break;
}
}
return bool;
}
public static void main(String[] args) {
//System.out.println("66666,22222".contains("66666,1111"));
String aa = "/open/manage/scoreBanck/openDeails";
String url = "/manage/scoreBanck/openDeails";
String r = "20210615000000462987,20210603000000384153,20210525000000128275,20210603000000446357,20210615000000368119,20210608000001015766,20210607000000578123,20210525000000128174";
VerifyCondition v = new VerifyCondition();
aa = aa.substring(5);
System.out.println(aa);
System.out.println(aa.equals(url));
}
private String newPath(String path) {
//return path.replace("/open", "");
return path.substring(5);
}
}