Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
C
custom-server
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
lizhonghong
custom-server
Commits
30bc7df6
Commit
30bc7df6
authored
Jun 04, 2026
by
HuAnYing
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' of
http://47.122.114.111:9999/lizhonghong/custom-server
parents
9fe6021d
22ab4ed1
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
364 additions
and
27 deletions
+364
-27
custom-server-core/pom.xml
+4
-0
custom-server-core/src/main/java/com/jomalls/custom/page/PageRequest.java
+3
-2
custom-server-core/src/main/java/com/jomalls/custom/security/JwtClaimsAdapter.java
+162
-0
custom-server-core/src/main/java/com/jomalls/custom/security/TokenCompatibilityParser.java
+173
-0
custom-server-core/src/main/java/com/jomalls/custom/security/TokenHandle.java
+0
-0
custom-server-starter/pom.xml
+0
-5
custom-server-starter/src/main/java/com/jomalls/custom/config/MybatisInterceptor.java
+0
-8
custom-server-starter/src/main/java/com/jomalls/custom/config/WebMvcConfiguration.java
+17
-3
custom-server-starter/src/main/resources/application-redis.properties
+1
-1
custom-server-starter/src/main/resources/application.properties
+2
-1
pom.xml
+2
-7
No files found.
custom-server-core/pom.xml
View file @
30bc7df6
...
@@ -39,6 +39,10 @@
...
@@ -39,6 +39,10 @@
<scope>
runtime
</scope>
<scope>
runtime
</scope>
</dependency>
</dependency>
<dependency>
<dependency>
<groupId>
com.alibaba.fastjson2
</groupId>
<artifactId>
fastjson2
</artifactId>
</dependency>
<dependency>
<groupId>
org.projectlombok
</groupId>
<groupId>
org.projectlombok
</groupId>
<artifactId>
lombok
</artifactId>
<artifactId>
lombok
</artifactId>
<optional>
true
</optional>
<optional>
true
</optional>
...
...
custom-server-core/src/main/java/com/jomalls/custom/page/PageRequest.java
View file @
30bc7df6
package
com
.
jomalls
.
custom
.
page
;
package
com
.
jomalls
.
custom
.
page
;
import
lombok.Dat
a
;
import
io.swagger.v3.oas.annotations.media.Schem
a
;
import
lombok.Getter
;
import
lombok.Getter
;
import
lombok.Setter
;
import
lombok.Setter
;
import
java.io.Serial
;
import
java.io.Serial
;
import
java.io.Serializable
;
import
java.io.Serializable
;
import
java.util.List
;
/**
/**
* @Author: Lizh
* @Author: Lizh
...
@@ -34,11 +33,13 @@ public class PageRequest implements Pageable, Serializable {
...
@@ -34,11 +33,13 @@ public class PageRequest implements Pageable, Serializable {
/**
/**
* 当前页码
* 当前页码
*/
*/
@Schema
(
description
=
"当前页码"
,
example
=
"1"
,
defaultValue
=
"1"
)
private
long
current
;
private
long
current
;
/**
/**
* 分页大小
* 分页大小
*/
*/
@Schema
(
description
=
"分页大小"
,
example
=
"10"
,
defaultValue
=
"10"
)
private
long
size
;
private
long
size
;
/**
/**
...
...
custom-server-core/src/main/java/com/jomalls/custom/security/JwtClaimsAdapter.java
0 → 100644
View file @
30bc7df6
package
com
.
jomalls
.
custom
.
security
;
import
io.jsonwebtoken.Claims
;
import
io.jsonwebtoken.RequiredTypeException
;
import
java.util.Date
;
import
java.util.LinkedHashMap
;
import
java.util.Map
;
import
java.util.Set
;
/**
* JJWT Claims 适配器 — 将 Map 包装为 Claims 接口
* 用于兼容旧版本 token 的手动解析场景
*
* @author Lizh
* @date 2026-06-03
*/
public
class
JwtClaimsAdapter
implements
Claims
{
private
final
Map
<
String
,
Object
>
claimsMap
;
public
JwtClaimsAdapter
(
Map
<
String
,
Object
>
claimsMap
)
{
this
.
claimsMap
=
new
LinkedHashMap
<>(
claimsMap
);
}
@Override
public
String
getIssuer
()
{
return
get
(
ISSUER
,
String
.
class
);
}
@Override
public
String
getSubject
()
{
return
get
(
SUBJECT
,
String
.
class
);
}
@Override
@SuppressWarnings
(
"unchecked"
)
public
Set
<
String
>
getAudience
()
{
Object
aud
=
get
(
AUDIENCE
);
if
(
aud
instanceof
Set
)
{
return
(
Set
<
String
>)
aud
;
}
if
(
aud
instanceof
String
)
{
return
Set
.
of
(((
String
)
aud
).
split
(
","
));
}
return
null
;
}
@Override
public
Date
getExpiration
()
{
Object
exp
=
get
(
EXPIRATION
);
if
(
exp
instanceof
Date
)
{
return
(
Date
)
exp
;
}
if
(
exp
instanceof
Number
)
{
return
new
Date
(((
Number
)
exp
).
longValue
()
*
1000
);
}
return
null
;
}
@Override
public
Date
getNotBefore
()
{
return
get
(
NOT_BEFORE
,
Date
.
class
);
}
@Override
public
Date
getIssuedAt
()
{
return
get
(
ISSUED_AT
,
Date
.
class
);
}
@Override
public
String
getId
()
{
return
get
(
ID
,
String
.
class
);
}
@Override
@SuppressWarnings
(
"unchecked"
)
public
<
T
>
T
get
(
String
claimName
,
Class
<
T
>
requiredType
)
{
Object
value
=
claimsMap
.
get
(
claimName
);
if
(
value
==
null
)
{
return
null
;
}
if
(
requiredType
.
isInstance
(
value
))
{
return
(
T
)
value
;
}
// 处理数字类型转换(Jackson 可能将整数解析为 Integer 而非 Long)
if
(
requiredType
==
Long
.
class
&&
value
instanceof
Integer
)
{
return
(
T
)
Long
.
valueOf
(((
Integer
)
value
).
longValue
());
}
if
(
requiredType
==
Long
.
class
&&
value
instanceof
Number
)
{
return
(
T
)
Long
.
valueOf
(((
Number
)
value
).
longValue
());
}
if
(
requiredType
==
String
.
class
)
{
return
(
T
)
value
.
toString
();
}
if
(
requiredType
==
Date
.
class
&&
value
instanceof
Number
)
{
return
(
T
)
new
Date
(((
Number
)
value
).
longValue
()
*
1000
);
}
throw
new
RequiredTypeException
(
"无法将 claim '"
+
claimName
+
"' 转换为 "
+
requiredType
.
getName
()
+
",实际类型: "
+
value
.
getClass
().
getName
());
}
@Override
public
int
size
()
{
return
claimsMap
.
size
();
}
@Override
public
boolean
isEmpty
()
{
return
claimsMap
.
isEmpty
();
}
@Override
public
boolean
containsKey
(
Object
key
)
{
return
claimsMap
.
containsKey
(
key
);
}
@Override
public
boolean
containsValue
(
Object
value
)
{
return
claimsMap
.
containsValue
(
value
);
}
@Override
public
Object
get
(
Object
key
)
{
return
claimsMap
.
get
(
key
);
}
@Override
public
Object
put
(
String
key
,
Object
value
)
{
return
claimsMap
.
put
(
key
,
value
);
}
@Override
public
Object
remove
(
Object
key
)
{
return
claimsMap
.
remove
(
key
);
}
@Override
public
void
putAll
(
Map
<?
extends
String
,
?>
m
)
{
claimsMap
.
putAll
(
m
);
}
@Override
public
void
clear
()
{
claimsMap
.
clear
();
}
@Override
public
Set
<
String
>
keySet
()
{
return
claimsMap
.
keySet
();
}
@Override
public
java
.
util
.
Collection
<
Object
>
values
()
{
return
claimsMap
.
values
();
}
@Override
public
Set
<
Entry
<
String
,
Object
>>
entrySet
()
{
return
claimsMap
.
entrySet
();
}
}
custom-server-core/src/main/java/com/jomalls/custom/security/TokenCompatibilityParser.java
0 → 100644
View file @
30bc7df6
package
com
.
jomalls
.
custom
.
security
;
import
com.fasterxml.jackson.databind.ObjectMapper
;
import
io.jsonwebtoken.Claims
;
import
io.jsonwebtoken.JwtException
;
import
io.jsonwebtoken.Jwts
;
import
io.jsonwebtoken.security.Keys
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.stereotype.Component
;
import
javax.crypto.Mac
;
import
javax.crypto.SecretKey
;
import
javax.crypto.spec.SecretKeySpec
;
import
java.nio.charset.StandardCharsets
;
import
java.security.MessageDigest
;
import
java.util.Base64
;
import
java.util.LinkedHashMap
;
import
java.util.Map
;
/**
* Token 兼容性解析工具
*
* <p>custom-back 使用 JJWT 0.9.1 生成 token,密钥推导方式为:
* <pre>
* secret = "custom"
* signingKey = TextCodec.BASE64.encode(secret) → "Y3VzdG9t"
* signWith(HS512, signingKey.getBytes(UTF_8)) → 8字节短密钥
* </pre>
*
* <p>JJWT 0.12.x 强制 HS512 密钥 ≥ 64 字节,无法直接解析旧 token。
* 本工具通过手动验证 HMAC-SHA512 签名 + 手动解码 payload 来兼容旧格式。
*
* @author Lizh
* @date 2026-06-03
*/
@Slf4j
@Component
public
class
TokenCompatibilityParser
{
private
static
final
ObjectMapper
OBJECT_MAPPER
=
new
ObjectMapper
();
/**
* 兼容性解析 token(支持 JJWT 0.9.1 旧格式)
*
* @param token JWT token
* @param secret 原始密钥(如 "custom")
* @return Claims
* @throws JwtException 解析失败
*/
public
Claims
parseTokenCompatibly
(
String
token
,
String
secret
)
throws
JwtException
{
// 方式1:兼容旧版本 JJWT 0.9.1 短密钥 token
Claims
result
=
tryLegacyParse
(
token
,
secret
);
if
(
result
!=
null
)
{
return
result
;
}
// 方式2:优先尝试 JJWT 0.12.x 标准方式(适用于新版长密钥生成的 token)
result
=
tryStandardParse
(
token
,
secret
);
if
(
result
!=
null
)
{
return
result
;
}
throw
new
JwtException
(
"无法解析 token,签名密钥不匹配或 token 格式无效"
);
}
/**
* JJWT 0.12.x 标准方式:secret 直接作为原始密钥字节
*/
private
Claims
tryStandardParse
(
String
token
,
String
secret
)
{
try
{
SecretKey
key
=
Keys
.
hmacShaKeyFor
(
secret
.
getBytes
(
StandardCharsets
.
UTF_8
));
return
Jwts
.
parser
()
.
verifyWith
(
key
)
.
build
()
.
parseSignedClaims
(
token
)
.
getPayload
();
}
catch
(
JwtException
e
)
{
log
.
debug
(
"标准方式解析失败: {}"
,
e
.
getMessage
());
}
return
null
;
}
/**
* 兼容 JJWT 0.9.1 旧格式 token
*
* <p>custom-back 密钥推导: signingKey = Base64(secret.getBytes(UTF_8))
* <p>因为密钥只有 8 字节,JJWT 0.12.x 拒绝,所以手工验证签名后解码 payload
*/
private
Claims
tryLegacyParse
(
String
token
,
String
secret
)
{
try
{
// 1. 按 JJWT 0.9.1 方式推导签名密钥
byte
[]
signingKey
=
deriveLegacySigningKey
(
secret
);
// 2. 手动验证 HMAC-SHA512 签名
if
(!
verifyHmacSha512Signature
(
token
,
signingKey
))
{
log
.
debug
(
"旧版 token 签名验证失败"
);
return
null
;
}
// 3. 签名有效,手动解码 payload 为 Claims
return
decodePayloadToClaims
(
token
);
}
catch
(
Exception
e
)
{
log
.
debug
(
"旧版兼容解析失败: {}"
,
e
.
getMessage
());
return
null
;
}
}
/**
* 按 JJWT 0.9.1 方式推导签名密钥
*
* <p>custom-back 调用链:
* <pre>
* signWith(HS512, TextCodec.BASE64.encode(secret))
* → Base64.encode("custom") → "Y3VzdG9t"
* → signWith 内部又 Base64.decode("Y3VzdG9t") → "custom" 字节
* → encode + decode = 恒等变换,实际密钥 = secret.getBytes(UTF_8)
* </pre>
*
* <p>同理 setSigningKey(TextCodec.BASE64.encode(secret)) 内部也是
* Base64.decode → secret 原始字节。
* 所以签名密钥始终是原始 secret 的 UTF-8 字节。
*
* @param secret 原始密钥字符串,如 "custom"
* @return 签名密钥字节 (= secret.getBytes(UTF_8))
*/
static
byte
[]
deriveLegacySigningKey
(
String
secret
)
{
return
secret
.
getBytes
(
StandardCharsets
.
UTF_8
);
}
/**
* 手动验证 HMAC-SHA512 签名
*
* @param token JWT token (header.payload.signature)
* @param keyBytes 签名密钥字节
* @return true=签名有效
*/
static
boolean
verifyHmacSha512Signature
(
String
token
,
byte
[]
keyBytes
)
{
try
{
String
[]
parts
=
token
.
split
(
"\\."
);
if
(
parts
.
length
<
3
)
{
return
false
;
}
String
headerPayload
=
parts
[
0
]
+
"."
+
parts
[
1
];
byte
[]
signatureBytes
=
Base64
.
getUrlDecoder
().
decode
(
parts
[
2
]);
Mac
mac
=
Mac
.
getInstance
(
"HmacSHA512"
);
SecretKeySpec
keySpec
=
new
SecretKeySpec
(
keyBytes
,
"HmacSHA512"
);
mac
.
init
(
keySpec
);
byte
[]
computedSignature
=
mac
.
doFinal
(
headerPayload
.
getBytes
(
StandardCharsets
.
UTF_8
));
return
MessageDigest
.
isEqual
(
computedSignature
,
signatureBytes
);
}
catch
(
Exception
e
)
{
log
.
debug
(
"HMAC-SHA512 签名验证异常: {}"
,
e
.
getMessage
());
return
false
;
}
}
/**
* 手动解码 JWT payload 为 JJWT Claims
*/
@SuppressWarnings
(
"unchecked"
)
private
static
Claims
decodePayloadToClaims
(
String
token
)
throws
Exception
{
String
[]
parts
=
token
.
split
(
"\\."
);
String
payloadJson
=
new
String
(
Base64
.
getUrlDecoder
().
decode
(
parts
[
1
]),
StandardCharsets
.
UTF_8
);
Map
<
String
,
Object
>
claimsMap
=
OBJECT_MAPPER
.
readValue
(
payloadJson
,
LinkedHashMap
.
class
);
return
new
JwtClaimsAdapter
(
claimsMap
);
}
}
custom-server-core/src/main/java/com/jomalls/custom/security/TokenHandle.java
View file @
30bc7df6
This diff is collapsed.
Click to expand it.
custom-server-starter/pom.xml
View file @
30bc7df6
...
@@ -59,11 +59,6 @@
...
@@ -59,11 +59,6 @@
<scope>
runtime
</scope>
<scope>
runtime
</scope>
</dependency>
</dependency>
<dependency>
<dependency>
<groupId>
com.github.xiaoymin
</groupId>
<artifactId>
knife4j-openapi3-jakarta-spring-boot-starter
</artifactId>
<version>
4.5.0
</version>
</dependency>
<dependency>
<groupId>
org.projectlombok
</groupId>
<groupId>
org.projectlombok
</groupId>
<artifactId>
lombok
</artifactId>
<artifactId>
lombok
</artifactId>
<scope>
provided
</scope>
<scope>
provided
</scope>
...
...
custom-server-starter/src/main/java/com/jomalls/custom/config/MybatisInterceptor.java
deleted
100644 → 0
View file @
9fe6021d
package
com
.
jomalls
.
custom
.
config
;
import
org.springframework.context.annotation.Configuration
;
@Configuration
public
class
MybatisInterceptor
{
}
\ No newline at end of file
custom-server-starter/src/main/java/com/jomalls/custom/config/WebMvcConfiguration.java
View file @
30bc7df6
...
@@ -17,9 +17,23 @@ public class WebMvcConfiguration implements WebMvcConfigurer {
...
@@ -17,9 +17,23 @@ public class WebMvcConfiguration implements WebMvcConfigurer {
@Override
@Override
public
void
addInterceptors
(
InterceptorRegistry
registry
)
{
public
void
addInterceptors
(
InterceptorRegistry
registry
)
{
registry
.
addInterceptor
(
securityInterceptor
())
registry
.
addInterceptor
(
securityInterceptor
())
.
excludePathPatterns
(
"/swagger-ui/**"
,
"/swagger-ui.html"
,
"/doc.html"
,
"/*/api-docs/**"
,
"/document.html"
,
.
excludePathPatterns
(
"/swagger-ui/**"
,
"/webjars/**"
,
"/swagger-resources/**"
,
"/sys/Serf/Health/*"
,
"/error"
,
"/swagger-ui.html"
,
"/actuator/health"
,
"/health/check"
);
"/doc.html"
,
"/document.html"
,
"/v3/api-docs/**"
,
"/v3/api-docs"
,
"/api-docs/**"
,
"/api-docs"
,
"/swagger-resources/**"
,
"/webjars/**"
,
"/sys/Serf/Health/*"
,
"/error"
,
"/actuator/health"
,
"/health/check"
,
"/.well-known/**"
,
"/favicon.ico"
,
"/static/**"
);
}
}
/**
/**
...
...
custom-server-starter/src/main/resources/application-redis.properties
View file @
30bc7df6
## Redis连接配置
## Redis连接配置
spring.data.redis.host
=
172.16.19.
99
spring.data.redis.host
=
172.16.19.
100
spring.data.redis.port
=
6379
spring.data.redis.port
=
6379
spring.data.redis.password
=
joshine.dev
spring.data.redis.password
=
joshine.dev
spring.data.redis.database
=
7
spring.data.redis.database
=
7
...
...
custom-server-starter/src/main/resources/application.properties
View file @
30bc7df6
...
@@ -36,7 +36,7 @@ TZ=Asia/Shanghai
...
@@ -36,7 +36,7 @@ TZ=Asia/Shanghai
server.needAuthentication
=
true
server.needAuthentication
=
true
# 令牌自定义标识
# 令牌自定义标识
token.header
=
Authorization
token.header
=
Authorization
# 令牌密钥
# 令牌密钥
(兼容旧版本)
token.secret
=
custom
token.secret
=
custom
# 令牌有效期(默认30分钟)
# 令牌有效期(默认30分钟)
token.expireTime
=
720
token.expireTime
=
720
\ No newline at end of file
pom.xml
View file @
30bc7df6
...
@@ -51,18 +51,13 @@
...
@@ -51,18 +51,13 @@
<version>
3.0.0
</version>
<version>
3.0.0
</version>
</dependency>
</dependency>
<!-- SpringDoc OpenAPI for Spring Boot 4.x -->
<!-- SpringDoc OpenAPI for Spring Boot 4.x -->
<dependency>
<dependency>
<groupId>
org.springdoc
</groupId>
<groupId>
org.springdoc
</groupId>
<artifactId>
springdoc-openapi-starter-webmvc-ui
</artifactId>
<artifactId>
springdoc-openapi-starter-webmvc-ui
</artifactId>
<version>
2.7.0
</version>
<version>
3.0.3
</version>
</dependency>
</dependency>
<!-- Orika Bean Mapper -->
<!-- Orika Bean Mapper -->
<dependency>
<dependency>
<groupId>
ma.glasnost.orika
</groupId>
<artifactId>
orika-core
</artifactId>
<version>
1.5.4
</version>
</dependency>
<dependency>
<groupId>
org.slf4j
</groupId>
<groupId>
org.slf4j
</groupId>
<artifactId>
slf4j-api
</artifactId>
<artifactId>
slf4j-api
</artifactId>
</dependency>
</dependency>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment