背景說明
我們組負責維護的一個系統,前端界面掛載在其他兩個系統上,因為歷史遺留原因,同時也掛在公網上,沒有登陸功能和用戶體系,只要輸入網址就能訪問,雖然這個系統是給公司內部人員使用,但是存在被惡意登陸并修改用戶數據的風險;
解決思想
這個問題顯然是權限校驗的問題;系統配置攔截器或者過濾器,對其他系統發送的請求進行攔截、設計的方案是先校驗參數完不完整、再校驗token在不在有效期內,最后校驗token是否正確;這三個環節哪個環節沒有校驗成功,不允許訪問;
因為我們的系統是用于公司其他系統使用,這里以系統A為例代表其他系統;我們系統和系統A都要在程序中保存一個密鑰,系統A每次訪問我們系統前,會先根據userId和當前時間戳timestamp根據SHA-256算法生成token,最后將userId、timestamp和token參數傳輸到我們系統,我們系統進行權限校驗;
程序流程
public class ApiSignatureInterceptor implements HandlerInterceptor {private static final long MAX_TIME_DIFF = TimeUnit.MINUTES.toMillis(5);@Value("${custom.interceptor.enabled:false}")private boolean enableInterceptor;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {if (this.enableInterceptor){String userId = request.getHeader("userId");String userName = request.getHeader("userName");String timestamp = request.getHeader("timestamp");String signature = request.getHeader("signature");//1.先驗證數據都存不存在if (userId == null || timestamp == null || signature == null) {sendErr(response,"參數不全");log.error("參數不全");return false;}//boolean isAccess = SignatureUtils.verifySignature()//2.驗證簽名是否過期long currentTime = System.currentTimeMillis();long timeDiff = currentTime - Long.parseLong(timestamp);if (timeDiff > MAX_TIME_DIFF) {sendErr(response,"簽名過期,請重新發送");return false;}//3.驗證token的有效性boolean isAccess = SignatureUtils.verifySignature(buildSignData(userId,timestamp), signature);if (!isAccess) {sendErr(response,"簽名校驗不通過");return false;}return true;}else {return true;}}/**** 生成簽名的原始數據* @param userId* @param timestamp* @return*/private static String buildSignData(String userId,String timestamp){return String.format("userId=%s×tamp=%s",userId,timestamp);}public static void sendErr(HttpServletResponse response,String msg) throws Exception {ResultResponse<Object> error = ResultResponseUtil.error(StatusEnum.FORBIDDEN, msg);response.setContentType("application/json;charset=utf-8");String errJson = JSON.toJSONString(error);response.getWriter().write(errJson);}
}
public static String generateSignatureUtils(String data){try{//簽名實例Mac sha256HMAC = Mac.getInstance(HMAC_SHA256);SecretKeySpec spec = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), HMAC_SHA256);sha256HMAC.init(spec);byte[] hashBytes = sha256HMAC.doFinal(data.getBytes(StandardCharsets.UTF_8));return Base64.getEncoder().encodeToString(hashBytes);}catch (Exception e){log.error("簽名生成失敗" + e.getMessage());throw new BusinessException("簽名生成失敗");}