对外接口,需要校验一下是否有相应权限,简单的一个小代码。
res加密util;
/** * @description: AES加密解密工具 * @author:mic * @create: **/ public class AESUtil { /** * AES加密字符串 * * @param content * 需要被加密的字符串 * @param password * 加密需要的密钥 String password = "123654"; * @return 密文 */ public static String encrypt(String content ,String password) { try { KeyGenerator kgen = KeyGenerator.getInstance("AES");// 创建AES的Key生产者 kgen.init(128, new SecureRandom(password.getBytes()));// 利用用户密码作为随机数初始化出 // 128位的key生产者 //加密没关系,SecureRandom是生成安全随机数序列,password.getBytes()是种子,只要种子相同,序列就一样,所以解密只要有password就行 SecretKey secretKey = kgen.generateKey();// 根据用户密码,生成一个密钥 byte[] enCodeFormat = secretKey.getEncoded();// 返回基本编码格式的密钥,如果此密钥不支持编码,则返回 // null。 SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");// 转换为AES专用密钥 Cipher cipher = Cipher.getInstance("AES");// 创建密码器 byte[] byteContent = content.getBytes("utf-8"); cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化为加密模式的密码器 byte[] result = cipher.doFinal(byteContent);// 加密 return parseByte2HexStr(result);//将二进制密文转换成16进制字符串 } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } return null; } /** * 解密AES加密过的字符串 * * @param content * AES加密过过的内容 * @param password * 加密时的密钥 * @return 明文 */ public static String decrypt(String content, String password) { try { KeyGenerator kgen = KeyGenerator.getInstance("AES");// 创建AES的Key生产者 kgen.init(128, new SecureRandom(password.getBytes())); SecretKey secretKey = kgen.generateKey();// 根据用户密码,生成一个密钥 byte[] enCodeFormat = secretKey.getEncoded();// 返回基本编码格式的密钥 SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");// 转换为AES专用密钥 Cipher cipher = Cipher.getInstance("AES");// 创建密码器 cipher.init(Cipher.DECRYPT_MODE, key);// 初始化为解密模式的密码器 byte[] bytesContent = parseHexStr2Byte(content); byte[] result = cipher.doFinal(bytesContent); return new String(result); // 明文 } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } return null; } /**将二进制转换成16进制 * @param buf * @return */ public static String parseByte2HexStr(byte buf[]) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < buf.length; i++) { String hex = Integer.toHexString(buf[i] & 0xFF); if (hex.length() == 1) { hex = '0' + hex; } sb.append(hex.toUpperCase()); } return sb.toString(); } /**将16进制转换为二进制 * @param hexStr * @return */ public static byte[] parseHexStr2Byte(String hexStr) { if (hexStr.length() < 1) return null; byte[] result = new byte[hexStr.length()/2]; for (int i = 0;i< hexStr.length()/2; i++) { int high = Integer.parseInt(hexStr.substring(i*2, i*2+1), 16); int low = Integer.parseInt(hexStr.substring(i*2+1, i*2+2), 16); result[i] = (byte) (high * 16 + low); } return result; } public static void main(String[] args) { System.out.println(JSON.parseObject("{\"stationId\":1, \"stationName\":\"test\"}")); System.out.println(AESUtil.encrypt("{\"stationId\":1}", "test")); } }
验签切面类:
@Aspect @Component public class SignAspect { Logger logger = LoggerFactory.getLogger(this.getClass()); private static final Map<String, String> accessKeyMap = new HashMap<String, String>(){{put("test", "test2021");}}; /** * 切入点 */ @Pointcut(value = "@annotation(com.sc.Test.support.annotation.Sign)") //对应的sign注解类 private void pointcut() { } @Around("pointcut()") public Object Around(ProceedingJoinPoint pjp) throws Throwable { ResultBean result = new ResultBean(); HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); // 验证签名,在目标方法前执行 //获取请求头中信息,生成密钥,用于解密参数 String signApp = request.getHeader("sign"); String accessKey = request.getHeader("accessKey"); if (StringUtils.isEmpty(accessKey) ) { result.setResultMsgEnum(ResultMsgEnum.RESULT_MSG_UNAUTHORITY); throw new RuntimeException(result.getMsg()); } // 1.先根据access_key做一个简单的判断,判断当前用户是否可以访问此接口 //不存在当前access_key直接返回结果 if (accessKeyMap.get(accessKey) == null) { result.setResultMsgEnum(ResultMsgEnum.RESULT_MSG_UNAUTHORITY); throw new RuntimeException(result.getMsg()); } String password = accessKeyMap.get(accessKey); //2.获取请求体的明文 String param = JSONObject.toJSONString(pjp.getArgs()).replace("[", "").replace("]", ""); //解密参数 String paramContent = AESUtil.decrypt(signApp, password); //3.验签 Map<String, Object> paramMap = JSON.parseObject(param, Map.class); Map<String, Object> paramContentMap = JSON.parseObject(paramContent, Map.class); for (Map.Entry<String, Object> entry : paramMap.entrySet()){ if (entry.getKey().toLowerCase().contains("time")){ String tempTime = DateUtil.timeStamp2Date((long)entry.getValue()); if (!tempTime.equals(paramContentMap.get(entry.getKey()))){ result.setResultMsgEnum(ResultMsgEnum.RESULT_MSG_UNAUTHORITY);//权限不足 throw new RuntimeException(result.getMsg()); } }else { if (!entry.getValue().equals(paramContentMap.get(entry.getKey()))){ result.setResultMsgEnum(ResultMsgEnum.RESULT_MSG_UNAUTHORITY);//权限不足 throw new RuntimeException(result.getMsg()); } } } return pjp.proceed(); } }
@Sign 注解类
/** * 方法签名加密的注解,供接口对拿有密钥第三方用户开放 * * @author test * */ @Target(value = { ElementType.METHOD }) @Retention(value = RetentionPolicy.RUNTIME) @Inherited public @interface Sign { }
需要校验的地方加上sign注解即可:
@Sign @PostMapping("/stationTest") @ResponseBody public ResultBean<String> saveOrUpdateStationTest(@RequestBody Station station){ return stationService.saveOrUpdateStationTest(station); }
第三方调用时,需要按照相关规定将对应类容加密,将密文和秘钥放在heard中,还有当前的明文放在请求体中传过来。
public static void main(String[] args) { try { System.out.println(AESUtil.encrypt("{\"stationId\":2,\"stationName\":\"211\"}", "test2021")); } catch (Exception e) { e.printStackTrace(); System.out.print("加解密异常"); } }