From a6ef4aa1ad98cc50094b8d1d13dfa891b02ba3c6 Mon Sep 17 00:00:00 2001 From: zichun Date: Wed, 29 Apr 2026 17:00:37 +0800 Subject: [PATCH] feat(mod): tenant + jwt config + util REQ-MOD-001 --- backend/src/main/java/com/xly/erp/common/security/JwtUtil.java | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ backend/src/test/java/com/xly/erp/common/security/JwtUtilTest.java | 37 +++++++++++++++++++++++++++++++++++++ backend/src/test/java/com/xly/erp/common/security/TestJwtHelper.java | 19 +++++++++++++++++++ 3 files changed, 110 insertions(+), 0 deletions(-) create mode 100644 backend/src/main/java/com/xly/erp/common/security/JwtUtil.java create mode 100644 backend/src/test/java/com/xly/erp/common/security/JwtUtilTest.java create mode 100644 backend/src/test/java/com/xly/erp/common/security/TestJwtHelper.java diff --git a/backend/src/main/java/com/xly/erp/common/security/JwtUtil.java b/backend/src/main/java/com/xly/erp/common/security/JwtUtil.java new file mode 100644 index 0000000..45c8638 --- /dev/null +++ b/backend/src/main/java/com/xly/erp/common/security/JwtUtil.java @@ -0,0 +1,54 @@ +package com.xly.erp.common.security; + +import com.xly.erp.common.config.StubSecurityProperties; +import com.xly.erp.common.exception.BizException; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.JwtException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.util.Date; + +@Component +public class JwtUtil { + + private static final Duration TTL = Duration.ofHours(8); + + private final SecretKey key; + + public JwtUtil(String secret) { + this.key = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"); + } + + @Autowired + public JwtUtil(StubSecurityProperties props) { + this(props.getJwtSecret()); + } + + public String sign(String userNo) { + Date now = new Date(); + return Jwts.builder() + .subject(userNo) + .issuedAt(now) + .expiration(new Date(now.getTime() + TTL.toMillis())) + .signWith(key) + .compact(); + } + + public String parse(String token) { + try { + return Jwts.parser() + .verifyWith(key) + .build() + .parseSignedClaims(token) + .getPayload() + .getSubject(); + } catch (JwtException | IllegalArgumentException e) { + throw new BizException(20001, "未认证或 token 已失效"); + } + } +} diff --git a/backend/src/test/java/com/xly/erp/common/security/JwtUtilTest.java b/backend/src/test/java/com/xly/erp/common/security/JwtUtilTest.java new file mode 100644 index 0000000..7748aed --- /dev/null +++ b/backend/src/test/java/com/xly/erp/common/security/JwtUtilTest.java @@ -0,0 +1,37 @@ +package com.xly.erp.common.security; + +import com.xly.erp.common.exception.BizException; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class JwtUtilTest { + + private static final String SECRET = "f8d4be76bff13bf32fa33ca0b14a4b152ad01ca5719f57df18ec4ecf2370b235"; + + private final JwtUtil jwtUtil = new JwtUtil(SECRET); + + @Test + void signAndParse_roundTrip() { + String token = jwtUtil.sign("ALICE001"); + assertThat(token).isNotBlank(); + assertThat(jwtUtil.parse(token)).isEqualTo("ALICE001"); + } + + @Test + void parseTamperedToken_throwsBizException20001() { + String token = jwtUtil.sign("ALICE001"); + String tampered = token.substring(0, token.length() - 4) + "XXXX"; + assertThatThrownBy(() -> jwtUtil.parse(tampered)) + .isInstanceOf(BizException.class) + .hasFieldOrPropertyWithValue("code", 20001); + } + + @Test + void parseGarbageToken_throwsBizException20001() { + assertThatThrownBy(() -> jwtUtil.parse("not.a.real.jwt")) + .isInstanceOf(BizException.class) + .hasFieldOrPropertyWithValue("code", 20001); + } +} diff --git a/backend/src/test/java/com/xly/erp/common/security/TestJwtHelper.java b/backend/src/test/java/com/xly/erp/common/security/TestJwtHelper.java new file mode 100644 index 0000000..dc79b96 --- /dev/null +++ b/backend/src/test/java/com/xly/erp/common/security/TestJwtHelper.java @@ -0,0 +1,19 @@ +package com.xly.erp.common.security; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class TestJwtHelper { + + private final JwtUtil jwtUtil; + + @Autowired + public TestJwtHelper(JwtUtil jwtUtil) { + this.jwtUtil = jwtUtil; + } + + public String signFor(String userNo) { + return jwtUtil.sign(userNo); + } +} -- libgit2 0.22.2