Commit 9425013f authored by 以墨为白's avatar 以墨为白 🎧

签名优化

parent 86e5d1b2
......@@ -98,6 +98,12 @@
<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>
......
......@@ -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);
}
}
......
......@@ -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 {
......
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
......@@ -10,6 +10,7 @@ public enum HttpResultState {
MULTI_DEVICE_LOGIN(9040, "登录已失效,账户已在别处登录,请重新登录"),
EXPIRE(9041, "登录已经过期,请重新登录"),
INVALID_TOKEN(9042, "身份验证失败"),
INVALID_SIGNATURE(9043, "签名验证失败"),
GATEWAY(9050, "请求的服务不存在,请联系管理员"),
......
......@@ -12,6 +12,7 @@ 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;
......@@ -26,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 {
......@@ -35,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) {
......@@ -75,28 +80,28 @@ public class SignatureVerificationFilter extends OncePerRequestFilter {
return false;
}
// timestamp 10分钟内有效
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 = "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;
}
// timestamp 10分钟内有效
// 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;
// }
// 请求体
String body = StreamUtils.copyToString(request.getInputStream(), StandardCharsets.UTF_8);
body = objectMapper.writeValueAsString(objectMapper.readValue(body, Map.class));
......@@ -119,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);
......
......@@ -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
......@@ -22,7 +22,7 @@ import javax.annotation.Resource;
@SpringBootTest()
public class MainTest {
String nonce = "nonce1";
String nonce = "2";
String timestampStr = "21";
String appId = "1";
......@@ -39,6 +39,8 @@ public class MainTest {
@Test
@DisplayName("任务完成情况")
public void xxRwwcqkTest() {
timestampStr = String.valueOf(System.currentTimeMillis() / 1000);
String secretKey = this.appStoreService.getAppSecretByAppKey(appId);
Assertions.assertNotNull(secretKey, "appId不存在");//断言appId存在,为空直接抛出异常不进行下一步测试,提高测试效率
// Assertions.fail(secretKey);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment