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