我们项目中一般需要去监控 controller 层, 校验参数、加密、日志打印等等。
一、首先是写个切面,定位所有controller 层的方法
@Aspect
@Component
@Slf4j
public class MutmsAspect {
@Value("${spring.application.name}")
private String appName;
@Autowired
private ExeService exeService;
@Around("execution(public * com.ceair.mutms..controller.*.*(..))")
public Object exeMethod(ProceedingJoinPoint joinPoint)throws Throwable{
二、校验参数
// 版本验证,低版本报错
MutmsVersion.update(methodName,req);
// token 验证
MutmsToken.verify(appName, permittedServer, joinPoint,req);
// session校验
MutmsSession.verify(appName,permittedServer, joinPoint);
三、打印入参,执行方法,拿到反参
// 因为阿里云日志只支持xxxx* 这种方式模糊查找不支持*xxxx这种方式所以给关键日志加一个前缀
log.info("{}{}方法:{},入参: {}", AliyunLogConsts.MUTMS_REQ_PREFIX, methodName, joinPoint.getSignature().getName(), JsonUtil.toString(req,""));
res=joinPoint.proceed(joinPoint.getArgs());
四、打印反参,方法执行时间,保存访问记录等
// 因为阿里云日志只支持xxxx* 这种方式模糊查找不支持*xxxx这种方式所以给关键日志加一个前缀
log.info("{}{}返回: {}", AliyunLogConsts.MUTMS_RES_PREFIX, methodName, JsonUtil.toString(res,""));
log.info("方法: {}, 接口耗时:{}毫秒", methodName, (System.currentTimeMillis()-starttime));
// 异步保存接口访问记录
MUTMSAccessLogQueueHelper.execute(() -> {
MUTMSAccessLog accessLog = new MUTMSAccessLog();
accessLog.setPassportId(passportId);
accessLog.setTransactionId(transactionId); // 明文transactionId
accessLog.setSessionId(sessionId);
accessLog.setOs(req.getOs());
accessLog.setLocalIp(IpHelper.getLocalIp());
accessLog.setClientRealIp(clentRealIp);
accessLog.setLanguage(req.getLanguage());
accessLog.setRegion(req.getRegion());
accessLog.setAppVersion(req.getAppVersion());
accessLog.setChannelNo(req.getChannelNo());
accessLog.setSecondChannelNo(req.getSecondChannelNo());
accessLog.setThirdChannelNo(req.getThirdChannelNo());
accessLog.setSalesChannel(salesChannel);
完整代码
package com.ceair.mutms.common.aop;
import com.ceair.mutms.common.aliyunlog.AliyunLogConsts;
import com.ceair.mutms.common.aliyunlog.MUTMSAccessLogItemKeyConsts;
import com.ceair.mutms.common.base.BaseReq;
import com.ceair.mutms.common.base.CommonRes;
import com.ceair.mutms.common.entity.MUTMSAccessLog;
import com.ceair.mutms.common.exception.*;
import com.ceair.mutms.common.filter.ContextUtil;
import com.ceair.mutms.common.aliyunlog.AliyunLogHelper;
import com.ceair.mutms.common.aliyunlog.AliyunLogItemKeyConsts;
import com.ceair.mutms.common.log.MDCBean;
import com.ceair.mutms.common.log.MDCHelper;
import com.ceair.mutms.common.sec.aes.BangBang;
import com.ceair.mutms.common.service.ExeService;
import com.ceair.mutms.common.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.support.StandardMultipartHttpServletRequest;
import java.util.Map;
/**
* 接口控制
* @author DELL
*
*/
@Order(-1)
@Aspect
@Component
@Slf4j
public class MutmsAspect {
@Value("${spring.application.name}")
private String appName;
@Autowired
private ExeService exeService;
@Around("execution(public * com.ceair.mutms..controller.*.*(..))")
public Object exeMethod(ProceedingJoinPoint joinPoint)throws Throwable{
long starttime = System.currentTimeMillis();
String permittedServer=CacheUtil.getOtherCode(CacheKey.PERMITTED_SERVER);
BaseReq req=getParam(joinPoint.getArgs());
// 加密 token
String tokenRsa = req.getCeairToken();
Long passportId = req.getPassportId();
String clentRealIp = ContextUtil.getContext().getString(CacheKey.CLIENT_REAL_IP_KEY);
String sessionId = ContextUtil.getContext().getString(CacheKey.SESSION_ID_KEY);
String salesChannel = SalesChannelHelper.salesChannel(req.getLanguage(), req.getRegion());
//当前请求所执行的controller类的方法
final String methodName=joinPoint.getSignature().getName();
String className = joinPoint.getSignature().getDeclaringTypeName();
final String interfaceKey = className + "." + methodName;
Object res=null;
final String transactionId = Assert.isNotEmpty(req.getTransactionId()) ? req.getTransactionId() : BaseReq.generateTransactionId(passportId);
MDCBean bean = new MDCBean();
bean.setTransactionId(transactionId);
bean.setPassportId(Assert.nullToEmpty(passportId));
bean.setSessionId(sessionId);
AliyunLogHelper.addItemIfAbsent(req.getAliyunLogItems(), AliyunLogItemKeyConsts.TRANSACTION_ID, transactionId);
AliyunLogHelper.addItemIfAbsent(req.getAliyunLogItems(), AliyunLogItemKeyConsts.SESSION_ID, sessionId);
AliyunLogHelper.addItemIfAbsent(req.getAliyunLogItems(), AliyunLogItemKeyConsts.PASSPORT_ID, passportId);
AliyunLogHelper.addItemIfAbsent(req.getAliyunLogItems(), AliyunLogItemKeyConsts.INTERFACE_NAME, interfaceKey);
AliyunLogHelper.addLogTime(req.getAliyunLogItems());
try {
MDCHelper.insertIntoMDC(bean);
// 当前请求所使用的语言类型
ContextUtil.getContext().put(CacheKey.LANGUAGE_CACHE_KEY, Assert.isEmpty(req.getLanguage())?"zh":req.getLanguage());
// 版本验证,低版本报错
MutmsVersion.update(methodName,req);
// token 验证
MutmsToken.verify(appName, permittedServer, joinPoint,req);
req.setSalesChannel(salesChannel);
// 当前线程执行子任务的唯一标识
// ContextUtil.getContext().put("logbean.uuid", UUID.randomUUID().toString());
// 地区
ContextUtil.getContext().put(CacheKey.REGION_CACHE_KEY, Assert.isEmpty(req.getRegion())?"CN":req.getRegion());
// 渠道
ContextUtil.getContext().put(CacheKey.SALES_CHANNEL_CACHE_KEY, salesChannel);
//系统支持的支付方式
if(req.getSupportPay()!=null && req.getSupportPay()) {
ContextUtil.getContext().put(CacheKey.OS_PAY_METHOD_CACHE_KEY, req.getOsPayMethod());
}
// 每次请求唯一ID,明文tansactionId
ContextUtil.getContext().put(CacheKey.TRANSACTION_ID_CACHE_KEY, transactionId);
// 当前请求的用户ID
ContextUtil.getContext().put(CacheKey.PASSPORTID_CACHE_KEY, passportId);
// 当前请求的客户端平台系统:AD|iOS
ContextUtil.getContext().put(CacheKey.APP_OS_CACHE_KEY, req.getOs());
// 当前请求的客户端版本号
ContextUtil.getContext().put(CacheKey.APP_VERSION_CACHE_KEY, req.getAppVersion());
// 当前请求的设备ID
ContextUtil.getContext().put(CacheKey.IEMI_CACHE_KEY,req.getImei());
//阿里token
ContextUtil.getContext().put(CacheKey.APDIDTOKEN_CACHE_KEY,req.getApdidToken());
// session校验
MutmsSession.verify(appName,permittedServer, joinPoint);
String codeEnity=null;
if(permittedServer==null||!permittedServer.contains(appName)) {
//接口调用开关
codeEnity=CacheUtil.getMethod(methodName);
}
//*表示关闭当前所有接口
if(codeEnity !=null || (codeEnity=CacheUtil.getMethod("*"))!=null) {
log.info("方法:{},被禁用:{}", joinPoint.getSignature().getName(), codeEnity);
res = new CommonRes<Object>(CommonRes.FAIL,CacheUtil.getI18nVal(codeEnity, codeEnity));
}else {
// 因为阿里云日志只支持xxxx* 这种方式模糊查找不支持*xxxx这种方式所以给关键日志加一个前缀
log.info("{}{}方法:{},入参: {}", AliyunLogConsts.MUTMS_REQ_PREFIX, methodName, joinPoint.getSignature().getName(), JsonUtil.toString(req,""));
res=joinPoint.proceed(joinPoint.getArgs());
}
}catch (MUTMSFirstExceptionWithData tx){
res= ExceptionHandler.doException(tx);
}catch (MUTMSFirstException tx){
res= ExceptionHandler.doException(tx);
}catch (MUTMSRuntimeException tx){
res= ExceptionHandler.doException(tx);
}catch(MUTMSSecondExceptionWithData e){
res= ExceptionHandler.doException(e);
}catch(MUTMSSecondException e){
res= ExceptionHandler.doException(e);
}catch(MUTMSThirdException e){
res= ExceptionHandler.doException(e);
}catch(MUTMSFourthException e){
res= ExceptionHandler.doException(e);
}catch(Exception e) {
if(e instanceof NullPointerException)
{
log.info("接口调用出现异常, ex: {}:", Assert.callStackOneLine(e));
}
res= ExceptionHandler.doException(e);
log.error("接口调用出现异常:", e);
}finally {
// 把请求参数存入redis缓存
exeService.exec(()-> {
try {
MutmsToken.setToken(tokenRsa,req);
} catch (Exception e) {
log.error("异步记录ceair token 出现异常:", e);
}
});
final String resultCode;
final String resultMsg;
if(res instanceof CommonRes){
((CommonRes<?>) res).setTransactionId(transactionId);
resultCode = ((CommonRes<?>) res).getResultCode();
resultMsg = ((CommonRes<?>) res).getResultMsg();
// 构建统一弹框
DialogHelper.buildDialog((CommonRes<?>)res, interfaceKey, req.getDialogParams());
}else {
resultCode = "";
resultMsg = "";
}
// log.info("isNeedToAliyunLog: {}, interfaceKey: {}", AliyunLogHelper.isNeedToAliyunLog(interfaceKey), interfaceKey);
if(AliyunLogHelper.isNeedToAliyunLog(interfaceKey))
{
AliyunLogHelper.addTopic(req.getAliyunLogItems(), interfaceKey);
AliyunLogHelper.execute(() -> {
AliyunLogHelper.putLogs(req.getAliyunLogItems());
});
}
Long costTimeMillisecond = System.currentTimeMillis() - starttime;
// 异步保存接口访问记录
MUTMSAccessLogQueueHelper.execute(() -> {
MUTMSAccessLog accessLog = new MUTMSAccessLog();
accessLog.setPassportId(passportId);
accessLog.setTransactionId(transactionId); // 明文transactionId
accessLog.setSessionId(sessionId);
accessLog.setOs(req.getOs());
accessLog.setLocalIp(IpHelper.getLocalIp());
accessLog.setClientRealIp(clentRealIp);
accessLog.setLanguage(req.getLanguage());
accessLog.setRegion(req.getRegion());
accessLog.setAppVersion(req.getAppVersion());
accessLog.setChannelNo(req.getChannelNo());
accessLog.setSecondChannelNo(req.getSecondChannelNo());
accessLog.setThirdChannelNo(req.getThirdChannelNo());
accessLog.setSalesChannel(salesChannel);
accessLog.setCrtDt(TimeUtils.nowLong());
accessLog.setClassName(className);
accessLog.setMethodName(methodName);
accessLog.setImei(req.getImei());
accessLog.setResultCode(resultCode);
accessLog.setResultMsg(resultMsg);
accessLog.setRegistrationID(req.getRegistrationID());
accessLog.setCostTimeMillisecond(costTimeMillisecond);
Map<String, String> accessLogItemsMap = MUTMSAccessLogItemKeyConsts.buildAliyunLogItems(accessLog, req.getAliyunLogItems(), req.getAliyunLogItemsExt());
AliyunLogHelper.putAccessLogs(accessLogItemsMap);
// MongoTemplate mongoTemplate = AppContext.getBean(MongoTemplate.class);
// mongoTemplate.insert(accessLog);
});
//非生产环境打印接口返回值
if(CacheUtil.PRINT_RES) {
// 因为阿里云日志只支持xxxx* 这种方式模糊查找不支持*xxxx这种方式所以给关键日志加一个前缀
log.info("{}{}返回: {}", AliyunLogConsts.MUTMS_RES_PREFIX, methodName, JsonUtil.toString(res,""));
}
log.info("方法: {}, 接口耗时:{}毫秒", methodName, (System.currentTimeMillis()-starttime));
}
return res;
}
private BaseReq getParam(Object args[]) {
String appVersion = null;
Long passportId = null;
// 转BaseReq 对象并返回
if(args !=null) {
for (Object object : args) {
if(object instanceof BaseReq) {
return (BaseReq)object;
// 参数写在 form-data中处理
}else if(object instanceof StandardMultipartHttpServletRequest){
try {
String pid = ((StandardMultipartHttpServletRequest) object).getParameter("passportId");
passportId = Assert.isEmpty(pid)?null:Long.valueOf(pid);
appVersion = ((StandardMultipartHttpServletRequest) object).getParameter("appVersion");
}catch (Exception e){
Assert.callStack(e);
}
}
}
}
BaseReq req =new BaseReq();
req.setLanguage("zh");
req.setRegion("CN");
req.setAppVersion(appVersion);
req.setPassportId(passportId);
return req;
}
}