From db8504fb7a8977bab876125ed2a37fa10f0a879b Mon Sep 17 00:00:00 2001
From: zhangli <123879394@qq.com>
Date: Tue, 22 Jul 2025 00:58:19 +0800
Subject: [PATCH] =?UTF-8?q?=E7=94=A8=E6=88=B7=E6=A0=A1=E9=AA=8C?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Dockerfile.buildagent | 15 ++++
Jenkinsfile | 37 ++++----
pom.xml | 21 +++++
.../sczx/gateway/filter/AuthGlobalFilter.java | 8 +-
.../gateway/filter/LoggingGlobalFilter.java | 8 ++
.../java/com/sczx/gateway/util/JwtUtil.java | 84 +++++++++++++++++++
.../java/com/sczx/gateway/util/RedisUtil.java | 70 ++++++++++++++++
src/main/resources/application.yml | 4 +
8 files changed, 229 insertions(+), 18 deletions(-)
create mode 100644 Dockerfile.buildagent
create mode 100644 src/main/java/com/sczx/gateway/util/JwtUtil.java
create mode 100644 src/main/java/com/sczx/gateway/util/RedisUtil.java
diff --git a/Dockerfile.buildagent b/Dockerfile.buildagent
new file mode 100644
index 0000000..f893f6c
--- /dev/null
+++ b/Dockerfile.buildagent
@@ -0,0 +1,15 @@
+# 使用 JDK 8 作为构建环境
+FROM openjdk:8-jdk
+
+# 使用阿里云的 apt 镜像源(Debian 11 bullseye)
+RUN sed -i 's/deb.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list && \
+ sed -i 's/security.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list
+
+# 更新包列表并安装 Maven 和 Git
+RUN apt update && \
+ apt install -y maven git && \
+ mvn --version && \
+ git --version
+
+# 设置工作目录
+WORKDIR /home/jenkins/workspace
\ No newline at end of file
diff --git a/Jenkinsfile b/Jenkinsfile
index 661ef2c..5e35cfe 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -1,8 +1,8 @@
pipeline {
agent any
- tools {
- maven 'M3'
- }
+// tools {
+// maven 'M3'
+// }
environment {
APP_NAME = "sczx_gateway"
DOCKER_IMAGE = "${APP_NAME}:latest"
@@ -10,34 +10,35 @@ pipeline {
}
stages {
- stage('Checkout') {
- steps {
- echo "📦 正在拉取代码..."
- git branch: 'main', url: 'http://115.190.8.52:3000/sczx_group/sczx_gateway.git'
- }
- }
+// stage('Checkout') {
+// steps {
+// echo "📦 正在拉取代码..."
+// git branch: 'main', url: 'http://115.190.8.52:3000/sczx_group/sczx_gateway.git'
+// }
+// }
- stage('Check Java Version') {
- steps {
- sh 'java -version'
- sh 'javac -version'
- }
- }
- stage('Build with Maven') {
+ stage('Build with Maven in JDK 8') {
+ agent {
+ dockerfile {
+ filename "Dockerfile.buildagent"
+ }
+ }
steps {
echo "🛠️ 正在使用 Maven 构建..."
- sh 'mvn clean package -s settings.xml -X'
+ sh 'mvn clean package -s settings.xml'
}
}
stage('Check Jar File') {
+ agent any
steps {
sh 'ls -la target/' // 确保 jar 文件存在
}
}
stage('Build Docker Image') {
+ agent any
steps {
echo "🐋 正在构建 Docker 镜像..."
sh """
@@ -47,6 +48,7 @@ pipeline {
}
stage('Stop Old Container') {
+ agent any
steps {
echo "🛑 正在停止旧容器..."
sh '''
@@ -59,6 +61,7 @@ pipeline {
}
stage('Run New Container') {
+ agent any
steps {
echo "🟢 正在运行新容器..."
sh """
diff --git a/pom.xml b/pom.xml
index 63cac39..3076183 100644
--- a/pom.xml
+++ b/pom.xml
@@ -52,6 +52,10 @@
+
+ org.springframework.cloud
+ spring-cloud-commons
+
org.springframework.boot
@@ -103,6 +107,23 @@
1.2.83
+
+
+ io.jsonwebtoken
+ jjwt-api
+ 0.11.5
+
+
+ io.jsonwebtoken
+ jjwt-impl
+ 0.11.5
+
+
+ io.jsonwebtoken
+ jjwt-jackson
+ 0.11.5
+
+
diff --git a/src/main/java/com/sczx/gateway/filter/AuthGlobalFilter.java b/src/main/java/com/sczx/gateway/filter/AuthGlobalFilter.java
index 0420938..86182a6 100644
--- a/src/main/java/com/sczx/gateway/filter/AuthGlobalFilter.java
+++ b/src/main/java/com/sczx/gateway/filter/AuthGlobalFilter.java
@@ -1,7 +1,9 @@
package com.sczx.gateway.filter;
+import com.sczx.gateway.util.JwtUtil;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.Ordered;
@@ -38,6 +40,9 @@ public class AuthGlobalFilter implements GlobalFilter, Ordered {
"/zc/**/webjars/**"
);
+ @Autowired
+ private JwtUtil jwtUtil;
+
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
@@ -52,7 +57,8 @@ public class AuthGlobalFilter implements GlobalFilter, Ordered {
if (!isAllowed) {
// 非放行路径,校验 token
String token = exchange.getRequest().getHeaders().getFirst("Authorization");
- if (token == null || !token.startsWith("Bearer ")) {
+
+ if (token == null || !token.startsWith("Bearer ") || !jwtUtil.validateToken(token.replace("Bearer ", ""))) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
diff --git a/src/main/java/com/sczx/gateway/filter/LoggingGlobalFilter.java b/src/main/java/com/sczx/gateway/filter/LoggingGlobalFilter.java
index 39a59de..6130b79 100644
--- a/src/main/java/com/sczx/gateway/filter/LoggingGlobalFilter.java
+++ b/src/main/java/com/sczx/gateway/filter/LoggingGlobalFilter.java
@@ -21,6 +21,7 @@ import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
+import java.util.UUID;
/**
* 全局日志过滤器,用于打印请求路径、Header、Body 和响应状态码、Body。
@@ -32,11 +33,18 @@ public class LoggingGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
+ String traceId = UUID.randomUUID().toString().substring(24,36);
+
+ // 将 traceId 添加到请求头中
+ ServerWebExchange mutatedExchange = exchange.mutate()
+ .request(builder -> builder.header("X-Trace-ID", traceId))
+ .build();
CustomExchange decoratedExchange = new CustomExchange(exchange);
return chain.filter(decoratedExchange)
.then(Mono.fromRunnable(() -> {
+ log.info("🌐 Trace ID: {}", traceId);
log.info("🌐 请求路径: {}", exchange.getRequest().getPath());
log.info("📥 请求头: {}", exchange.getRequest().getHeaders());
log.info("📥 请求体: {}", decoratedExchange.getRequestBody());
diff --git a/src/main/java/com/sczx/gateway/util/JwtUtil.java b/src/main/java/com/sczx/gateway/util/JwtUtil.java
new file mode 100644
index 0000000..97a96bd
--- /dev/null
+++ b/src/main/java/com/sczx/gateway/util/JwtUtil.java
@@ -0,0 +1,84 @@
+package com.sczx.gateway.util;
+
+
+import io.jsonwebtoken.JwtException;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.security.Keys;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import javax.crypto.SecretKey;
+
+/**
+ * @Author: 张黎
+ * @Date: 2025/07/06/14:00
+ * @Description:
+ */
+@Slf4j
+@Component
+public class JwtUtil {
+
+ @Value("${auth.token-expiration}")
+ private long expiration;
+ private final SecretKey key;
+
+ public JwtUtil(@Value("${auth.secret-key}") String secretKey) {
+ this.key = Keys.hmacShaKeyFor(secretKey.getBytes());
+
+// this.key = Keys.secretKeyFor(SignatureAlgorithm.HS512);
+ log.info("JWT 密钥:{}", secretKey);
+ }
+ // 支持自定义 claims 的 generateToken 方法
+// public String generateToken(SimpleUserInfoDTO simpleUserInfoDTO, String subject){
+// Map claims = Maps.newHashMap();
+// claims.put("userInfo", simpleUserInfoDTO.getUserId());
+// return Jwts.builder()
+// .setClaims(claims) // 设置自定义 claims
+// .setSubject(subject)
+// .setExpiration(new Date(System.currentTimeMillis() + expiration))
+// .signWith(key, SignatureAlgorithm.HS512)
+// .compact();
+// }
+
+ // 从 token 中提取某个字段
+// public T getClaim(String token, String claimKey, Class clazz) {
+// return Jwts.parserBuilder()
+// .setSigningKey(key)
+// .build()
+// .parseClaimsJws(token)
+// .getBody()
+// .get(claimKey, clazz);
+// }
+//
+// // 从 token 中提取某个对象类型的字段(支持复杂对象)
+// public T getClaimFromToken(String token, String claimKey, Class targetClass) {
+// return Jwts.parserBuilder()
+// .setSigningKey(key)
+// .build()
+// .parseClaimsJws(token)
+// .getBody()
+// .get(claimKey, targetClass);
+// }
+
+ public String extractUsername(String token) {
+ return Jwts.parserBuilder()
+ .setSigningKey(key)
+ .build()
+ .parseClaimsJws(token)
+ .getBody()
+ .getSubject();
+ }
+
+ public boolean validateToken(String token) {
+ try {
+ Jwts.parserBuilder()
+ .setSigningKey(key)
+ .build()
+ .parseClaimsJws(token);
+ return true;
+ } catch (JwtException ex) {
+ return false;
+ }
+ }
+}
diff --git a/src/main/java/com/sczx/gateway/util/RedisUtil.java b/src/main/java/com/sczx/gateway/util/RedisUtil.java
new file mode 100644
index 0000000..5f0da46
--- /dev/null
+++ b/src/main/java/com/sczx/gateway/util/RedisUtil.java
@@ -0,0 +1,70 @@
+package com.sczx.gateway.util;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.stereotype.Component;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @Author: 张黎
+ * @Date: 2025/07/06/14:21
+ * @Description:
+ */
+@Slf4j
+@Component
+public class RedisUtil {
+
+
+ @Autowired
+ private StringRedisTemplate redisTemplate;
+
+ public void set(String key, String value, long timeout, TimeUnit unit) {
+ redisTemplate.opsForValue().set(key, value, timeout, unit);
+ }
+
+ public String get(String key) {
+ return redisTemplate.opsForValue().get(key);
+ }
+
+ public void delete(String key) {
+ redisTemplate.delete(key);
+ }
+
+ private static final Long DEFAULT_EXPIRE = 120L;
+
+ public boolean getRedisLock(String key, String lockName) {
+ log.info("获取锁 - {}", key);
+ return getRedisLockWithTimeout(key, lockName, 120L);
+ }
+
+ /**
+ * 获取redislock
+ * 加锁并设置过期时间
+ *
+ * @param key
+ * @param lockName
+ * @return
+ */
+ public boolean getRedisLockWithTimeout(String key, String lockName, Long lockExpire) {
+ boolean lock = false;
+ if (Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(key, lockName))) {
+ redisTemplate.expire(key, lockExpire, TimeUnit.SECONDS);
+ return true;
+ }
+ if (redisTemplate.getExpire(key) == -1) {
+ //保证锁一定设置过期时间
+ redisTemplate.expire(key, DEFAULT_EXPIRE, TimeUnit.SECONDS);
+ }
+ log.info("未获取到锁:" + lock + ", lockName={},key={}", lockName, key);
+
+ return lock;
+ }
+
+ public void deleteRedisLock(String key) {
+ log.info("释放锁 - {}", key);
+ redisTemplate.delete(key);
+ }
+
+}
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 82f074e..a0a6378 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -43,6 +43,10 @@ management:
health:
show-details: always
+auth:
+ secret-key: his-is-a-very-long-and-secure-secret-key-for-jwt-signing-please-dont-use-short-keys
+ token-expiration: 86400000 # 24小时
+
logging:
level:
org.springframework.cloud.gateway: DEBUG