# 统一返回响应体封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
@Data
public class JsonResult<T> {


/**
* 错误码
*/
private Integer code;

/**
* 提示信息
*/
private String msg;

/**
* 返回的具体内容
*/
private T data;


public JsonResult(Integer code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}

public JsonResult() {
}


public JsonResult<T> fail() {
return new JsonResult<>(1000, "操作失败", null);
}

public JsonResult<T> fail(String msg) {
return new JsonResult<>(1001, msg, null);
}

public JsonResult<T> ok() {
return new JsonResult<>(2000, "操作成功", null);
}

public JsonResult<T> ok(T data) {
return new JsonResult<>(2001, "操作成功", data);
}

public JsonResult<T> jud(boolean condition) {
return condition ? ok() : fail();
}

public static void returnMsg(HttpServletResponse response, String msg) throws IOException {
response.setCharacterEncoding("utf-8");
PrintWriter out;
String jsonString;
out = response.getWriter();
jsonString = new ObjectMapper().writeValueAsString(new JsonResult<>().fail(msg));
out.println(jsonString);
out.flush();
}
}

# JWT 配置

1
2
3
4
5
6
<!--jwt依赖-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
@Slf4j(topic = "jwtConfig")
@Data
@Component
@ConfigurationProperties(prefix = "jwt")
public class JwtConfig {

/**
* 密钥
*/
private String secret = "XXXXX";

/**
* 过期时间(单位:秒)-- 1小时
**/
private long expireTime = 60 * 60 * 1000L;


public String sign(User user) {
Map<String, Object> claim = new HashMap<>();
claim.put("username", user.getUsername());
claim.put("userId", user.getUserId());
Date date = new Date();
return Jwts.builder()
.setClaims(claim)
.setIssuedAt(date)
.setExpiration(new Date(date.getTime() + expireTime))
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}

/**
* 获取token中注册信息
*/
public Claims getTokenClaim(String token) {
try {
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
} catch (Exception e) {
return null;
/* catch (ExpiredJwtException e){
return e.getClaims(); //防止jwt过期解析报错
}
*/
}
}

/**
* 验证token是否过期失效
*
* @param token 令牌
*/
public boolean isTokenExpired(String token) {
Date expiration = getExpirationDateFromToken(token);
if (expiration != null) {
return getExpirationDateFromToken(token).before(new Date());
} else {
return true;
}
}

/**
* 获取token失效时间
*
* @param token 令牌
*/
public Date getExpirationDateFromToken(String token) {
Claims claims = getTokenClaim(token);
return (claims == null) ? null : claims.getExpiration();
}

/**
* 获取用户名从token中
*/
public User getUser(String token) {
Claims map = getTokenClaim(token);
String username = (String) map.get("username");
Integer userId = (Integer) map.get("userId");
return new User(userId, username);
}

/**
* 获取jwt发布时间
*/
public Date getIssuedAtDateFromToken(String token) {
return getTokenClaim(token).getIssuedAt();
}

}

还有一个版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125

/**
* JWT工具类
*/
public class JwtUtil {

/**
* 有效期为 60 * 60 * 1000 一个小时
*/
public static final Long JWT_TTL = 60 * 60 * 1000L;
/**
* 设置秘钥明文
*/
public static final String JWT_KEY = "XXXXXX";

public static String getUUID() {
return UUID.randomUUID().toString().replaceAll("-", "").substring(0,8);
}

/**
* 生成jtw
*
* @param subject token中要存放的数据(json格式)
* @return
*/
public static String createJWT(String subject) {
// 设置过期时间
JwtBuilder builder = getJwtBuilder(subject, null, getUUID());
return builder.compact();
}

/**
* 生成jtw
*
* @param subject token中要存放的数据(json格式)
* @param ttlMillis token超时时间
* @return
*/
public static String createJWT(String subject, Long ttlMillis) {
// 设置过期时间
JwtBuilder builder = getJwtBuilder(subject, ttlMillis, getUUID());
return builder.compact();
}


/**
* 创建token
*
* @param id
* @param subject
* @param ttlMillis
* @return
*/
public static String createJWT(String id, String subject, Long ttlMillis) {
JwtBuilder builder = getJwtBuilder(subject, ttlMillis, id);// 设置过期时间
return builder.compact();
}

private static JwtBuilder getJwtBuilder(String subject, Long ttlMillis, String uuid) {
SecretKey secretKey = generalKey();
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
if (ttlMillis == null) {
ttlMillis = JwtUtil.JWT_TTL;
}
long expMillis = nowMillis + ttlMillis;
Date expDate = new Date(expMillis);
return Jwts.builder()
// jti:jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
.setId(uuid)
// sub: jwt所面向的用户
.setSubject(subject)
// iss: 签发者
.setIssuer("windlinxy.top")
// iat: jwt的签发时间
.setIssuedAt(now)
// exp: jwt的过期时间
.setExpiration(expDate)
//使用HS256对称加密算法签名, 第二个参数为秘钥
.signWith(SignatureAlgorithm.HS256, secretKey);

}

public static void main(String[] args) throws Exception {
long now = System.currentTimeMillis();
System.out.println(new Date());
String token = createJWT("nihao");
Claims claims = parseJWT("token");
System.out.println(System.currentTimeMillis() - now);
System.out.println(new Date());
System.out.println(token);
System.out.println(claims);
}

/**
* 生成加密后的秘钥 secretKey
*
* @return
*/
public static SecretKey generalKey() {
byte[] encodedKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);
SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
return key;
}

/**
* 解析
*
* @param jwt
* @return
* @throws Exception
*/
public static Claims parseJWT(String jwt) throws Exception {
if(jwt == null) {
return null;
}
SecretKey secretKey = generalKey();
return Jwts.parser()
.setSigningKey(secretKey)
.parseClaimsJws(jwt)
.getBody();
}


}

# CORS 跨域解决

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Configuration
public class WebAppConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
//是否发送Cookie
.allowCredentials(true)
//放行哪些原始域
.allowedOriginPatterns("*")
.allowedMethods("*")
.allowedHeaders("*")
.exposedHeaders("*");
}
}

# 拦截器(注解拦截)

@PassToken

1
2
3
4
5
6
7
8
9
10
/**
* @author Windlinxy
* @description: 权限判断跳过
* @create 2023-01-30 19:11
**/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PassToken {
boolean required() default true;
}

拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
@Slf4j(topic = "AdminInterceptor")
public class AdminInterceptor implements HandlerInterceptor {
@Resource
private JwtConfig jwtConfig;

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
/*
* 放行预检请求
*/
String options = "OPTIONS";
if (options.equalsIgnoreCase(request.getMethod())) {
return true;
}
if(judAdminCheck(handler, request)){
return true;
}else {
JsonResult.returnMsg(response, "需要管理员权限");
return false;
}
}

/**
* 检查是否有AdminCheck注解,有则验证是否是管理员
*
* @param handler handler
* @return 是否有PassToken注释
*/
private boolean judAdminCheck(Object handler, HttpServletRequest request) {
AdminCheck adminCheck = ((HandlerMethod) handler).getMethodAnnotation(AdminCheck.class);
if (adminCheck == null){
adminCheck = ((HandlerMethod) handler).getBean().getClass().getAnnotation(AdminCheck.class);
}
String username = jwtConfig.getUser(request.getHeader("Authorization")).getUsername();
if ("admin".equals(username)) {
return adminCheck.required();
} else {
return false;
}
}
}

# RedisConfig

使用原生的 RedisTemplate 时存储数据时键值对会带上编码前缀,解决办法是使用 FastJson 实现一个 redis 序列化器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!--redis依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<!--fastjson依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.33</version>
</dependency>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class FastJsonRedisSerializer<T> implements RedisSerializer<T> {

public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;

private final Class<T> clazz;

static {
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
}

public FastJsonRedisSerializer(Class<T> clazz) {
super();
this.clazz = clazz;
}

@Override
public byte[] serialize(T t) throws SerializationException {
if (t == null) {
return new byte[0];
}
return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
}

@Override
public T deserialize(byte[] bytes) throws SerializationException {
if (bytes == null || bytes.length <= 0) {
return null;
}
String str = new String(bytes, DEFAULT_CHARSET);

return JSON.parseObject(str, clazz);
}


protected JavaType getJavaType(Class<?> clazz) {
return TypeFactory.defaultInstance().constructType(clazz);
}
}

配置 RedisTemplate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Configuration
public class RedisConfig {

@Bean
@SuppressWarnings(value = { "unchecked", "rawtypes" })
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory)
{
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);


FastJsonRedisSerializer serializer = new FastJsonRedisSerializer(Object.class);
// 使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());

template.setValueSerializer(serializer);

// Hash的key也采用StringRedisSerializer的序列化方式
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(serializer);

template.afterPropertiesSet();
return template;
}
}

# redis 工具类

参考三更草堂 SpringSecurity 课程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
@Component
@SuppressWarnings(value = {"unchecked", "rawtypes", "unused", "all"})
public class RedisCache {

@Resource
public RedisTemplate redisTemplate;


/**
* 缓存基本的对象,Integer、String、实体类等
*
* @param key 缓存的键值
* @param value 缓存的值
*/
public <T> void setCacheObject(final String key, final T value) {
redisTemplate.opsForValue().set(key, value);
}

/**
* 缓存基本的对象,Integer、String、实体类等
*
* @param key 缓存的键值
* @param value 缓存的值
* @param timeout 时间
* @param timeUnit 时间颗粒度
*/
public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) {
redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
}

/**
* 设置有效时间
*
* @param key Redis键
* @param timeout 超时时间
* @return true=设置成功;false=设置失败
*/
public boolean expire(final String key, final long timeout) {
return expire(key, timeout, TimeUnit.SECONDS);
}

/**
* 设置有效时间
*
* @param key Redis键
* @param timeout 超时时间
* @param unit 时间单位
* @return true=设置成功;false=设置失败
*/
public boolean expire(final String key, final long timeout, final TimeUnit unit) {
return redisTemplate.expire(key, timeout, unit);
}

/**
* 获得缓存的基本对象。
*
* @param key 缓存键值
* @return 缓存键值对应的数据
*/
public <T> T getCacheObject(final String key) {
ValueOperations<String, T> operation = redisTemplate.opsForValue();
return operation.get(key);
}

/**
* 删除单个对象
*
* @param key
*/
public boolean deleteObject(final String key) {
return redisTemplate.delete(key);
}

/**
* 删除集合对象
*
* @param collection 多个对象
* @return
*/
public long deleteObject(final Collection collection) {
return redisTemplate.delete(collection);
}

/**
* 缓存List数据
*
* @param key 缓存的键值
* @param dataList 待缓存的List数据
* @return 缓存的对象
*/
public <T> long setCacheList(final String key, final List<T> dataList) {
Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
return count == null ? 0 : count;
}

/**
* 获得缓存的list对象
*
* @param key 缓存的键值
* @return 缓存键值对应的数据
*/
public <T> List<T> getCacheList(final String key) {
return redisTemplate.opsForList().range(key, 0, -1);
}

/**
* 缓存Set
*
* @param key 缓存键值
* @param dataSet 缓存的数据
* @return 缓存数据的对象
*/
public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet) {
BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
Iterator<T> it = dataSet.iterator();
while (it.hasNext()) {
setOperation.add(it.next());
}
return setOperation;
}

/**
* 获得缓存的set
*
* @param key
* @return
*/
public <T> Set<T> getCacheSet(final String key) {
return redisTemplate.opsForSet().members(key);
}

/**
* 缓存Map
*
* @param key
* @param dataMap
*/
public <T> void setCacheMap(final String key, final Map<String, T> dataMap) {
if (dataMap != null) {
redisTemplate.opsForHash().putAll(key, dataMap);
}
}

/**
* 获得缓存的Map
*
* @param key
* @return
*/
public <T> Map<String, T> getCacheMap(final String key) {
return redisTemplate.opsForHash().entries(key);
}

/**
* 往Hash中存入数据
*
* @param key Redis键
* @param hKey Hash键
* @param value 值
*/
public <T> void setCacheMapValue(final String key, final String hKey, final T value) {
redisTemplate.opsForHash().put(key, hKey, value);
}

/**
* 获取Hash中的数据
*
* @param key Redis键
* @param hKey Hash键
* @return Hash中的对象
*/
public <T> T getCacheMapValue(final String key, final String hKey) {
HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
return opsForHash.get(key, hKey);
}

/**
* 删除Hash中的数据
*
* @param key
* @param hkey
*/
public void delCacheMapValue(final String key, final String hkey) {
HashOperations hashOperations = redisTemplate.opsForHash();
hashOperations.delete(key, hkey);
}

/**
* 获取多个Hash中的数据
*
* @param key Redis键
* @param hKeys Hash键集合
* @return Hash对象集合
*/
public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys) {
return redisTemplate.opsForHash().multiGet(key, hKeys);
}

/**
* 获得缓存的基本对象列表
*
* @param pattern 字符串前缀
* @return 对象列表
*/
public Collection<String> keys(final String pattern) {
return redisTemplate.keys(pattern);
}
}

更新于 阅读次数 本文阅读量:

请我喝[茶]~( ̄▽ ̄)~*

Windlinxy 微信支付

微信支付

Windlinxy 支付宝

支付宝