Commit 2c8ec2b3 by henry

修改gateway服务

1 parent 3647d763
Showing with 2002 additions and 18 deletions
...@@ -16,5 +16,94 @@ ...@@ -16,5 +16,94 @@
<maven.compiler.target>8</maven.compiler.target> <maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties> </properties>
<dependencies>
<!--(nacos统一配置中心管理依赖)-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-gateway-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<!--gateway依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--jwt-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
</dependency>
<!--SM4-CBC加密需要-->
<!-- https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<includeSystemScope>true</includeSystemScope>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project> </project>
\ No newline at end of file
package org.arch;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* Hello world!
*/
@SpringBootApplication
@EnableDiscoveryClient
public class GatewayApp {
public static void main(String[] args) {
SpringApplication.run(GatewayApp.class, args);
}
}
package org.arch;
public class Main {
public static void main(String[] args) {
System.out.println("Hello world!");
}
}
\ No newline at end of file
package org.arch.common;
public class Constant {
/**
* redis过期时间,以秒为单位,一分钟
*/
public static final int EXRP_MINUTE = 60;
/**
* redis过期时间,以秒为单位,一小时
*/
public static final int EXRP_HOUR = 60 * 60;
/**
* redis过期时间,以秒为单位,一天
*/
public static final int EXRP_DAY = 60 * 60 * 24;
/**
* redis-key-前缀-shiro:cache:
*/
public static final String PREFIX_SHIRO_CACHE = "shiro:cache:";
/**
* 用户信息缓存
*/
public static final String USER_ACCOUNT = "user_account:";
/**
* redis-key-前缀-shiro:access_token:
*/
public static final String PREFIX_SHIRO_ACCESS_TOKEN = "shiro:access_token:";
/**
* redis-key-前缀-shiro:refresh_token:
*/
public static final String PREFIX_SHIRO_REFRESH_TOKEN = "shiro:refresh_token:";
/**
* redis-key-前缀-tkv:refresh_token:
*/
public static final String PREFIX_TKV_REFRESH_TOKEN = "tkv:refresh_token:";
/**
* JWT-account:
*/
public static final String ACCOUNT = "account";
/**
* JWT-currentTimeMillis:
*/
public static final String CURRENT_TIME_MILLIS = "currentTimeMillis";
/**
* PASSWORD_MAX_LEN
*/
public static final Integer PASSWORD_MAX_LEN = 8;
//token
public static final int RESCODE_REFTOKEN_MSG = 1006; //刷新TOKEN(有返回数据)
public static final int RESCODE_REFTOKEN = 1007; //刷新TOKEN
public static final int JWT_ERRCODE_NULL = 4000; //Token不存在
public static final int JWT_ERRCODE_EXPIRE = 4001; //Token过期
public static final int JWT_ERRCODE_FAIL = 4002; //验证不通过
//密匙
public static final String JWT_SECRET = "8677df7fc87d23u87k61c89s54312hpk";
public static final String TOKEN_ISSUER="IT4IT";
//token失效的时间,单位:秒,默认设置3天
public static final Long ACCESS_TOKEN_EXPIRE = 7*24*60*60L;
//accessToken在header中的key
public static final String ACCESS_TOKEN_HEADER_KEY = "accessToken";
//用户sessionKey前缀
public static final String USER_SESSION_KEY = "User:Session:key:";
//用户令牌前缀
public static final String JWT_TOKEN_REDIS_KEY_PREFIX = "UserAuth:JWT:Key:";
//省市区redis key,缓存7天
public static final String AREA_REDIS_KEY = "areaKey";
public static final Long AREA_REDIS_EXPEPIRE = 7*24*60*60L;
//一次最多只能两个时段
public static final Integer EVERY_TIME_MAX_TWO_TIMES = 2;
//排队受理业务人均时长,单位:分钟
public static final Integer LINE_PER_TIMES = 3;
//订单到期规定的时间不能取消订单(分钟)
public static final Integer VALID_EXPIRE_TIME = 120;
//演示系统账户
public static String DEMO_ACCOUNT = "test";
//自动去除表前缀
public static String AUTO_REOMVE_PRE = "true";
//停止计划任务
public static String STATUS_RUNNING_STOP = "stop";
//开启计划任务
public static String STATUS_RUNNING_START = "start";
//通知公告阅读状态-未读
public static String OA_NOTIFY_READ_NO = "0";
//通知公告阅读状态-已读
public static int OA_NOTIFY_READ_YES = 1;
//部门根节点id
public static Long DEPT_ROOT_ID = 0l;
//缓存方式
public static String CACHE_TYPE_REDIS ="redis";
public static String LOG_ERROR = "error";
}
package org.arch.config;
import org.springframework.util.AntPathMatcher;
import java.util.ArrayList;
import java.util.List;
/**
* 不需要登录的请求路径
*/
public class AnonFilterChainDefinitionMap {
//不需要登录的路径
public static List<String> anonUrlList = new ArrayList<>();
static{
anonUrlList.add("/project/updateFile2");
anonUrlList.add("/adsproject/updateFile2");
anonUrlList.add("/file/webDownload");
anonUrlList.add("/directoryList/getJiagoutu");
anonUrlList.add("/project/getTableDatas");//架构表/
anonUrlList.add("/psrPlan/getPsrPlansByFsrId");////插入需求
anonUrlList.add("/expose/getEquipmentList");
anonUrlList.add("/expose/getBusList");
anonUrlList.add("/expose/getRelatedList");
anonUrlList.add("/auth/isclogin");////插入需求
anonUrlList.add("/auth/login");////插入需求
anonUrlList.add("/file/upload");////插入需求
anonUrlList.add("/version/getCurrentVersion");////插入需求
// 架构元模型管理
anonUrlList.add("/architecture/vi/file/getById");
}
/**
* 验证是否不需要登录
* @param url 路径
* @return 是否需要登录
*/
public static boolean isNotPermission(String url) {
AntPathMatcher matcher = new AntPathMatcher();
for (String p : anonUrlList) {
if (matcher.match(p,url)) {
return true;
}
}
return false;
}
}
package org.arch.config;
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.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
/**
* 把body中的数据缓存起来
* @author zg_xl920
* @create 2022-05-26 19:43
*/
@Component
public class CacheBodyGlobalFilter implements Ordered, GlobalFilter {
// public static final String CACHE_REQUEST_BODY_OBJECT_KEY = "cachedRequestBodyObject";
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
if (exchange.getRequest().getHeaders().getContentType() == null) {
return chain.filter(exchange);
} else {
return DataBufferUtils.join(exchange.getRequest().getBody())
.flatMap(dataBuffer -> {
DataBufferUtils.retain(dataBuffer);
Flux<DataBuffer> cachedFlux = Flux
.defer(() -> Flux.just(dataBuffer.slice(0, dataBuffer.readableByteCount())));
ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(
exchange.getRequest()) {
@Override
public Flux<DataBuffer> getBody() {
return cachedFlux;
}
};
//exchange.getAttributes().put(CACHE_REQUEST_BODY_OBJECT_KEY, cachedFlux);
return chain.filter(exchange.mutate().request(mutatedRequest).build());
});
}
}
@Override
public int getOrder() {
return -2;
}
}
package org.arch.config;//package com.it4it.config;
//
//import com.it4it.common.Constant;
//import com.it4it.service.RedisService;
//import com.sgcc.isc.ualogin.client.IscServiceTicketValidator;
//import com.sgcc.isc.ualogin.client.util.IscSSOResourceUtil;
//import com.sgcc.isc.ualogin.client.vo.IscSSOUserBean;
//import org.apache.commons.lang.StringUtils;
//import org.slf4j.Logger;
//import org.slf4j.LoggerFactory;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.beans.factory.annotation.Value;
//import org.springframework.core.Ordered;
//import org.xml.sax.SAXException;
//
//import javax.servlet.*;
//import javax.servlet.http.HttpServletRequest;
//import javax.servlet.http.HttpServletResponse;
//import javax.xml.parsers.ParserConfigurationException;
//import java.io.IOException;
//import java.util.concurrent.atomic.AtomicReference;
//
////import org.apache.shiro.web.util.WebUtils;
//
////import javax.servlet.*;
//
///**
// * 全局跨域放开
// *
// * @author wliduo[i@dolyw.com]
// * @date 2019/11/26 14:29
// */
////@Component
//public class OriginFilter_ISC implements Filter, Ordered {
//
// /**
// * logger
// */
// private static final Logger logger = LoggerFactory.getLogger(OriginFilter_ISC.class);
//
// @Value("${isc.serviceValidate}")
// private String isc_serviceValidate;
//
// @Value("${isc.sso_front_home_url}")
// private String isc_sso_front_home_url;
//
//
// @Autowired
// private RedisService redisService;
//
// @Override
// public void init(FilterConfig filterConfig) throws ServletException {
// logger.info("初始化全局跨域放开...");
// }
//
// @Override
// public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
//
// logger.info("111:--进入isc过滤器。。。。。。。。。。。。。。。。。。。。。。。。。。。");
// // HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
//// HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
// HttpServletRequest httpServletRequest = (HttpServletRequest)request;
// HttpServletResponse httpServletResponse = (HttpServletResponse)response;
//
// httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
// httpServletResponse.setHeader("Access-Control-Max-Age", "3600");
// httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
// httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
//// try {
//// IscSSOUserBean userbean2= IscSSOResourceUtil.getIscUserBean(((HttpServletRequest) request).getSession());
//// } catch (Exception e1) {
//// // TODO Auto-generated catch block
//// e1.printStackTrace();
//// }
// String ticket= httpServletRequest.getHeader("ticket");
// String authon=httpServletRequest.getHeader("Authorization"); //Authorization
//// String authon=JwtUtil.getClaim(Authorization, Constant.ACCOUNT);
// String tkv=httpServletRequest.getHeader("tkv"); //每个接口的刷新token值
// String method=httpServletRequest.getMethod();
//
// String uRl=httpServletRequest.getRequestURI();
// String uRl2=httpServletRequest.getRequestURL().toString();
// String uRl3=httpServletRequest.getContextPath();
////
// String user ="";
// String userid="";
// String errorCode;
// String errorMessage ;
// String xmlResponse;
// //httpServletRequest.getParameter("ticket")
// if(uRl.contains("project/download/pdf")){
// filterChain.doFilter(request, response);
// return;
// }
//
//
// if(method.equals("OPTIONS")){
// filterChain.doFilter(request, response);
// return;
// }
//
// AtomicReference<Boolean> isTrue = new AtomicReference<>(false);
// AnonFilterChainDefinitionMap.anonUrlList.forEach((item)->{
// if(uRl.contains(item)){
// isTrue.set(true);
// }
// });
// if(isTrue.get()){
// filterChain.doFilter(request, response);
// }
//
// if(StringUtils.isNotBlank(authon)){
// if(StringUtils.isNotBlank(tkv)&&StringUtils.isNotBlank(uRl)){
// String tkval=redisService.get(Constant.PREFIX_SHIRO_REFRESH_TOKEN + authon+":"+uRl);
// if(tkv.equals(tkval)){
// httpServletResponse.sendError(555, "无效请求");
// filterChain.doFilter(request, response);
// return;
// }
// redisService.set(Constant.PREFIX_SHIRO_REFRESH_TOKEN + authon+":"+uRl, tkv);
// }
// filterChain.doFilter(request, response);
// return;
// }
//
// /*判断ticket是否存在,不存在重定向到统一认证客户端*/
// if(null == ticket || "".equals(ticket)){
//
//// /*重定向到统一认证服务端,service参数是业务系统LoginModule请求地址*/
//// httpServletResponse.sendRedirect("http://27.50.21.55:17001/isc_sso/login?service=http://10.138.91.158:8082");
// httpServletResponse.sendError(555, "ticket_check_fail");
// filterChain.doFilter(request, response);
//// httpServletResponse.setStatus(401, "ticket_check_fail");
// return;
// }else{
// /* ticket校验器 */
// IscServiceTicketValidator sv = new IscServiceTicketValidator();
//
//
// /*统一认证服务端校验器地址*/
// sv.setCasValidateUrl(isc_serviceValidate);
//// sv.setCasValidateUrl("http://27.50.21.55:17001/isc_sso/serviceValidate");
// /*业务系统LoginModule访问地址*/
// sv.setService(isc_sso_front_home_url);
//// sv.setService("http://10.138.91.158:8082/");
// /*设置Ticket*/
// sv.setServiceTicket(ticket);
//
// /*校验*/
// try {
// sv.validate();
// } catch (SAXException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// } catch (ParserConfigurationException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
//
// xmlResponse = sv.getResponse();
// if(sv.isAuthenticationSuccesful()) {
// user = sv.getUser();
// IscSSOUserBean iscSSOUserBean = null;
// try {
// /*获取当前用户登录信息*/
// iscSSOUserBean = IscSSOResourceUtil.transferIscUserBean(user);
// /*当前登录用户ID*/
// userid = iscSSOUserBean.getIscUserId();
// //***************获取用户部门组织信息
//// List<BusinessOrganization> org= orgs.getBusiOrgsByUserId(userid, "c3482d816eb24dbc8b644444dd0fb0db");
//// BusiOrgNode mode= orgs.getAllBusiOrgsById("36776AA83E554E5E8D4A93CC19DA2912");//36776AA83E554E5E8D4A93CC19DA2912
//// System.out.println(userid);
// //***************
// /*当前登录用户账号*/
// String loginName = iscSSOUserBean.getIscUserSourceId();
//// System.out.println(loginName);
// httpServletResponse.setHeader("iscUser", userid);
// logger.info("userinfo >>>>>>>>>>>> "+user);
// } catch (Exception e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
// } else {
// errorCode = sv.getErrorCode();
// errorMessage = sv.getErrorMessage();
// httpServletResponse.sendError(555, "ticket_check_fail");
// /* handle the error */
// logger.error("errorInfo -----------> "+errorCode +"\r\n"+errorMessage);
//// return;
// }
//
//
// }
//
//
//
//
//
// filterChain.doFilter(request, response);
// }
//
// @Override
// public void destroy() {
//
// }
//
//// @Override
//// public void destroy() { }
//
//
// @Override
// public int getOrder() {
// return 1;
// }
//
//
//}
package org.arch.config;
import cn.hutool.crypto.symmetric.AES;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import io.netty.buffer.ByteBufAllocator;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Value;
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.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLDecoder;
import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
/**
* 解密过滤器
* @author zg_xl920
* @create 2022-05-26 15:47
*/
//@Component
public class ParamsEncryptionFilter implements GlobalFilter, Ordered {
@Value("${crypto.key}")
private String key;
@SneakyThrows
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
AES aes = new AES(key.getBytes());
ServerHttpRequest serverHttpRequest = exchange.getRequest();
HttpMethod method = serverHttpRequest.getMethod();
URI uri = serverHttpRequest.getURI();
//过滤上传文件的接口,不做解密处理
if (uri.toString().contains("upload")){
return chain.filter(exchange);
}
MediaType mediaType = serverHttpRequest.getHeaders().getContentType();
if (method == HttpMethod.POST){
//从请求里获取post请求体
String bodyStr = resolveBodyFromRequest(serverHttpRequest);
//密文参数
String cipherParams = "";
//明文参数
String plainParams = "";
if (bodyStr != null){
plainParams = aes.decryptStr(bodyStr);
}
ServerHttpRequest request = serverHttpRequest.mutate().uri(uri).build();
DataBuffer bodyDataBuffer = stringBuffer(plainParams);
Flux<DataBuffer> bodyFlux = Flux.just(bodyDataBuffer);
request = new ServerHttpRequestDecorator(request){
@Override
public Flux<DataBuffer> getBody() {
return bodyFlux;
}
};
//构建新的请求头
HttpHeaders headers = new HttpHeaders();
headers.putAll(exchange.getRequest().getHeaders());
// 重新设置CONTENT_LENGTH
int length = plainParams.getBytes().length;
headers.remove(HttpHeaders.CONTENT_LENGTH);
headers.setContentLength(length);
request = new ServerHttpRequestDecorator(request){
@Override
public HttpHeaders getHeaders() {
return headers;
}
};
return chain.filter(exchange.mutate().request(request).build());
} else if (method == HttpMethod.GET){
MultiValueMap<String, String> requestQueryParams = serverHttpRequest.getQueryParams();
//String rawQuery = uri.getRawQuery();
//
//String query = uri.getQuery();
if (requestQueryParams.isEmpty()){
return chain.filter(exchange);
}
//String params = requestQueryParams.get("0").get(0);
//String first = requestQueryParams.getFirst("0");
//解密
//params = aes.decryptStr(params);
Map<String, Object> map = this.dealGetRequestDecode(requestQueryParams, aes);
if (map.isEmpty()){
return chain.filter(exchange);
}
String stringParams = stringParams(map);
URI newUri = UriComponentsBuilder.fromUri(uri)
.replaceQuery(stringParams)
.build()
.toUri();
//封装url
//URI plaintUrl = new URI(uri.getScheme(), uri.getAuthority(), uri.getPath(), params, uri.getFragment());
//封装request,传给下一级
ServerHttpRequest request = serverHttpRequest.mutate().uri(newUri).build();
return chain.filter(exchange.mutate().request(request).build());
}
return chain.filter(exchange);
}
/**
* 前端get请求 加密之后需要解码请求参数
* @param requestQueryParams
* @param aes
* @return
*/
private Map<String,Object> dealGetRequestDecode(MultiValueMap<String, String> requestQueryParams, AES aes){
Map<String,Object> paras = new HashMap<>();
requestQueryParams.forEach((paramKey, encryptValue) -> {
for (String s : encryptValue) {
String params = aes.decryptStr(s);
JSONObject jsonObject = JSON.parseObject(params);
jsonObject.forEach((name,value) -> {
paras.put(name,value);
});
}
});
return paras;
}
/**
* 拼接路径参数
* @param params
* @return
*/
public String stringParams(Map<String, Object> params) {
StringBuffer sb = new StringBuffer();
for (Map.Entry<String, Object> param : params.entrySet()) {
if (param.getValue() instanceof List) {
List<Object> listObj = (List<Object>) param.getValue();
for (Object obj : listObj) {
if (sb.length() > 0) {
sb.append("&");
}
sb.append(param.getKey() + "=" + obj.toString());
}
} else { // String Int Long Dobule 之类的
if (sb.length() > 0) {
sb.append("&");
}
sb.append(param.getKey() + "=" + param.getValue().toString());
}
}
return sb.toString();
}
/**
* 解密过滤器必须在所有过滤器之前,否则后续过滤器获取参数会报错
* 如果有的其他的过滤器添加请调整过滤器顺序
* @return
*/
@Override
public int getOrder() {
return -1;
}
/**
* 根据请求方法类型获取密文
* @param bodyStr
* @param mediaType
* @return
* @throws UnsupportedEncodingException
*/
private String getCiphertextByMediaType(String bodyStr, MediaType mediaType) throws UnsupportedEncodingException {
/*if (mediaType.equals(MediaType.APPLICATION_JSON)){
JSONObject bodyJson = JSONObject.parseObject(bodyStr);
return bodyJson.getString("params");
}*/
if (mediaType.equals(MediaType.MULTIPART_FORM_DATA) || mediaType.equals(MediaType.APPLICATION_FORM_URLENCODED)){
Map<String, String> keyValues = urlSplit(bodyStr);
return URLDecoder.decode(keyValues.get("params"), "UTF-8");
} else {
return "-1";
}
}
/**
* 解析出url参数中的键值对
* 如 "Action=del&id=123",解析出Action:del,id:123存入map中
*
* @param params url地址
* @return url请求参数部分
*/
private static Map<String, String> urlSplit(String params) {
Map<String, String> mapRequest = new HashMap<>();
String[] arrSplit = null;
if (params == null) {
return mapRequest;
}
arrSplit = params.split("[&]");
for (String strSplit : arrSplit) {
String[] arrSplitEqual = null;
arrSplitEqual = strSplit.split("[=]");
//解析出键值
if (arrSplitEqual.length > 1) {
//正确解析
mapRequest.put(arrSplitEqual[0], arrSplitEqual[1]);
} else if (arrSplitEqual[0] != "") {
//只有参数没有值,不加入
mapRequest.put(arrSplitEqual[0], "");
}
}
return mapRequest;
}
private String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest) {
//获取请求体
Flux<DataBuffer> body = serverHttpRequest.getBody();
AtomicReference<String> bodyRef = new AtomicReference<>();
body.subscribe(buffer -> {
CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());
DataBufferUtils.release(buffer);
bodyRef.set(charBuffer.toString());
});
//获取request body
return bodyRef.get();
}
private DataBuffer stringBuffer(String value) {
byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);
DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length);
buffer.write(bytes);
return buffer;
}
}
package org.arch.config;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONObject;
import com.eadc.config.sm4jm.SM4Utils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* 国网SM4加密
*/
@Component
@ConditionalOnProperty(value = "sm4.enable", havingValue = "true")
public class ParamsSM4EncryptionFilter implements GlobalFilter, Ordered {
@Value("${sm4.CBCSecretKey}")
private String CBCSecretKey;
@Value("${sm4.CBCIV}")
private String CBCIV;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest serverHttpRequest = exchange.getRequest();
HttpMethod method = serverHttpRequest.getMethod();
String uri = serverHttpRequest.getURI().toString();
if (uri.contains("upload") || method != HttpMethod.POST) {
return chain.filter(exchange);
}
String bodyStr = serverHttpRequest.getBody().toString();
String plainParams = SM4Utils.decryptData_CBC(bodyStr, CBCSecretKey, CBCIV);
if (StrUtil.isBlank(plainParams)) {
return errorResponse(exchange, "已开启加密模式,请先加密");
}
return chain.filter(exchange);
}
private Mono<Void> errorResponse(ServerWebExchange exchange, String message) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.BAD_REQUEST);
response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
JSONObject json = new JSONObject();
json.put("code", 500);
json.put("msg", message);
json.put("data", "");
return response.writeWith(Mono.just(response.bufferFactory().wrap(json.toString().getBytes())));
}
@Override
public int getOrder() {
return -1;
}
}
\ No newline at end of file
package org.arch.config.fileFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
/**
* 限值文件上传
*/
public class FileUploadInterceptor implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
// 检查请求是否包含文件
if (request.getHeaders().getContentType() != null
&& request.getHeaders().getContentType().includes(MediaType.MULTIPART_FORM_DATA)) {
// 文件上传拦截逻辑
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
response.getHeaders().setContentType(MediaType.TEXT_PLAIN);
return response.writeWith(Mono.just(response.bufferFactory().wrap("File upload is not allowed.".getBytes(StandardCharsets.UTF_8))));
}
// 继续过滤器链
return chain.filter(exchange);
}
}
\ No newline at end of file
package org.arch.config.fileFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Objects;
/**
* 文件上传大小限制
*/
public class FileUploadSizeLimitFilter implements GlobalFilter {
private final int maxFileSize = 100 * 1024 * 1024; // 设置最大文件大小为100MB
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
// 如果是POST请求且内容类型是multipart/form-data,则检查文件大小
if ("POST".equals(request.getMethodValue()) && request.getHeaders().getContentType().includes(MediaType.MULTIPART_FORM_DATA)) {
return DataBufferUtils.join(exchange.getRequest().getBody())
.flatMap(dataBuffer -> {
try {
// 此处可以获取文件数据,进行大小校验
// 示例仅为检查是否超出最大文件大小
long contentLength = request.getHeaders().getContentLength();
if (!Objects.isNull(contentLength) && contentLength > maxFileSize) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(org.springframework.http.HttpStatus.PAYLOAD_TOO_LARGE);
// 可以返回错误信息到客户端
return response.writeWith(
Mono.just(response.bufferFactory().wrap(
"File size exceeds the maximum allowed limit.".getBytes()))
);
}
} catch (Exception e) {
// 处理异常
}
return chain.filter(exchange);
});
}
return chain.filter(exchange);
}
}
\ No newline at end of file
package org.arch.config.fileFilter;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
//@Configuration
public class FilterConfig {
@Bean
public GlobalFilter fileUploadSizeLimitFilter() {
return new FileUploadSizeLimitFilter();
}
}
\ No newline at end of file
package org.arch.config.fileFilter;//package com.eadc.config.fileFilter;
//
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.context.annotation.Configuration;
//import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
//import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
//
///**
// * 注册文件上传拦截器(做文件上传安全漏洞扫描使用)
// *
// */
////@Configuration
//public class WebFileConfig implements WebMvcConfigurer {
//
// @Autowired
// private FileUploadInterceptor fileUploadInterceptor;
//
// @Override
// public void addInterceptors(InterceptorRegistry registry) {
// registry.addInterceptor(fileUploadInterceptor)
// .addPathPatterns("/upload/**"); // 指定需要拦截的路径模式
// }
//}
package org.arch.config.sm4jm;
class SM4Context {
public int mode;
public int[] sk;
public boolean isPadding;
public SM4Context() {
this.mode = 1;
this.isPadding = true;
this.sk = new int[32];
}
}
package org.arch.config.tszf;
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.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.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;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
//@Component
@Slf4j
//@Order(3)
public class specialCharFilter 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);
}
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中的参数
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();
// 执行特殊字符清理
log.info("{} - [{}] tszf处理前参数:{}", method, uri.getPath(), bodyString);
// bodyString = XssUtil.INSTANCE.cleanXss(bodyString);
bodyString = this.StringFilter(bodyString);
log.info("{} - [{}] tszf处理后参数:{}", method, uri.getPath(), bodyString);
ServerHttpRequest newRequest = request.mutate().uri(uri).build();
// 重新构造body
byte[] newBytes = bodyString.getBytes(StandardCharsets.UTF_8);
DataBuffer bodyDataBuffer = 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, "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);
}
}
private String StringFilter(String str) throws PatternSyntaxException {
// 只允许字母和数字
// String regEx = "[^a-zA-Z0-9]";
// 清除掉所有特殊字符
String regEx = "[`~!@#$%^&*()+=|{}':;',//[//].<>/?~!@#¥%……&*()——+|{}【】‘;:”“’。,、?]";
Pattern p = Pattern.compile(regEx);
Matcher m = p.matcher(str);
return m.replaceAll("").trim();
}
/**
* 是否是白名单
*
* @param url 路由
* @param method 请求方式
* @return true/false
*/
private boolean white(String url, String method) {
return whiteUrls != null && whiteUrls.contains(XssWhiteUrl.builder().url(url).method(method).build());
}
/**
* 字节数组转DataBuffer
*
* @param bytes 字节数组
* @return DataBuffer
*/
private DataBuffer toDataBuffer(byte[] bytes) {
NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);
DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length);
buffer.write(bytes);
return buffer;
}
@Override
public int getOrder() {
return 0;
}
@Data
@Validated
@AllArgsConstructor
@NoArgsConstructor
@Builder
private static class XssWhiteUrl {
@NotEmpty
private String url;
@NotEmpty
private String method;
}
}
package org.arch.config.xssFilter;
import com.alibaba.fastjson.JSONObject;
import org.springframework.util.StringUtils;
/**
* JSON处理工具类
*
* @author lieber
*/
public enum JsonUtil {
/**
* 实例
*/
INSTANCE;
/**
* json对象字符串开始标记
*/
private final static String JSON_OBJECT_START = "{";
/**
* json对象字符串结束标记
*/
private final static String JSON_OBJECT_END = "}";
/**
* json数组字符串开始标记
*/
private final static String JSON_ARRAY_START = "[";
/**
* json数组字符串结束标记
*/
private final static String JSON_ARRAY_END = "]";
/**
* 判断字符串是否json对象字符串
*
* @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数组字符串
*
* @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;
}
/**
* 判断对象是否是json对象
*
* @param obj 待判断对象
* @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);
}
}
package org.arch.config.xssFilter;
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.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.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过滤
*
*/
//@Component
@Slf4j
//@ConfigurationProperties("config.xss")
@Data
//@Order(4)
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);
}
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中的参数
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();
// 执行XSS清理
log.info("{} - [{}] XSS处理前参数:{}", method, uri.getPath(), bodyString);
bodyString = XssUtil.INSTANCE.cleanXss(bodyString);
log.info("{} - [{}] XSS处理后参数:{}", method, uri.getPath(), bodyString);
ServerHttpRequest newRequest = request.mutate().uri(uri).build();
// 重新构造body
byte[] newBytes = bodyString.getBytes(StandardCharsets.UTF_8);
DataBuffer bodyDataBuffer = 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, "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 请求方式
* @return true/false
*/
private boolean white(String url, String method) {
return whiteUrls != null && whiteUrls.contains(XssWhiteUrl.builder().url(url).method(method).build());
}
/**
* 字节数组转DataBuffer
*
* @param bytes 字节数组
* @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
@Builder
private static class XssWhiteUrl {
@NotEmpty
private String url;
@NotEmpty
private String method;
}
}
package org.arch.config.xssFilter;
import com.alibaba.fastjson.JSONObject;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.safety.Safelist;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* xss拦截工具类
*
* @author lieber
*/
public enum XssUtil {
/**
* 实例
*/
INSTANCE;
private final static String RICH_TEXT = "</";
/**
* 自定义白名单
*/
private final static Safelist CUSTOM_WHITELIST = Safelist.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对象中的xss攻击字符
*
* @param val json对象字符串
* @return 清除后的json对象字符串
*/
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数组中的xss攻击字符
*
* @param val json数组字符串
* @return 清除后的json数组字符串
*/
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攻击字符串,此处优化空间较大
*
* @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, "参数包含特殊字符");
throw new RuntimeException("参数包含特殊字符");
}
str = richText ? afterClean : this.backSpecialStr(afterClean);
}
return str;
}
/**
* 判断是否是富文本
*
* @param str 待判断字符串
* @return true/false
*/
private boolean richText(String str) {
return str.contains(RICH_TEXT);
}
/**
* 判断是否参数错误
*
* @param richText 是否富文本
* @param afterClean 清理后字符
* @param str 原字符串
* @return true/false
*/
private boolean paramError(boolean richText, String afterClean, String str) {
// 如果包含富文本字符,那么不是参数错误
if (richText) {
return false;
}
// 如果清理后的字符和清理前的字符匹配,那么不是参数错误
if (Objects.equals(str, afterClean)) {
return false;
}
// 如果仅仅包含可以通过的特殊字符,那么不是参数错误
if (Objects.equals(str, this.backSpecialStr(afterClean))) {
return false;
}
// 如果还有......
return true;
}
/**
* 转义回特殊字符
*
* @param str 已经通过转义字符
* @return 转义后特殊字符
*/
private String backSpecialStr(String str) {
return str.replaceAll("&amp;", "&");
}
}
package org.arch.systemLog;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class GatewayLog {
/**请求来源**/
private String origin;
/**访问实例*/
private String targetServer;
/**请求路径*/
private String requestPath;
/**请求方法*/
private String requestMethod;
/**协议 */
private String schema;
/**请求类型 */
private String requestContentType;
/**请求头 */
private String headers;
/**请求体*/
private String requestBody;
/**响应体*/
private String responseData;
/**请求ip*/
private String ip;
/**IP所属城市*/
private String city;
/**开始时间*/
private Long startTime;
/**结束时间*/
private Long endTime;
/**请求时间*/
private String requestTime;
/**响应时间*/
private String responseTime;
/**执行时间*/
private long executeTime;
/**路由配置*/
private String routeConfig;
/**响应状态*/
private String status;
}
package org.arch.util;
import java.io.UnsupportedEncodingException;
import java.util.Base64;
/**
* Base64工具
* @author dolyw.com
* @date 2018/8/21 15:14
*/
public class Base64ConvertUtil {
private Base64ConvertUtil() {}
/**
* 加密JDK1.8
* @param str
* @return java.lang.String
* @author dolyw.com
* @date 2018/8/21 15:28
*/
public static String encode(String str) throws UnsupportedEncodingException {
byte[] encodeBytes = Base64.getEncoder().encode(str.getBytes("utf-8"));
return new String(encodeBytes);
}
/**
* 解密JDK1.8
* @param str
* @return java.lang.String
* @author dolyw.com
* @date 2018/8/21 15:28
*/
public static String decode(String str) throws UnsupportedEncodingException {
byte[] decodeBytes = Base64.getDecoder().decode(str.getBytes("utf-8"));
return new String(decodeBytes);
}
}
package org.arch.util;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import static org.springframework.web.context.request.RequestContextHolder.getRequestAttributes;
/**
* JAVA-JWT工具类
* @author Wang926454
* @date 2018/8/30 11:45
*/
@Component
public class JwtUtil {
/**
* logger
*/
private static final Logger logger = LoggerFactory.getLogger(JwtUtil.class);
/**
* 过期时间改为从配置文件获取
*/
private static String accessTokenExpireTime;
/**
* JWT认证加密私钥(Base64加密)
*/
private static String encryptJWTKey;
@Value("${accessTokenExpireTime}")
public void setAccessTokenExpireTime(String accessTokenExpireTime) {
JwtUtil.accessTokenExpireTime = accessTokenExpireTime;
}
public static HttpServletRequest getRequest() {
// RequestAttributes requestAttributes = getRequestAttributes();
return ((ServletRequestAttributes) getRequestAttributes()).getRequest();
}
public static String getToken() {
HttpServletRequest request = getRequest();
return request.getHeader("Authorization");
}
@Value("${encryptJWTKey}")
public void setEncryptJWTKey(String encryptJWTKey) {
JwtUtil.encryptJWTKey = encryptJWTKey;
}
/**
* 校验token是否正确
* @param token Token
* @return boolean 是否正确
* @author Wang926454
* @date 2018/8/31 9:05
*/
public static boolean verify(String token) {
try {
// 帐号加JWT私钥解密
String secret = getClaim(token,"account") + Base64ConvertUtil.decode(encryptJWTKey);
Algorithm algorithm = Algorithm.HMAC256(secret);
JWTVerifier verifier = JWT.require(algorithm).build();
verifier.verify(token);
return true;
} catch (Exception e) {
logger.error("JWTToken认证解密出现UnsupportedEncodingException异常:{}", e.getMessage());
return false;
// throw new RuntimeException("JWTToken认证解密出现UnsupportedEncodingException异常:" + e.getMessage());
}
}
/**
* 获得Token中的信息无需secret解密也能获得
* @param token
* @param claim
* @return java.lang.String
* @author Wang926454
* @date 2018/9/7 16:54
*/
public static String getClaim(String token, String claim) {
try {
DecodedJWT jwt = JWT.decode(token);
// 只能输出String类型,如果是其他类型返回null
return jwt.getClaim(claim).asString();
} catch (JWTDecodeException e) {
logger.error("解密Token中的公共信息出现JWTDecodeException异常:{},token:{}", e.getMessage(),token);
throw new RuntimeException("解密Token中的公共信息出现JWTDecodeException异常:" + e.getMessage()+",token---->"+token);
}
}
/**
* 用户
* @param account
* @param currentTimeMillis
* @return
*/
public static String sign(String account, String currentTimeMillis) throws Exception {
if(StringUtils.isBlank(account)){
throw new Exception("用户账户信息为空!");
}
try {
// 帐号加JWT私钥加密
String secret = account + Base64ConvertUtil.decode(encryptJWTKey);
// 此处过期时间是以毫秒为单位,所以乘以1000
Date date = new Date(System.currentTimeMillis() + Long.parseLong(accessTokenExpireTime) * 1000);
Algorithm algorithm = Algorithm.HMAC256(secret);
// 附带account帐号信息
return JWT.create()
.withClaim("account", account)
.withClaim("currentTimeMillis", currentTimeMillis)
.withExpiresAt(date)
.sign(algorithm);
} catch (UnsupportedEncodingException e) {
logger.error("JWTToken加密出现UnsupportedEncodingException异常:{}", e.getMessage());
throw new RuntimeException("JWTToken加密出现UnsupportedEncodingException异常:" + e.getMessage());
}
}
}
/**
* Copyright (C) 2018-2020
* All rights reserved, Designed By www.yixiang.co
* 注意:
* 本软件为www.yixiang.co开发研制
*/
package org.arch.util;
import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Calendar;
import java.util.Date;
/**
* @author Zheng Jie
* 字符串工具类, 继承org.apache.commons.lang3.StringUtils类
*/
public class StringUtils extends org.apache.commons.lang3.StringUtils {
private static final char SEPARATOR = '_';
private static final String UNKNOWN = "unknown";
/**
* 驼峰命名法工具
*
* @return toCamelCase(" hello_world ") == "helloWorld"
* toCapitalizeCamelCase("hello_world") == "HelloWorld"
* toUnderScoreCase("helloWorld") = "hello_world"
*/
public static String toCamelCase(String s) {
if (s == null) {
return null;
}
s = s.toLowerCase();
StringBuilder sb = new StringBuilder(s.length());
boolean upperCase = false;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == SEPARATOR) {
upperCase = true;
} else if (upperCase) {
sb.append(Character.toUpperCase(c));
upperCase = false;
} else {
sb.append(c);
}
}
return sb.toString();
}
/**
* 驼峰命名法工具
*
* @return toCamelCase(" hello_world ") == "helloWorld"
* toCapitalizeCamelCase("hello_world") == "HelloWorld"
* toUnderScoreCase("helloWorld") = "hello_world"
*/
public static String toCapitalizeCamelCase(String s) {
if (s == null) {
return null;
}
s = toCamelCase(s);
return s.substring(0, 1).toUpperCase() + s.substring(1);
}
/**
* 驼峰命名法工具
*
* @return toCamelCase(" hello_world ") == "helloWorld"
* toCapitalizeCamelCase("hello_world") == "HelloWorld"
* toUnderScoreCase("helloWorld") = "hello_world"
*/
static String toUnderScoreCase(String s) {
if (s == null) {
return null;
}
StringBuilder sb = new StringBuilder();
boolean upperCase = false;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
boolean nextUpperCase = true;
if (i < (s.length() - 1)) {
nextUpperCase = Character.isUpperCase(s.charAt(i + 1));
}
if ((i > 0) && Character.isUpperCase(c)) {
if (!upperCase || !nextUpperCase) {
sb.append(SEPARATOR);
}
upperCase = true;
} else {
upperCase = false;
}
sb.append(Character.toLowerCase(c));
}
return sb.toString();
}
/**
* 获取ip地址
*/
public static String getIp(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
String comma = ",";
String localhost = "127.0.0.1";
if (ip.contains(comma)) {
ip = ip.split(",")[0];
}
if (localhost.equals(ip)) {
// 获取本机真正的ip地址
try {
ip = InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
return ip;
}
/**
* 获得当天是周几
*/
public static String getWeekDay(){
String[] weekDays = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
Calendar cal = Calendar.getInstance();
cal.setTime(new Date());
int w = cal.get(Calendar.DAY_OF_WEEK) - 1;
if (w < 0){
w = 0;
}
return weekDays[w];
}
}
...@@ -72,9 +72,36 @@ ...@@ -72,9 +72,36 @@
<ooxml-security.version>1.1</ooxml-security.version> <ooxml-security.version>1.1</ooxml-security.version>
<ooxml-schemas.version>1.4</ooxml-schemas.version> <ooxml-schemas.version>1.4</ooxml-schemas.version>
<xmlbean.version>3.1.0</xmlbean.version> <xmlbean.version>3.1.0</xmlbean.version>
<java-jwt.version>3.2.0</java-jwt.version>
<jjwt.version>0.7.0</jjwt.version>
<javax.servlet-api.version>3.1.0</javax.servlet-api.version>
<jsoup.version>1.16.1</jsoup.version>
</properties> </properties>
<dependencyManagement> <dependencyManagement>
<dependencies> <dependencies>
<!--jwt-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>${java-jwt.version}</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>${jjwt.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${javax.servlet-api.version}</version>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>${jsoup.version}</version>
</dependency>
<!-- 文字识别 --> <!-- 文字识别 -->
<dependency> <dependency>
<groupId>io.github.mymonstercat</groupId> <groupId>io.github.mymonstercat</groupId>
...@@ -428,10 +455,6 @@ ...@@ -428,10 +455,6 @@
<artifactId>jedis</artifactId> <artifactId>jedis</artifactId>
<version>${jedis.version}</version> <version>${jedis.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!--监控sql日志--> <!--监控sql日志-->
<dependency> <dependency>
<groupId>org.bgee.log4jdbc-log4j2</groupId> <groupId>org.bgee.log4jdbc-log4j2</groupId>
...@@ -455,12 +478,6 @@ ...@@ -455,12 +478,6 @@
<artifactId>xmlbeans</artifactId> <artifactId>xmlbeans</artifactId>
<version>${xmlbean.version}</version> <version>${xmlbean.version}</version>
</dependency> </dependency>
<!-- fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>
...@@ -490,6 +507,10 @@ ...@@ -490,6 +507,10 @@
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId> <artifactId>spring-boot-starter-validation</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!--lombok插件--> <!--lombok插件-->
<dependency> <dependency>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
...@@ -509,6 +530,13 @@ ...@@ -509,6 +530,13 @@
</exclusion> </exclusion>
</exclusions> </exclusions>
</dependency> </dependency>
<!-- fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
</dependencies> </dependencies>
<build> <build>
<pluginManagement> <pluginManagement>
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!