Commit 42bc6412 by 陈皓

init

parents
HELP.md
.gradle
/build/
/logs/
**/build/
**/out/
**/logs/
!gradle/wrapper/gradle-wrapper.jar
*.log
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
/out/
**/target/
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.11.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.gxmailu</groupId>
<artifactId>gateway</artifactId>
<version>server-1.0.0</version>
<name>gateway</name>
<description>spring cloud 网关</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR11</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.75</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
package com.gxmailu.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
package com.gxmailu.gateway.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.util.pattern.PathPatternParser;
/**
* 跨域允许
*/
@Configuration
public class Cors {
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedMethod("*");
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
package com.gxmailu.gateway.entity;
import lombok.Data;
import lombok.ToString;
import java.util.Date;
@Data
@ToString
public class AppAuth {
private String id;
private String secretKey;
private String company;
private String appName;
private String os;
private String mac;
private String ip;
private String uris;
private Date expireDateTime;
public AppAuth() {
}
public AppAuth(String id, String secretKey) {
this.id = id;
this.secretKey = secretKey;
}
}
package com.gxmailu.gateway.filter;
import com.alibaba.fastjson.JSONObject;
import com.gxmailu.gateway.entity.AppAuth;
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.http.MediaType;
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.Flux;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
@Component
@Slf4j
public class AuthFilter implements GlobalFilter, Ordered {
private Map<String, AppAuth> gatewayAuthMap = new HashMap<>();
private Map<String, Integer> qpsMap = new HashMap<>();
@Override
public int getOrder() {
return 0;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
log.info("IP地址:[{}] 访问地址:[{}] 请求参数:{}", request.getRemoteAddress().getAddress().getHostAddress(),
request.getURI().getPath(), request.getQueryParams());
//System.out.println(request.getRemoteAddress().getAddress().getHostAddress());
/*
HttpHeaders headers = request.getHeaders();
ServerHttpResponse response = exchange.getResponse();
List<String> appIds = headers.get("appId");
if (appIds == null) {
response.setStatusCode(HttpStatus.BAD_REQUEST);
return getVoidMono(exchange, response, 400, "请求头中的 [appId] 不能为空。");
}
List<String> secretKeys = headers.get("secretKey");
if (secretKeys == null) {
response.setStatusCode(HttpStatus.BAD_REQUEST);
return getVoidMono(exchange, response, 400, "请求头中的 [secretKey] 不能为空。");
}
String appId = appIds.get(0);
String secretKey = secretKeys.get(0);
AppAuth appAuth = gatewayAuthMap.get(appId);
String uri = request.getURI().getPath();
JSONObject uriJson = null;
if (appAuth == null) {
String decode = AESUtil.decode(secretKey);
if (decode == null) {
response.setStatusCode(HttpStatus.BAD_REQUEST);
return getVoidMono(exchange, response, 400, "[secretKey] 有误。");
}
appAuth = JSONObject.parseObject(decode, AppAuth.class);
if (Objects.isNull(appAuth)) {
response.setStatusCode(HttpStatus.BAD_REQUEST);
return getVoidMono(exchange, response, 400, "[secretKey] 有误。");
}
if (!appAuth.getId().equals(appId)) {
response.setStatusCode(HttpStatus.BAD_REQUEST);
return getVoidMono(exchange, response, 400, "[appId] 有误。");
}
uriJson = JSONObject.parseObject(appAuth.getUris());
if (Objects.isNull(appAuth.getUris()) || Objects.isNull(uriJson.getInteger(uri))) {
response.setStatusCode(HttpStatus.FORBIDDEN);
return getVoidMono(exchange, response, 403, "您没有请求URI [" + uri + "] 的权限。");
}
if (uriJson.getInteger(uri).equals(0)) {
response.setStatusCode(HttpStatus.FORBIDDEN);
return getVoidMono(exchange, response, 403, "您的请求已达到极限, [" + uri + "] 每秒请求并发数:[" + uriJson.getInteger(uri) + "]。");
}
for (String key : uriJson.keySet()) {
qpsMap.put(appId + "_" + key, 0);
}
appAuth.setSecretKey(secretKey);
gatewayAuthMap.put(appId, appAuth);
}
if (!appAuth.getSecretKey().equals(secretKey)) {
String decode = AESUtil.decode(secretKey);
if (decode == null) {
response.setStatusCode(HttpStatus.BAD_REQUEST);
return getVoidMono(exchange, response, 400, "[secretKey] 有误。");
}
appAuth = JSONObject.parseObject(decode, AppAuth.class);
if (Objects.isNull(appAuth)) {
response.setStatusCode(HttpStatus.BAD_REQUEST);
return getVoidMono(exchange, response, 400, "[secretKey] 有误。");
}
if (!appAuth.getId().equals(appId)) {
response.setStatusCode(HttpStatus.BAD_REQUEST);
return getVoidMono(exchange, response, 400, "[appId] 有误");
}
uriJson = JSONObject.parseObject(appAuth.getUris());
if (Objects.isNull(appAuth.getUris()) || Objects.isNull(uriJson.getInteger(uri))) {
response.setStatusCode(HttpStatus.FORBIDDEN);
return getVoidMono(exchange, response, 403, "您没有请求URI [" + uri + "] 的权限。");
}
if (uriJson.getInteger(uri).equals(0)) {
response.setStatusCode(HttpStatus.FORBIDDEN);
return getVoidMono(exchange, response, 403, "您的请求已达到极限, [" + uri + "] 每秒请求并发数:[" + uriJson.getInteger(uri) + "]。");
}
qpsMap.keySet().removeIf(key -> key.startsWith(appId));
for (String key : uriJson.keySet()) {
qpsMap.put(appId + "_" + key, 0);
}
appAuth.setSecretKey(secretKey);
gatewayAuthMap.put(appId, appAuth);
}
String requestIP = request.getRemoteAddress().getAddress().getHostAddress();
if (!appAuth.getIp().equals(requestIP)) {
response.setStatusCode(HttpStatus.FORBIDDEN);
return getVoidMono(exchange, response, 403, "您的服务器未授权。");
}
if (appAuth.getExpireDateTime().getTime() < new Date().getTime()) {
response.setStatusCode(HttpStatus.FORBIDDEN);
return getVoidMono(exchange, response, 403, "您的授权已过期。");
}
String qpsId = appId + "_" + uri;
if (qpsMap.get(qpsId) == null) {
response.setStatusCode(HttpStatus.FORBIDDEN);
return getVoidMono(exchange, response, 403, "您没有请求URI [" + uri + "] 的权限。");
}
if (qpsMap.get(qpsId).equals(0)) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
qpsMap.put(qpsId, 0);
timer.cancel();
}
}, 1000);
}
uriJson = JSONObject.parseObject(appAuth.getUris());
qpsMap.put(qpsId, qpsMap.get(qpsId) + 1);
if (qpsMap.get(qpsId) > uriJson.getInteger(uri)) {
response.setStatusCode(HttpStatus.FORBIDDEN);
return getVoidMono(exchange, response, 403, "您的请求已达到极限, [" + uri + "] 每秒请求并发数:[" + uriJson.getInteger(uri) + "]。");
}
*/
return chain.filter(exchange);
}
private Mono<Void> getVoidMono(ServerWebExchange exchange, ServerHttpResponse response, int code, String msg) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("code", code);
jsonObject.put("msg", msg);
response.getHeaders().setContentType(new MediaType("application", "json", StandardCharsets.UTF_8));
DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(jsonObject.toJSONString().getBytes(StandardCharsets.UTF_8));
return response.writeWith(Flux.just(buffer));
}
}
package com.gxmailu.gateway.util;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
@Slf4j
public class AESUtil {
private static String key() {
return "GXMaiLuCloudGatewayHys20210601Create";
}
/**
* 加密
* 1.构造密钥生成器
* 2.根据ecnodeRules规则初始化密钥生成器
* 3.产生密钥
* 4.创建和初始化密码器
* 5.内容加密
* 6.返回字符串
*/
public static String encode(String content) {
try {
JSONObject jsonObject = getCipher();
Cipher cipher = (Cipher) jsonObject.get("cipher");
SecretKeySpec key = (SecretKeySpec) jsonObject.get("key");
//7.初始化密码器,第一个参数为加密(Encrypt_mode)或者解密解密(Decrypt_mode)操作,第二个参数为使用的KEY
cipher.init(Cipher.ENCRYPT_MODE, key);
//8.获取加密内容的字节数组(这里要设置为utf-8)不然内容中如果有中文和英文混合中文就会解密为乱码
byte[] byte_encode = content.getBytes(StandardCharsets.UTF_8);
//9.根据密码器的初始化方式--加密:将数据加密
byte[] byte_AES = cipher.doFinal(byte_encode);
//10.将加密后的数据转换为字符串
//这里用Base64Encoder中会找不到包
//解决办法:
//在项目的Build path中先移除JRE System Library,再添加库JRE System Library,重新编译后就一切正常了。
//11.将字符串返回
return new BASE64Encoder().encode(byte_AES);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
log.error("加密失败:{}", e.getMessage());
}
//如果有错就返加nulll
return null;
}
private static JSONObject getCipher() throws NoSuchAlgorithmException, NoSuchPaddingException {
//1.构造密钥生成器,指定为AES算法,不区分大小写
KeyGenerator keygen = KeyGenerator.getInstance("AES");
//2.根据ecnodeRules规则初始化密钥生成器
//生成一个128位的随机源,根据传入的字节数组
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
random.setSeed(key().getBytes());
keygen.init(128, random);
//3.产生原始对称密钥
SecretKey original_key = keygen.generateKey();
//4.获得原始对称密钥的字节数组
byte[] raw = original_key.getEncoded();
//5.根据字节数组生成AES密钥
//6.根据指定算法AES自成密码器
JSONObject jsonObject = new JSONObject();
jsonObject.put("cipher", Cipher.getInstance("AES"));
jsonObject.put("key", new SecretKeySpec(raw, "AES"));
return jsonObject;
}
/**
* AES加密
*
* @param content 待加密的内容
* @param encryptKey 加密密钥
* @return 加密后的byte[]
* @throws Exception
*/
public static byte[] aesEncryptToBytes(String content, String encryptKey) throws Exception {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128, new SecureRandom(encryptKey.getBytes()));
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(kgen.generateKey().getEncoded(), "AES"));
return cipher.doFinal(content.getBytes(StandardCharsets.UTF_8));
}
/**
* 解密
* 解密过程:
* 1.同加密1-4步
* 2.将加密后的字符串反纺成byte[]数组
* 3.将加密内容解密
*/
public static String decode(String content) {
try {
JSONObject jsonObject = getCipher();
Cipher cipher = (Cipher) jsonObject.get("cipher");
SecretKeySpec key = (SecretKeySpec) jsonObject.get("key");
//7.初始化密码器,第一个参数为加密(Encrypt_mode)或者解密(Decrypt_mode)操作,第二个参数为使用的KEY
cipher.init(Cipher.DECRYPT_MODE, key);
//8.将加密并编码后的内容解码成字节数组
byte[] byte_content = new BASE64Decoder().decodeBuffer(content);
/*
* 解密
*/
byte[] byte_decode = cipher.doFinal(byte_content);
return new String(byte_decode, StandardCharsets.UTF_8);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IOException | IllegalBlockSizeException | BadPaddingException e) {
log.error("解密失败:{}", e.getMessage());
}
//如果有错就返加nulll
return null;
}
/**
* AES解密
*
* @param encryptBytes 待解密的byte[]
* @param decryptKey 解密密钥
* @return 解密后的String
* @throws Exception
*/
public static String aesDecryptByBytes(byte[] encryptBytes, String decryptKey) throws Exception {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128, new SecureRandom(decryptKey.getBytes()));
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(kgen.generateKey().getEncoded(), "AES"));
byte[] decryptBytes = cipher.doFinal(encryptBytes);
return new String(decryptBytes);
}
/**
* base 64 加密
*
* @param bytes 待编码的byte[]
* @return 编码后的base 64 code
*/
public static String base64Encode(byte[] bytes) {
return new BASE64Encoder().encode(bytes);
}
/**
* base 64 解密
*
* @param base64Code 待解码的base 64 code
* @return 解码后的byte[]
* @throws Exception
*/
public static byte[] base64Decode(String base64Code) throws Exception {
return StringUtils.isEmpty(base64Code) ? null : new BASE64Decoder().decodeBuffer(base64Code);
}
/**
* 将base 64 code AES解密
*
* @param encryptStr 待解密的base 64 code
* @param decryptKey 解密密钥
* @return 解密后的string
* @throws Exception
*/
public static String aesDecrypt(String encryptStr, String decryptKey) throws Exception {
return StringUtils.isEmpty(encryptStr) ? null : aesDecryptByBytes(base64Decode(encryptStr), decryptKey);
}
/**
* AES加密为base 64 code
*
* @param content 待加密的内容
* @param encryptKey 加密密钥
* @return 加密后的base 64 code
* @throws Exception
*/
public static String aesEncrypt(String content, String encryptKey) throws Exception {
return base64Encode(aesEncryptToBytes(content, encryptKey));
}
}
package com.gxmailu.gateway.util;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateUtil {
public static String getDate(String format) {
return new SimpleDateFormat(format).format(new Date());
}
public static String getDate(String format, Date date) {
return new SimpleDateFormat(format).format(date);
}
/**
* 时间戳转换成日期格式字符串
*
* @param seconds 精确到秒的字符串
* @param format
* @return
*/
public static String timeStamp2Date(String seconds, String format) {
if (seconds == null || seconds.isEmpty() || seconds.equals("null")) {
return "";
}
if (format == null || format.isEmpty()) {
format = "yyyy-MM-dd HH:mm:ss";
}
SimpleDateFormat sdf = new SimpleDateFormat(format);
return sdf.format(new Date(Long.parseLong(seconds)));
}
/**
* 日期格式字符串转换成时间戳
*
* @param dateStr 字符串日期
* @param format 如:yyyy-MM-dd HH:mm:ss
* @return
*/
public static String date2TimeStamp(String dateStr, String format) {
try {
SimpleDateFormat sdf = new SimpleDateFormat(format);
return String.valueOf(sdf.parse(dateStr).getTime() / 1000);
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
/**
* 日期格式化
*
* @return
*/
public static String dateToStr() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.format(new Date());
}
/**
* 日期格式化
*
* @param date 日期对象
* @return
*/
public static String dateToStr(Date date) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.format(date);
}
/**
* 日期格式化
*
* @param format 格式化字符串
* @return
*/
public static String dateToStr(String format) {
SimpleDateFormat sdf = new SimpleDateFormat(format);
return sdf.format(new Date());
}
/**
* 日期格式化
*
* @param date 日期对象
* @param format 格式化字符串
* @return
*/
public static String dateToStr(Date date, String format) {
SimpleDateFormat sdf = new SimpleDateFormat(format);
return sdf.format(date);
}
/**
* 字符串转换成日期
*
* @param str 日期字符串
* @return date
*/
public static Date strToDate(String str) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = null;
try {
date = format.parse(str);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
/**
* 取得当前时间戳(精确到秒)
*
* @return
*/
public static String timeStamp() {
long time = System.currentTimeMillis();
return String.valueOf(time / 1000);
}
}
package com.gxmailu.gateway.util;
import java.util.UUID;
public class UUIDUtil {
public static String create() {
return UUID.randomUUID().toString().replaceAll("-", "");
}
/* public static void main(String[] args) {
System.out.println(UUID.randomUUID().toString().toUpperCase());
}*/
}
server:
port: 8888
tomcat:
max-http-form-post-size: -1
spring:
application:
name: gateway
cloud:
gateway:
routes:
- id: ocrCloudPlatform
uri: lb://ocrCloudPlatformApi
predicates:
- Path=/**
filters:
- StripPrefix=0
eureka:
client:
service-url:
defaultZone: http://localhost:9000/eureka
instance:
prefer-ip-address: true
instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}
lease-expiration-duration-in-seconds: 30
lease-renewal-interval-in-seconds: 10
logging:
level:
com.netflix: warn
spring:
profiles:
active: mldev
package com.gxmailu.gateway;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class GatewayApplicationTests {
@Test
void contextLoads() {
}
}
package com.gxmailu.auth;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class AuthApplicationTests {
@Test
void contextLoads() {
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.11.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.gxmailu</groupId>
<artifactId>gateway</artifactId>
<version>server-1.0.0</version>
<name>gateway2</name>
<description>spring cloud 网关</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR11</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.75</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
package com.gxmailu.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
package com.gxmailu.gateway.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.util.pattern.PathPatternParser;
/**
* 跨域允许
*/
@Configuration
public class Cors {
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedMethod("*");
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
package com.gxmailu.gateway.entity;
import lombok.Data;
import lombok.ToString;
import java.util.Date;
@Data
@ToString
public class AppAuth {
private String id;
private String secretKey;
private String company;
private String appName;
private String os;
private String mac;
private String ip;
private String uris;
private Date expireDateTime;
public AppAuth() {
}
public AppAuth(String id, String secretKey) {
this.id = id;
this.secretKey = secretKey;
}
}
package com.gxmailu.gateway.filter;
import com.alibaba.fastjson.JSONObject;
import com.gxmailu.gateway.entity.AppAuth;
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.http.MediaType;
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.Flux;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
@Component
@Slf4j
public class AuthFilter implements GlobalFilter, Ordered {
private Map<String, AppAuth> gatewayAuthMap = new HashMap<>();
private Map<String, Integer> qpsMap = new HashMap<>();
@Override
public int getOrder() {
return 0;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
log.info("IP地址:[{}] 访问地址:[{}] 请求参数:{}", request.getRemoteAddress().getAddress().getHostAddress(),
request.getURI().getPath(), request.getQueryParams());
//System.out.println(request.getRemoteAddress().getAddress().getHostAddress());
/*
HttpHeaders headers = request.getHeaders();
ServerHttpResponse response = exchange.getResponse();
List<String> appIds = headers.get("appId");
if (appIds == null) {
response.setStatusCode(HttpStatus.BAD_REQUEST);
return getVoidMono(exchange, response, 400, "请求头中的 [appId] 不能为空。");
}
List<String> secretKeys = headers.get("secretKey");
if (secretKeys == null) {
response.setStatusCode(HttpStatus.BAD_REQUEST);
return getVoidMono(exchange, response, 400, "请求头中的 [secretKey] 不能为空。");
}
String appId = appIds.get(0);
String secretKey = secretKeys.get(0);
AppAuth appAuth = gatewayAuthMap.get(appId);
String uri = request.getURI().getPath();
JSONObject uriJson = null;
if (appAuth == null) {
String decode = AESUtil.decode(secretKey);
if (decode == null) {
response.setStatusCode(HttpStatus.BAD_REQUEST);
return getVoidMono(exchange, response, 400, "[secretKey] 有误。");
}
appAuth = JSONObject.parseObject(decode, AppAuth.class);
if (Objects.isNull(appAuth)) {
response.setStatusCode(HttpStatus.BAD_REQUEST);
return getVoidMono(exchange, response, 400, "[secretKey] 有误。");
}
if (!appAuth.getId().equals(appId)) {
response.setStatusCode(HttpStatus.BAD_REQUEST);
return getVoidMono(exchange, response, 400, "[appId] 有误。");
}
uriJson = JSONObject.parseObject(appAuth.getUris());
if (Objects.isNull(appAuth.getUris()) || Objects.isNull(uriJson.getInteger(uri))) {
response.setStatusCode(HttpStatus.FORBIDDEN);
return getVoidMono(exchange, response, 403, "您没有请求URI [" + uri + "] 的权限。");
}
if (uriJson.getInteger(uri).equals(0)) {
response.setStatusCode(HttpStatus.FORBIDDEN);
return getVoidMono(exchange, response, 403, "您的请求已达到极限, [" + uri + "] 每秒请求并发数:[" + uriJson.getInteger(uri) + "]。");
}
for (String key : uriJson.keySet()) {
qpsMap.put(appId + "_" + key, 0);
}
appAuth.setSecretKey(secretKey);
gatewayAuthMap.put(appId, appAuth);
}
if (!appAuth.getSecretKey().equals(secretKey)) {
String decode = AESUtil.decode(secretKey);
if (decode == null) {
response.setStatusCode(HttpStatus.BAD_REQUEST);
return getVoidMono(exchange, response, 400, "[secretKey] 有误。");
}
appAuth = JSONObject.parseObject(decode, AppAuth.class);
if (Objects.isNull(appAuth)) {
response.setStatusCode(HttpStatus.BAD_REQUEST);
return getVoidMono(exchange, response, 400, "[secretKey] 有误。");
}
if (!appAuth.getId().equals(appId)) {
response.setStatusCode(HttpStatus.BAD_REQUEST);
return getVoidMono(exchange, response, 400, "[appId] 有误");
}
uriJson = JSONObject.parseObject(appAuth.getUris());
if (Objects.isNull(appAuth.getUris()) || Objects.isNull(uriJson.getInteger(uri))) {
response.setStatusCode(HttpStatus.FORBIDDEN);
return getVoidMono(exchange, response, 403, "您没有请求URI [" + uri + "] 的权限。");
}
if (uriJson.getInteger(uri).equals(0)) {
response.setStatusCode(HttpStatus.FORBIDDEN);
return getVoidMono(exchange, response, 403, "您的请求已达到极限, [" + uri + "] 每秒请求并发数:[" + uriJson.getInteger(uri) + "]。");
}
qpsMap.keySet().removeIf(key -> key.startsWith(appId));
for (String key : uriJson.keySet()) {
qpsMap.put(appId + "_" + key, 0);
}
appAuth.setSecretKey(secretKey);
gatewayAuthMap.put(appId, appAuth);
}
String requestIP = request.getRemoteAddress().getAddress().getHostAddress();
if (!appAuth.getIp().equals(requestIP)) {
response.setStatusCode(HttpStatus.FORBIDDEN);
return getVoidMono(exchange, response, 403, "您的服务器未授权。");
}
if (appAuth.getExpireDateTime().getTime() < new Date().getTime()) {
response.setStatusCode(HttpStatus.FORBIDDEN);
return getVoidMono(exchange, response, 403, "您的授权已过期。");
}
String qpsId = appId + "_" + uri;
if (qpsMap.get(qpsId) == null) {
response.setStatusCode(HttpStatus.FORBIDDEN);
return getVoidMono(exchange, response, 403, "您没有请求URI [" + uri + "] 的权限。");
}
if (qpsMap.get(qpsId).equals(0)) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
qpsMap.put(qpsId, 0);
timer.cancel();
}
}, 1000);
}
uriJson = JSONObject.parseObject(appAuth.getUris());
qpsMap.put(qpsId, qpsMap.get(qpsId) + 1);
if (qpsMap.get(qpsId) > uriJson.getInteger(uri)) {
response.setStatusCode(HttpStatus.FORBIDDEN);
return getVoidMono(exchange, response, 403, "您的请求已达到极限, [" + uri + "] 每秒请求并发数:[" + uriJson.getInteger(uri) + "]。");
}
*/
return chain.filter(exchange);
}
private Mono<Void> getVoidMono(ServerWebExchange exchange, ServerHttpResponse response, int code, String msg) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("code", code);
jsonObject.put("msg", msg);
response.getHeaders().setContentType(new MediaType("application", "json", StandardCharsets.UTF_8));
DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(jsonObject.toJSONString().getBytes(StandardCharsets.UTF_8));
return response.writeWith(Flux.just(buffer));
}
}
package com.gxmailu.gateway.util;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
@Slf4j
public class AESUtil {
private static String key() {
return "GXMaiLuCloudGatewayHys20210601Create";
}
/**
* 加密
* 1.构造密钥生成器
* 2.根据ecnodeRules规则初始化密钥生成器
* 3.产生密钥
* 4.创建和初始化密码器
* 5.内容加密
* 6.返回字符串
*/
public static String encode(String content) {
try {
JSONObject jsonObject = getCipher();
Cipher cipher = (Cipher) jsonObject.get("cipher");
SecretKeySpec key = (SecretKeySpec) jsonObject.get("key");
//7.初始化密码器,第一个参数为加密(Encrypt_mode)或者解密解密(Decrypt_mode)操作,第二个参数为使用的KEY
cipher.init(Cipher.ENCRYPT_MODE, key);
//8.获取加密内容的字节数组(这里要设置为utf-8)不然内容中如果有中文和英文混合中文就会解密为乱码
byte[] byte_encode = content.getBytes(StandardCharsets.UTF_8);
//9.根据密码器的初始化方式--加密:将数据加密
byte[] byte_AES = cipher.doFinal(byte_encode);
//10.将加密后的数据转换为字符串
//这里用Base64Encoder中会找不到包
//解决办法:
//在项目的Build path中先移除JRE System Library,再添加库JRE System Library,重新编译后就一切正常了。
//11.将字符串返回
return new BASE64Encoder().encode(byte_AES);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
log.error("加密失败:{}", e.getMessage());
}
//如果有错就返加nulll
return null;
}
private static JSONObject getCipher() throws NoSuchAlgorithmException, NoSuchPaddingException {
//1.构造密钥生成器,指定为AES算法,不区分大小写
KeyGenerator keygen = KeyGenerator.getInstance("AES");
//2.根据ecnodeRules规则初始化密钥生成器
//生成一个128位的随机源,根据传入的字节数组
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
random.setSeed(key().getBytes());
keygen.init(128, random);
//3.产生原始对称密钥
SecretKey original_key = keygen.generateKey();
//4.获得原始对称密钥的字节数组
byte[] raw = original_key.getEncoded();
//5.根据字节数组生成AES密钥
//6.根据指定算法AES自成密码器
JSONObject jsonObject = new JSONObject();
jsonObject.put("cipher", Cipher.getInstance("AES"));
jsonObject.put("key", new SecretKeySpec(raw, "AES"));
return jsonObject;
}
/**
* AES加密
*
* @param content 待加密的内容
* @param encryptKey 加密密钥
* @return 加密后的byte[]
* @throws Exception
*/
public static byte[] aesEncryptToBytes(String content, String encryptKey) throws Exception {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128, new SecureRandom(encryptKey.getBytes()));
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(kgen.generateKey().getEncoded(), "AES"));
return cipher.doFinal(content.getBytes(StandardCharsets.UTF_8));
}
/**
* 解密
* 解密过程:
* 1.同加密1-4步
* 2.将加密后的字符串反纺成byte[]数组
* 3.将加密内容解密
*/
public static String decode(String content) {
try {
JSONObject jsonObject = getCipher();
Cipher cipher = (Cipher) jsonObject.get("cipher");
SecretKeySpec key = (SecretKeySpec) jsonObject.get("key");
//7.初始化密码器,第一个参数为加密(Encrypt_mode)或者解密(Decrypt_mode)操作,第二个参数为使用的KEY
cipher.init(Cipher.DECRYPT_MODE, key);
//8.将加密并编码后的内容解码成字节数组
byte[] byte_content = new BASE64Decoder().decodeBuffer(content);
/*
* 解密
*/
byte[] byte_decode = cipher.doFinal(byte_content);
return new String(byte_decode, StandardCharsets.UTF_8);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IOException | IllegalBlockSizeException | BadPaddingException e) {
log.error("解密失败:{}", e.getMessage());
}
//如果有错就返加nulll
return null;
}
/**
* AES解密
*
* @param encryptBytes 待解密的byte[]
* @param decryptKey 解密密钥
* @return 解密后的String
* @throws Exception
*/
public static String aesDecryptByBytes(byte[] encryptBytes, String decryptKey) throws Exception {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128, new SecureRandom(decryptKey.getBytes()));
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(kgen.generateKey().getEncoded(), "AES"));
byte[] decryptBytes = cipher.doFinal(encryptBytes);
return new String(decryptBytes);
}
/**
* base 64 加密
*
* @param bytes 待编码的byte[]
* @return 编码后的base 64 code
*/
public static String base64Encode(byte[] bytes) {
return new BASE64Encoder().encode(bytes);
}
/**
* base 64 解密
*
* @param base64Code 待解码的base 64 code
* @return 解码后的byte[]
* @throws Exception
*/
public static byte[] base64Decode(String base64Code) throws Exception {
return StringUtils.isEmpty(base64Code) ? null : new BASE64Decoder().decodeBuffer(base64Code);
}
/**
* 将base 64 code AES解密
*
* @param encryptStr 待解密的base 64 code
* @param decryptKey 解密密钥
* @return 解密后的string
* @throws Exception
*/
public static String aesDecrypt(String encryptStr, String decryptKey) throws Exception {
return StringUtils.isEmpty(encryptStr) ? null : aesDecryptByBytes(base64Decode(encryptStr), decryptKey);
}
/**
* AES加密为base 64 code
*
* @param content 待加密的内容
* @param encryptKey 加密密钥
* @return 加密后的base 64 code
* @throws Exception
*/
public static String aesEncrypt(String content, String encryptKey) throws Exception {
return base64Encode(aesEncryptToBytes(content, encryptKey));
}
}
package com.gxmailu.gateway.util;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateUtil {
public static String getDate(String format) {
return new SimpleDateFormat(format).format(new Date());
}
public static String getDate(String format, Date date) {
return new SimpleDateFormat(format).format(date);
}
/**
* 时间戳转换成日期格式字符串
*
* @param seconds 精确到秒的字符串
* @param format
* @return
*/
public static String timeStamp2Date(String seconds, String format) {
if (seconds == null || seconds.isEmpty() || seconds.equals("null")) {
return "";
}
if (format == null || format.isEmpty()) {
format = "yyyy-MM-dd HH:mm:ss";
}
SimpleDateFormat sdf = new SimpleDateFormat(format);
return sdf.format(new Date(Long.parseLong(seconds)));
}
/**
* 日期格式字符串转换成时间戳
*
* @param dateStr 字符串日期
* @param format 如:yyyy-MM-dd HH:mm:ss
* @return
*/
public static String date2TimeStamp(String dateStr, String format) {
try {
SimpleDateFormat sdf = new SimpleDateFormat(format);
return String.valueOf(sdf.parse(dateStr).getTime() / 1000);
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
/**
* 日期格式化
*
* @return
*/
public static String dateToStr() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.format(new Date());
}
/**
* 日期格式化
*
* @param date 日期对象
* @return
*/
public static String dateToStr(Date date) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.format(date);
}
/**
* 日期格式化
*
* @param format 格式化字符串
* @return
*/
public static String dateToStr(String format) {
SimpleDateFormat sdf = new SimpleDateFormat(format);
return sdf.format(new Date());
}
/**
* 日期格式化
*
* @param date 日期对象
* @param format 格式化字符串
* @return
*/
public static String dateToStr(Date date, String format) {
SimpleDateFormat sdf = new SimpleDateFormat(format);
return sdf.format(date);
}
/**
* 字符串转换成日期
*
* @param str 日期字符串
* @return date
*/
public static Date strToDate(String str) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = null;
try {
date = format.parse(str);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
/**
* 取得当前时间戳(精确到秒)
*
* @return
*/
public static String timeStamp() {
long time = System.currentTimeMillis();
return String.valueOf(time / 1000);
}
}
package com.gxmailu.gateway.util;
import java.util.UUID;
public class UUIDUtil {
public static String create() {
return UUID.randomUUID().toString().replaceAll("-", "");
}
/* public static void main(String[] args) {
System.out.println(UUID.randomUUID().toString().toUpperCase());
}*/
}
server:
port: 8889
tomcat:
max-http-form-post-size: -1
spring:
application:
name: gateway
cloud:
gateway:
# api-prefix: /api/v1
# api2-prefix: /api/v2
# api3-prefix: /api/v3
routes:
- id: ocrCloudPlatform
uri: lb://ocrCloudPlatformApi
predicates:
- Path=/**
filters:
- StripPrefix=0
eureka:
client:
service-url:
defaultZone: http://localhost:9000/eureka
instance:
prefer-ip-address: true
instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}
lease-expiration-duration-in-seconds: 30
lease-renewal-interval-in-seconds: 10
logging:
level:
com.netflix: warn
spring:
profiles:
active: mldev
package com.gxmailu.gateway;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class GatewayApplicationTests {
@Test
void contextLoads() {
}
}
package com.gxmailu.auth;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class AuthApplicationTests {
@Test
void contextLoads() {
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.11.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.gxmailu</groupId>
<artifactId>registration</artifactId>
<version>server-1.0.0</version>
<name>registration</name>
<description>服务注册中心</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR11</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
package com.gxmailu.registration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class RegistrationApplication {
public static void main(String[] args) {
SpringApplication.run(RegistrationApplication.class, args);
}
}
server:
port: 9000
spring:
application:
name: registration
eureka:
instance:
hostname: registration
lease-expiration-duration-in-seconds: 30
lease-renewal-interval-in-seconds: 10
server:
eviction-interval-timer-in-ms: 5000
client:
healthcheck:
enabled: true
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka
register-with-eureka: false
fetch-registry: false
logging:
level:
com.netflix: warn
\ No newline at end of file
package com.gxmailu.registration;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class RegistrationApplicationTests {
@Test
void contextLoads() {
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment