Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
S
szpt
Project overview
Project overview
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
以墨为白
szpt
Commits
7a1e4e8b
Commit
7a1e4e8b
authored
Dec 28, 2024
by
夏敏伟
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' of
http://192.168.168.218/wcyuee/szpt
parents
c033d06f
9425013f
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
227 additions
and
28 deletions
+227
-28
pom.xml
pom.xml
+11
-0
src/main/java/com/zksy/szpt/aspect/AuthAspect.java
src/main/java/com/zksy/szpt/aspect/AuthAspect.java
+1
-1
src/main/java/com/zksy/szpt/aspect/SignatureAspect.java
src/main/java/com/zksy/szpt/aspect/SignatureAspect.java
+1
-1
src/main/java/com/zksy/szpt/config/RedisConfig.java
src/main/java/com/zksy/szpt/config/RedisConfig.java
+108
-0
src/main/java/com/zksy/szpt/domain/HttpResultState.java
src/main/java/com/zksy/szpt/domain/HttpResultState.java
+1
-0
src/main/java/com/zksy/szpt/filter/SignatureVerificationFilter.java
...ava/com/zksy/szpt/filter/SignatureVerificationFilter.java
+30
-18
src/main/resources/application-dev.yml
src/main/resources/application-dev.yml
+6
-0
src/test/java/com/zksy/szpt/MainTest.java
src/test/java/com/zksy/szpt/MainTest.java
+69
-8
No files found.
pom.xml
View file @
7a1e4e8b
...
...
@@ -93,6 +93,17 @@
<version>
${druid.version}
</version>
</dependency>
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-webflux
</artifactId>
</dependency>
<!--redis依赖配置-->
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-data-redis
</artifactId>
</dependency>
<dependency>
<groupId>
org.apache.commons
</groupId>
<artifactId>
commons-lang3
</artifactId>
...
...
src/main/java/com/zksy/szpt/aspect/AuthAspect.java
View file @
7a1e4e8b
...
...
@@ -76,7 +76,7 @@ public class AuthAspect {
return
joinPoint
.
proceed
();
}
else
{
//传递的签名没有通过
logger
.
info
(
"appKey:{}没有通过验证"
,
param
.
get
(
"appKey"
));
return
new
HttpResult
<
String
>(
HttpResultState
.
INVALID_
TOKEN
);
return
new
HttpResult
<
String
>(
HttpResultState
.
INVALID_
SIGNATURE
);
}
}
...
...
src/main/java/com/zksy/szpt/aspect/SignatureAspect.java
View file @
7a1e4e8b
...
...
@@ -57,7 +57,7 @@ public class SignatureAspect {
String
generatedSignature
=
DigestUtil
.
md5Hex
(
appId
+
nonce
+
timestamp
+
objectMapper
.
writeValueAsString
(
paramValues
[
0
])
+
appSecret
);
if
(!
generatedSignature
.
equals
(
signature
))
{
logger
.
error
(
"签名验证失败,请求参数:{}"
,
paramValues
[
0
]);
HttpResultState
httpResultState
=
HttpResultState
.
INVALID_
TOKEN
;
HttpResultState
httpResultState
=
HttpResultState
.
INVALID_
SIGNATURE
;
httpResultState
.
setMessage
(
"签名验证失败"
);
return
new
HttpResult
<
String
>(
httpResultState
);
}
else
{
...
...
src/main/java/com/zksy/szpt/config/RedisConfig.java
0 → 100644
View file @
7a1e4e8b
package
com
.
zksy
.
szpt
.
config
;
import
com.fasterxml.jackson.annotation.JsonAutoDetect
;
import
com.fasterxml.jackson.annotation.PropertyAccessor
;
import
com.fasterxml.jackson.databind.ObjectMapper
;
import
org.springframework.cache.CacheManager
;
import
org.springframework.cache.annotation.CachingConfigurerSupport
;
import
org.springframework.cache.annotation.EnableCaching
;
import
org.springframework.cache.interceptor.KeyGenerator
;
import
org.springframework.context.annotation.Bean
;
import
org.springframework.context.annotation.Configuration
;
import
org.springframework.data.redis.cache.RedisCacheConfiguration
;
import
org.springframework.data.redis.cache.RedisCacheManager
;
import
org.springframework.data.redis.connection.RedisConnectionFactory
;
import
org.springframework.data.redis.core.RedisTemplate
;
import
org.springframework.data.redis.core.StringRedisTemplate
;
import
org.springframework.data.redis.listener.RedisMessageListenerContainer
;
import
org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer
;
import
org.springframework.data.redis.serializer.StringRedisSerializer
;
import
java.lang.reflect.Method
;
import
java.time.Duration
;
import
java.util.HashMap
;
import
java.util.HashSet
;
import
java.util.Map
;
import
java.util.Set
;
@Configuration
@EnableCaching
//开启注解式缓存
public
class
RedisConfig
extends
CachingConfigurerSupport
{
@Bean
public
RedisTemplate
<
String
,
Object
>
redisTemplate
(
RedisConnectionFactory
factory
)
{
RedisTemplate
<
String
,
Object
>
template
=
new
RedisTemplate
<
String
,
Object
>();
template
.
setConnectionFactory
(
factory
);
Jackson2JsonRedisSerializer
jackson2JsonRedisSerializer
=
new
Jackson2JsonRedisSerializer
(
Object
.
class
);
ObjectMapper
om
=
new
ObjectMapper
();
om
.
setVisibility
(
PropertyAccessor
.
ALL
,
JsonAutoDetect
.
Visibility
.
ANY
);
om
.
enableDefaultTyping
(
ObjectMapper
.
DefaultTyping
.
NON_FINAL
);
jackson2JsonRedisSerializer
.
setObjectMapper
(
om
);
StringRedisSerializer
stringRedisSerializer
=
new
StringRedisSerializer
();
// key采用String的序列化方式
template
.
setKeySerializer
(
stringRedisSerializer
);
// hash的key也采用String的序列化方式
template
.
setHashKeySerializer
(
stringRedisSerializer
);
// value序列化方式采用jackson
template
.
setValueSerializer
(
jackson2JsonRedisSerializer
);
// hash的value序列化方式采用jackson
template
.
setHashValueSerializer
(
jackson2JsonRedisSerializer
);
template
.
afterPropertiesSet
();
return
template
;
}
@Bean
@Override
public
KeyGenerator
keyGenerator
()
{
return
new
KeyGenerator
()
{
@Override
public
Object
generate
(
Object
target
,
Method
method
,
Object
...
params
)
{
StringBuilder
sb
=
new
StringBuilder
();
sb
.
append
(
target
.
getClass
().
getName
());
sb
.
append
(
method
.
getName
());
for
(
Object
obj
:
params
)
{
sb
.
append
(
obj
.
toString
());
}
return
sb
.
toString
();
}
};
}
@Bean
public
CacheManager
cacheManager
(
RedisConnectionFactory
redisConnectionFactory
)
{
//通过Spring提供的RedisCacheConfiguration类,构造一个自己的redis配置类,从该配置类中可以设置一些初始化的缓存命名空间
// 及对应的默认过期时间等属性,再利用RedisCacheManager中的builder.build()的方式生成cacheManager:
RedisCacheConfiguration
config
=
RedisCacheConfiguration
.
defaultCacheConfig
();
// 生成一个默认配置,通过config对象即可对缓存进行自定义配置
config
=
config
.
entryTtl
(
Duration
.
ofMinutes
(
1
))
// 设置缓存的默认过期时间,也是使用Duration设置
.
disableCachingNullValues
();
// 不缓存空值
// 设置一个初始化的缓存空间set集合
Set
<
String
>
cacheNames
=
new
HashSet
<>();
cacheNames
.
add
(
"my-redis-cache1"
);
cacheNames
.
add
(
"my-redis-cache2"
);
// 对每个缓存空间应用不同的配置
Map
<
String
,
RedisCacheConfiguration
>
configMap
=
new
HashMap
<>();
configMap
.
put
(
"my-redis-cache1"
,
config
);
configMap
.
put
(
"my-redis-cache2"
,
config
.
entryTtl
(
Duration
.
ofSeconds
(
120
)));
RedisCacheManager
cacheManager
=
RedisCacheManager
.
builder
(
redisConnectionFactory
)
// 使用自定义的缓存配置初始化一个cacheManager
.
initialCacheNames
(
cacheNames
)
// 注意这两句的调用顺序,一定要先调用该方法设置初始化的缓存名,再初始化相关的配置
.
withInitialCacheConfigurations
(
configMap
)
.
build
();
return
cacheManager
;
}
@Bean
public
StringRedisTemplate
stringRedisTemplate
(
RedisConnectionFactory
factory
)
{
StringRedisTemplate
stringRedisTemplate
=
new
StringRedisTemplate
();
stringRedisTemplate
.
setConnectionFactory
(
factory
);
return
stringRedisTemplate
;
}
//创建Redis消息监听者容器
@Bean
public
RedisMessageListenerContainer
redisMessageListenerContainer
(
RedisConnectionFactory
factory
)
{
RedisMessageListenerContainer
container
=
new
RedisMessageListenerContainer
();
container
.
setConnectionFactory
(
factory
);
return
container
;
}
}
\ No newline at end of file
src/main/java/com/zksy/szpt/domain/HttpResultState.java
View file @
7a1e4e8b
...
...
@@ -10,6 +10,7 @@ public enum HttpResultState {
MULTI_DEVICE_LOGIN
(
9040
,
"登录已失效,账户已在别处登录,请重新登录"
),
EXPIRE
(
9041
,
"登录已经过期,请重新登录"
),
INVALID_TOKEN
(
9042
,
"身份验证失败"
),
INVALID_SIGNATURE
(
9043
,
"签名验证失败"
),
GATEWAY
(
9050
,
"请求的服务不存在,请联系管理员"
),
...
...
src/main/java/com/zksy/szpt/filter/SignatureVerificationFilter.java
View file @
7a1e4e8b
...
...
@@ -8,14 +8,17 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import
com.zksy.szpt.domain.HttpResult
;
import
com.zksy.szpt.domain.HttpResultState
;
import
com.zksy.szpt.service.AppStoreService
;
import
com.zksy.szpt.util.EncryptUtil
;
import
com.zksy.szpt.util.SignatureUtil
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
org.springframework.data.redis.core.RedisTemplate
;
import
org.springframework.http.MediaType
;
import
org.springframework.stereotype.Component
;
import
org.springframework.util.StreamUtils
;
import
org.springframework.util.StringUtils
;
import
org.springframework.web.filter.OncePerRequestFilter
;
import
javax.annotation.Resource
;
import
javax.servlet.FilterChain
;
import
javax.servlet.ServletException
;
...
...
@@ -24,6 +27,7 @@ import javax.servlet.http.HttpServletResponse;
import
java.io.IOException
;
import
java.nio.charset.StandardCharsets
;
import
java.util.Map
;
import
java.util.concurrent.TimeUnit
;
//@Component
public
class
SignatureVerificationFilter
extends
OncePerRequestFilter
{
...
...
@@ -33,6 +37,9 @@ public class SignatureVerificationFilter extends OncePerRequestFilter {
@Resource
ObjectMapper
objectMapper
;
@Resource
private
RedisTemplate
<
String
,
Object
>
redisTemplate
;
private
final
AppStoreService
appStoreService
;
public
SignatureVerificationFilter
(
AppStoreService
appStoreService
)
{
...
...
@@ -73,32 +80,37 @@ public class SignatureVerificationFilter extends OncePerRequestFilter {
return
false
;
}
String
secretKey
=
this
.
appStoreService
.
getAppSecretByAppKey
(
appId
);
// timestamp 10分钟内有效
//
long timestamp = Long.parseLong(timestampStr);
//
long currentTimestamp = System.currentTimeMillis() / 1000;
//
if (Math.abs(currentTimestamp - timestamp) > 600) {
//
this.write(response, "请求已过期");
//
return false;
//
}
long
timestamp
=
Long
.
parseLong
(
timestampStr
);
long
currentTimestamp
=
System
.
currentTimeMillis
()
/
1000
;
if
(
Math
.
abs
(
currentTimestamp
-
timestamp
)
>
600
)
{
this
.
write
(
response
,
"请求已过期"
);
return
false
;
}
//
防止请求重放,nonce只能用一次,放在redis中,有效期 20分钟
// String nonceKey = "SignatureVerificationFilter
:nonce:" + nonce;
// if (!this.redisTemplate.opsForValue().setIfAbsent(nonceKey, "1", 20, TimeUnit.MINUTES
)) {
//
this.write(response, "nonce无效");
//
return false;
//
}
//
防止请求重放,nonce只能用一次,放在redis中,有效期 20分钟
String
nonceKey
=
"api_signature
:nonce:"
+
nonce
;
if
(
Boolean
.
FALSE
.
equals
(
this
.
redisTemplate
.
opsForValue
().
setIfAbsent
(
nonceKey
,
"1"
,
20
,
TimeUnit
.
MINUTES
)
))
{
this
.
write
(
response
,
"nonce无效"
);
return
false
;
}
// 校验appId
String
secretKey
=
this
.
appStoreService
.
getAppSecretByAppKey
(
appId
);
if
(!
StringUtils
.
hasText
(
secretKey
))
{
this
.
write
(
response
,
"appId无效"
);
return
false
;
}
// 请求体
String
body
=
StreamUtils
.
copyToString
(
request
.
getInputStream
(),
StandardCharsets
.
UTF_8
);
body
=
objectMapper
.
writeValueAsString
(
objectMapper
.
readValue
(
body
,
Map
.
class
));
// 校验签名appId+nonce+timestampStr+body+secretKey
String
data
=
String
.
format
(
"%s%s%s%s%s"
,
appId
,
nonce
,
timestampStr
,
body
,
secretKey
);
body
=
EncryptUtil
.
getInstance
().
AESEncode
(
body
,
secretKey
);
// 校验签名appId+nonce+timestampStr+aes(body,secret)
String
data
=
String
.
format
(
"%s%s%s%s"
,
appId
,
nonce
,
timestampStr
,
body
);
String
generatedSignature
=
DigestUtil
.
md5Hex
(
data
);
if
(!
generatedSignature
.
equals
(
sign
))
{
write
(
response
,
"签名有误
"
);
write
(
response
,
"签名有误
,generatedSignature:"
+
generatedSignature
+
",sign: "
+
sign
+
",appId:"
+
appId
+
",nonce:"
+
nonce
+
",timestamp:"
+
timestampStr
);
return
false
;
}
return
true
;
...
...
@@ -112,7 +124,7 @@ public class SignatureVerificationFilter extends OncePerRequestFilter {
* @throws IOException 如果写入失败
*/
private
void
write
(
HttpServletResponse
response
,
String
msg
)
throws
IOException
{
HttpResultState
httpResultState
=
HttpResultState
.
INVALID_
TOKEN
;
HttpResultState
httpResultState
=
HttpResultState
.
INVALID_
SIGNATURE
;
httpResultState
.
setMessage
(
msg
);
HttpResult
<
String
>
httpResult
=
new
HttpResult
<>(
httpResultState
);
response
.
setContentType
(
MediaType
.
APPLICATION_JSON_VALUE
);
...
...
src/main/resources/application-dev.yml
View file @
7a1e4e8b
...
...
@@ -5,3 +5,9 @@ spring:
password
:
123456
driver-class-name
:
com.mysql.cj.jdbc.Driver
type
:
com.alibaba.druid.pool.DruidDataSource
redis
:
database
:
0
port
:
6379
password
:
1qaz2wsx
timeout
:
1000
host
:
192.168.168.110
\ No newline at end of file
src/test/java/com/zksy/szpt/MainTest.java
View file @
7a1e4e8b
package
com
.
zksy
.
szpt
;
import
static
org
.
junit
.
jupiter
.
api
.
Assertions
.
assertTrue
;
import
cn.hutool.crypto.digest.DigestUtil
;
import
com.fasterxml.jackson.core.JsonProcessingException
;
import
com.fasterxml.jackson.databind.ObjectMapper
;
import
com.zksy.szpt.domain.dto.XxRwwcqkDTO
;
import
com.zksy.szpt.service.AppStoreService
;
import
com.zksy.szpt.util.EncryptUtil
;
import
com.zksy.szpt.util.SignatureUtil
;
import
org.junit.jupiter.api.Assertions
;
import
org.junit.jupiter.api.DisplayName
;
import
org.junit.jupiter.api.Test
;
import
org.springframework.boot.test.context.SpringBootTest
;
import
org.springframework.web.reactive.function.client.WebClient
;
import
reactor.core.publisher.Mono
;
import
javax.annotation.Resource
;
/**
* A simple unit test
*/
public
class
MainTest
{
@SpringBootTest
()
public
class
MainTest
{
String
nonce
=
"2"
;
String
timestampStr
=
"21"
;
String
appId
=
"1"
;
@Resource
private
ObjectMapper
objectMapper
;
@Resource
private
AppStoreService
appStoreService
;
/**
*
Rigorous Test :-)
*
任务完成情况
*/
@Test
public
void
shouldAnswerWithTrue
()
{
assertTrue
(
true
);
@DisplayName
(
"任务完成情况"
)
public
void
xxRwwcqkTest
()
{
timestampStr
=
String
.
valueOf
(
System
.
currentTimeMillis
()
/
1000
);
String
secretKey
=
this
.
appStoreService
.
getAppSecretByAppKey
(
appId
);
Assertions
.
assertNotNull
(
secretKey
,
"appId不存在"
);
//断言appId存在,为空直接抛出异常不进行下一步测试,提高测试效率
// Assertions.fail(secretKey);
//请求参数
XxRwwcqkDTO
xxRwwcqkDTO
=
new
XxRwwcqkDTO
();
xxRwwcqkDTO
.
setRwid
(
"123456"
);
xxRwwcqkDTO
.
setXxyid
(
"123456"
);
xxRwwcqkDTO
.
setShrid
(
"123456"
);
xxRwwcqkDTO
.
setWczt
(
"1"
);
xxRwwcqkDTO
.
setBmzt
(
"1"
);
String
json
=
null
;
try
{
json
=
objectMapper
.
writeValueAsString
(
xxRwwcqkDTO
);
}
catch
(
JsonProcessingException
e
)
{
Assertions
.
fail
(
"json序列化失败"
);
}
//请求体加密
json
=
EncryptUtil
.
getInstance
().
AESEncode
(
json
,
secretKey
);
//签名appId+nonce+timestampStr+aes(body)
String
data
=
String
.
format
(
"%s%s%s%s"
,
appId
,
nonce
,
timestampStr
,
json
);
String
generatedSignature
=
DigestUtil
.
md5Hex
(
data
);
//请求
WebClient
webClient
=
WebClient
.
builder
()
.
baseUrl
(
"http://localhost:8086"
)
.
defaultHeader
(
"Content-Type"
,
"application/json"
)
.
build
();
String
response
=
webClient
.
post
().
uri
(
"/rest/index/addXxRwwcqk"
)
.
header
(
SignatureUtil
.
APPID
,
appId
)
.
header
(
SignatureUtil
.
NONCE
,
nonce
)
.
header
(
SignatureUtil
.
TIMESTAMP
,
timestampStr
)
.
header
(
SignatureUtil
.
SIGNATURE
,
generatedSignature
)
.
body
(
Mono
.
just
(
xxRwwcqkDTO
),
XxRwwcqkDTO
.
class
)
.
retrieve
()
.
bodyToMono
(
String
.
class
)
.
block
();
System
.
out
.
println
(
response
);
}
}
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