RequestAspect.java
9.54 KB
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
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
package com.huaheng.control.management.utils.aspect;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.alibaba.fastjson.JSON;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.huaheng.control.management.utils.config.ApplicationConfig;
import com.huaheng.control.management.utils.constant.CommonConstant;
import com.huaheng.control.management.utils.constant.RSA256Key;
import com.huaheng.control.management.utils.support.PassRequestAuthentication;
import com.huaheng.control.management.utils.support.RequestAuthentication;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import lombok.extern.slf4j.Slf4j;
/**
* 第三方系统调用接口身份认证Aspect
* @author TanYibin
* @createDate 2023年2月14日
*/
@Slf4j
@Aspect
@Component
public class RequestAspect {
@Autowired
private RSA256Key rsa256Key;
@Autowired
private ApplicationConfig applicationConfig;
@Pointcut("execution(public * com.huaheng.control.management.controller..*.*(..))" + "&& (@annotation(org.springframework.web.bind.annotation.RequestMapping) "
+ "|| @annotation(org.springframework.web.bind.annotation.GetMapping) " + "|| @annotation(org.springframework.web.bind.annotation.PostMapping))")
public void executeRequestAspect() {}
/**
* @author TanYibin
* @createDate 2026年1月4日
* @param point
* @return
* @throws Throwable
*/
@Around("executeRequestAspect()")
public Object doAround(ProceedingJoinPoint point) throws Throwable {
long startTime = System.currentTimeMillis();
ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
String url = request.getRequestURL().toString();
String audience = (String)request.getAttribute("audience");
if (StringUtils.isEmpty(audience)) {
audience = "unknown";
}
// 记录请求开始信息(包含参数)
log.info(">>> [{}] {} Audience:{} Params:{} From:{}", request.getMethod(), url, audience, getRequestParams(request, point.getArgs()),
CommonConstant.getIpAddr(request));
Throwable throwable = null;
Object result = null;
try {
result = point.proceed();
return result;
} catch (Throwable e) {
throwable = e;
throw e;
} finally {
long callTime = System.currentTimeMillis() - startTime;
// 记录返回结果
if (throwable != null) {
log.error("<<< [{}] {} Audience:{} Execution exception! {}", request.getMethod(), url, audience, throwable.getMessage(), throwable);
} else {
log.info("<<< [{}] {} Audience:{} Result:{} Cost:{}ms", request.getMethod(), url, audience, getResultInfo(result), callTime);
}
}
}
/**
* API Token 验证
* @author TanYibin
* @createDate 2023年2月14日
* @param joinPoint
* @throws NoSuchMethodException
*/
@Before("executeRequestAspect()")
public void doBefore(JoinPoint joinPoint) throws NoSuchMethodException {
Method method = this.getTargetMethod(joinPoint);
// 检查是否有passtoken注释,有则跳过认证
if (method.isAnnotationPresent(PassRequestAuthentication.class)) {
PassRequestAuthentication passApiAuthentication = method.getAnnotation(PassRequestAuthentication.class);
if (passApiAuthentication.required()) {
return;
}
}
// 获取request对象
ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
String token = request.getHeader("token");
if (token == null) {
throw new RuntimeException("Request authentication token is null");
}
Algorithm algorithm = Algorithm.RSA256(rsa256Key.getPublicKey(), rsa256Key.getPrivateKey());
JWTVerifier verifier = JWT.require(algorithm).withIssuer(applicationConfig.getArtifactId()).build();
DecodedJWT jwt = verifier.verify(token);
new RequestAuthentication.ApiAuthenticationBuild().operator(jwt.getClaim("operator").asString()).audience(jwt.getAudience().get(0)).issuer(jwt.getIssuer())
.issuedAt(jwt.getIssuedAt()).expireDateTime(jwt.getExpiresAt()).bulid();
if (jwt.getClaim("operator").asString().equals(jwt.getIssuer())) {
throw new RuntimeException("Request authentication token error");
}
log.debug("Request authentication token info: 使用方: {}, 提供方: {}, 失效时间: {}", jwt.getAudience().get(0), jwt.getClaim("operator").asString(),
DateUtil.format(jwt.getExpiresAt(), DatePattern.NORM_DATETIME_PATTERN));
// 在验证token后,将audience存储为请求属性
request.setAttribute("audience", jwt.getAudience().get(0));
}
@After("executeRequestAspect()")
public void doAfter(JoinPoint joinPoint) throws NoSuchMethodException {
RequestAuthentication.clean();
}
/**
* 基于连接点信息获取目标方法对象
* @author TanYibin
* @createDate 2023年2月14日
* @param joinPoint
* @return
* @throws NoSuchMethodException
*/
private Method getTargetMethod(JoinPoint joinPoint) throws NoSuchMethodException {
// 获取目标类对象
Class<?> clazz = joinPoint.getTarget().getClass();
// 获取方法签名信息,方法名和参数列表
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
// 获取目标方法对象
return clazz.getDeclaredMethod(signature.getName(), signature.getParameterTypes());
}
/**
* 获取请求参数信息
* 针对单个对象作为请求体的情况进行优化
*/
private String getRequestParams(HttpServletRequest request, Object[] args) {
try {
Object params = null;
// 获取URL参数(适用于GET请求和POST的URL参数)
Map<String, String> queryParams = getQueryParams(request);
if (!queryParams.isEmpty()) {
params = queryParams;
}
// 获取Body参数(适用于POST请求的请求体对象)
Object bodyParam = getBodyParam(args);
if (bodyParam != null) {
params = bodyParam;
}
return params == null ? "无参数" : JSON.toJSONString(params);
} catch (Exception e) {
return "参数解析失败";
}
}
/**
* 获取URL查询参数
*/
private Map<String, String> getQueryParams(HttpServletRequest request) {
Map<String, String> queryParams = new HashMap<>();
Enumeration<String> paramNames = request.getParameterNames();
while (paramNames.hasMoreElements()) {
String paramName = paramNames.nextElement();
String paramValue = request.getParameter(paramName);
queryParams.put(paramName, paramValue);
}
return queryParams;
}
/**
* 获取请求体参数(针对单个对象的情况)
*/
private Object getBodyParam(Object[] args) {
if (args == null || args.length == 0) {
return null;
}
// 过滤掉HttpServletRequest等框架参数,返回第一个业务参数对象
for (Object arg : args) {
if (!isFrameworkType(arg)) {
return arg;
}
}
return null;
}
/**
* 判断是否为框架内置类型参数
*/
private boolean isFrameworkType(Object arg) {
return arg instanceof HttpServletRequest || arg instanceof javax.servlet.http.HttpServletResponse
|| arg instanceof org.springframework.validation.BindingResult || arg instanceof org.springframework.ui.Model;
}
/**
* 获取返回结果信息(安全处理,避免日志过大)
*/
private String getResultInfo(Object result) {
try {
if (result == null) {
return "null";
}
String resultStr = JSON.toJSONString(result);
// 控制日志长度,避免日志文件过大
int maxLength = 500;
if (resultStr.length() > maxLength) {
return resultStr.substring(0, maxLength) + "...[长度:" + resultStr.length() + "]";
}
return resultStr;
} catch (Exception e) {
return result.toString();
}
}
}