背景
最近在寫一個小程式接口,由于安全性比較高,是以需要給請求參數和響應進行加密處理。如果在每個方法上都加密解密,那樣代碼就顯得太繁瑣了而且工作量會加大。是以,我們會統一進行加解密處理,一種比較傳統的方式就是通過攔截器進行攔截處理。在這裡我們選擇通過使用spring的aop來實作。
處理方案
1.比較spring的五種通知後。很容易發現,環繞通知可以解決我們的問題,環繞通知有哪些特點呢?
- 環繞通知是所有通知類型中功能最為強大的, 能夠全面地控制連接配接點. 甚至可以控制是否執行連接配接點 。
- 對于環繞通知來說, 連接配接點的參數類型必須是 ProceedingJoinPoint. 它是 JoinPoint的子接口, 允許控制何時執行, 是否執行連接配接點 。
- 在環繞通知中需要明确調用 ProceedingJoinPoint 的 proceed() 方法來執行被代理的方法. 如果忘記這樣做就會導緻通知被執行了, 但目标方法沒有被執行 。
- 環繞通知的方法需要傳回目标方法執行之後的結果, 即調用 joinPoint.proceed(); 的傳回值, 否則會出現空指針異常
2.具體看一下代碼如何實作。
- aspect類
import com.legendnet.elecmeter.utils.EncryptUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
/**
* @Author 情系IT
* @Description
* @Date 2019-12-04 14:55
*/
@Aspect
@Slf4j
@Component
public class HttpAspect {
// 定義切點controller包及子包下面的所有方法
@Pointcut("execution(public * com.legendnet.elecmeter.controller..*.*(..))")
public void httpRequest(){}
@Around("httpRequest()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Exception {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// target 判斷來源然後根據不同的解密算法解密
String target = request.getParameter("target");
String paramCode = request.getParameter("param_code");
paramCode = paramCode.replaceAll(" ", " ");
if(StringUtils.isNotBlank(paramCode)){
if("miniProgram".equals(target)){
paramCode = EncryptUtils.aesDecrypt(paramCode);
}
log.info("請求參數為:【{}】",paramCode);
}
request.setAttribute("param_code",paramCode);
Object proceed = "";
try {
proceed = proceedingJoinPoint.proceed();
if("miniProgram".equals(target)){
proceed = EncryptUtils.aesEncrypt(proceed.toString());
}
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return proceed;
}
}
- controller類
import com.alibaba.fastjson.JSON;
import com.legendnet.elecmeter.common.ResultBean;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author 情系IT
* @Description
* @Date 2019-12-04 15:23
*/
@RestController
@Slf4j
public class TestController {
@RequestMapping("test")
public String test(HttpServletRequest request){
String param_code = request.getAttribute("param_code").toString();
log.info("我已接收到參數,參數為:【{}】",paramCode);
ResultBean resultBean = new ResultBean();
resultBean.fillData("這就是我的響應");
return JSON.toJSONString(resultBean);
}
}
3.由于小程式和app的加密方式不同,為了代碼的高可用,我們通過target參數來判斷其來源,然後選擇不同的加解密方式進行處理。
大功告成,接下來安心的寫接口就可以了,媽媽再也不用擔心我去處理加密解密的問題了。