溪云阁:专注编程教学,架构,JAVA,Python,微服务,机器学习等,欢迎关注
上一篇:springboot2.2.X手册:redis的7种类型100个方法全解析
有没有遇到这样子的接口,放到互联网上面去,谁都可以调用,谁都可以访问,完全就是公开的,这样子的接口,如果只是普通的数据,其实可以考虑,只是可以考虑,但是,一般情况下,我们是不允许这样子做的。
接口安全防什么
1、防止恶意调用攻击
2、防止篡改信息攻击
3、防拦截攻击,数据被截取后进行修改后重新放回去
4、防止数据泄漏攻击
什么是抓包
抓包(packet capture)就是将网络传输发送与接收的数据包进行截获、重发、编辑、转存等操作,也用来检查网络安全。抓包也经常被用来进行数据截取等。
这是百度百科给我们的解释,当我们一些放到互联网上的数据,直接采用明文的话,就很容易被抓包,然后进行修改或者被恶意植入木马,这是比较恶心的行为,今天我们就来研究一下怎么样对接口进行数据加密。
POM文件
org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-autoconfigure com.bootsmodule-boots-api2.0.0.RELEASE
编写加密解密工具类
/** * All rights Reserved, Designed By 林溪 * Copyright: Copyright(C) 2016-2020 * Company 溪云阁 . */package com.module.boots.api.de.utils;import java.security.NoSuchAlgorithmException;import java.security.SecureRandom;import javax.crypto.Cipher;import javax.crypto.KeyGenerator;import javax.crypto.SecretKey;import javax.crypto.spec.SecretKeySpec;import org.apache.tomcat.util.codec.binary.Base64;import com.module.boots.exception.CommonRuntimeException;/** * AES加密解密 * @author:溪云阁 * @date:2020年6月4日 */public class AesUtils { private static final String KEY_ALGORITHM = "AES"; private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";// 默认的加密算法 /** * AES 加密操作 * @author 溪云阁 * @param content 待加密内容 * @param password 加密密码 * @return String 返回Base64转码后的加密数据 */ public static String encrypt(String content, String password) { try { // 创建密码器 final Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM); // 设置为UTF-8编码 final byte[] byteContent = content.getBytes("utf-8"); // 初始化为加密模式的密码器 cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(password)); // 加密 final byte[] result = cipher.doFinal(byteContent); // 通过Base64转码返回 return Base64.encodeBase64String(result); } catch (final Exception ex) { throw new CommonRuntimeException(ex.fillInStackTrace()); } } /** * AES 解密操作 * @author 溪云阁 * @param content * @param password * @return String */ public static String decrypt(String content, String password) { try { // 实例化 final Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM); // 使用密钥初始化,设置为解密模式 cipher.init(Cipher.DECRYPT_MODE, getSecretKey(password)); // 执行操作 final byte[] result = cipher.doFinal(Base64.decodeBase64(content)); // 采用UTF-8编码转化为字符串 return new String(result, "utf-8"); } catch (final Exception ex) { throw new CommonRuntimeException(ex.fillInStackTrace()); } } /** * 生成加密秘钥 * @author 溪云阁 * @param password 加密的密码 * @return SecretKeySpec */ private static SecretKeySpec getSecretKey(final String password) { // 返回生成指定算法密钥生成器的 KeyGenerator 对象 KeyGenerator kg = null; try { kg = KeyGenerator.getInstance(KEY_ALGORITHM); // AES 要求密钥长度为 128 kg.init(128, new SecureRandom(password.getBytes())); // 生成一个密钥 final SecretKey secretKey = kg.generateKey(); // 转换为AES专用密钥 return new SecretKeySpec(secretKey.getEncoded(), KEY_ALGORITHM); } catch (final NoSuchAlgorithmException ex) { throw new CommonRuntimeException(ex.fillInStackTrace()); } } public static void main(String[] args) { final String str = "V9JofCHn02eyXRiDb1VuseRSuOgEQftROwudMPWwMAO2Wk5K7aYZ4Vtm6xiTn5i5"; System.out.println(decrypt(str, "xy934yrn9342u0ry4br8cn-9u2")); }}
编写加密注解
/** * All rights Reserved, Designed By 林溪 * Copyright: Copyright(C) 2016-2020 * Company 溪云阁 . */package com.module.boots.api.de;import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/** * 返回对body加密,针对类跟方法 * @author:溪云阁 * @date:2020年6月4日 */@Target({ ElementType.METHOD, ElementType.TYPE })@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface ResponseEncrypt { /** * 返回对body加密,默认是true * @author 溪云阁 * @return boolean */ boolean value() default true;}
编写加密判断类
/** * All rights Reserved, Designed By 林溪 * Copyright: Copyright(C) 2016-2020 * Company 溪云阁 . */package com.module.boots.api.de;import org.springframework.core.MethodParameter;/** * 是否需要加密解密 * @author:溪云阁 * @date:2020年6月4日 */public class NeedDe { /** * 判断是否需要加密 * @author 溪云阁 * @param returnType * @return boolean */ public static boolean needEncrypt(MethodParameter returnType) { boolean encrypt = false; // 获取类上的注解 final boolean classPresentAnno = returnType.getContainingClass().isAnnotationPresent(ResponseEncrypt.class); // 获取方法上的注解 final boolean methodPresentAnno = returnType.getMethod().isAnnotationPresent(ResponseEncrypt.class); if (classPresentAnno) { // 类上标注的是否需要加密 encrypt = returnType.getContainingClass().getAnnotation(ResponseEncrypt.class).value(); // 类不加密,所有都不加密 if (!encrypt) { return false; } } if (methodPresentAnno) { // 方法上标注的是否需要加密 encrypt = returnType.getMethod().getAnnotation(ResponseEncrypt.class).value(); } return encrypt; }}
编写加密拦截
/** * All rights Reserved, Designed By 林溪 * Copyright: Copyright(C) 2016-2020 * Company 溪云阁 . */package com.module.boots.api.de;import org.springframework.beans.factory.annotation.Value;import org.springframework.core.MethodParameter;import org.springframework.http.MediaType;import org.springframework.http.converter.HttpMessageConverter;import org.springframework.http.server.ServerHttpRequest;import org.springframework.http.server.ServerHttpResponse;import org.springframework.web.bind.annotation.ControllerAdvice;import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;import com.module.boots.api.de.utils.AesUtils;import com.module.boots.api.message.ResponseMsg;/** * 对接口数据进行加密 * @author:溪云阁 * @date:2020年6月4日 */@ControllerAdvicepublic class ResponseEncryptAdvice implements ResponseBodyAdvice { @Value("${module.boots.response.aes.key}") private String key; @Override public boolean supports(MethodParameter returnType, Class extends HttpMessageConverter>> converterType) { return true; } /** * 在写入之前更改body的值 * @author 溪云阁 * @param body * @param returnType * @param selectedContentType * @param selectedConverterType * @param request * @param response * @return * @return */ @SuppressWarnings({ "unchecked", "rawtypes" }) @Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class extends HttpMessageConverter>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { // 判断是否需要加密 final boolean encrypt = NeedDe.needEncrypt(returnType); if (!encrypt) { return body; } else { // 如果body是属于ResponseMsg类型,只需要对data里面的数据进行加密即可 if (body instanceof ResponseMsg) { final ResponseMsg responseMsg = (ResponseMsg) body; final Object data = responseMsg.getData(); if (data == null) { return body; } else { responseMsg.setData(AesUtils.encrypt(data.toString(), key)); return responseMsg; } } else { return body; } } }}
加入密钥
# aes的密钥module.boots.response.aes.key: xy934yrn9342u0ry4br8cn-9u2
编写加密解密接口
/** * All rights Reserved, Designed By 林溪 * Copyright: Copyright(C) 2016-2020 * Company 溪云阁 . */package com.boots.api.de.view.de.view;import org.springframework.beans.factory.annotation.Value;import org.springframework.http.MediaType;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;import com.boots.api.de.view.de.vo.GetEncryptVO;import com.module.boots.api.de.ResponseEncrypt;import com.module.boots.api.de.utils.AesUtils;import com.module.boots.api.message.ResponseMsg;import com.module.boots.api.utils.MsgUtils;import com.module.boots.exception.CommonRuntimeException;import io.swagger.annotations.Api;import io.swagger.annotations.ApiOperation;import lombok.SneakyThrows;/** * 加密数据接口 * @author:溪云阁 * @date:2020年6月4日 */@SuppressWarnings("deprecation")@Api(tags = { "web服务:加密数据接口" })@RestController@RequestMapping("view/deView")public class DeView { @Value("${module.boots.response.aes.key}") private String key; /** * 获取加密数据 * @author 溪云阁 * @return ResponseMsg */ @ApiOperation(value = "获取加密数据") @GetMapping(value = "/getEncrypt", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) @SneakyThrows(CommonRuntimeException.class) @ResponseEncrypt public ResponseMsg getEncrypt() { final GetEncryptVO vo = new GetEncryptVO(); vo.setId("b037123c"); vo.setUserName("xnwqr98urx"); return MsgUtils.buildSuccessMsg(vo); } /** * 获取解密数据 * @author 溪云阁 * @return ResponseMsg */ @ApiOperation(value = "获取解密数据") @GetMapping(value = "/getDecrypt", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) @SneakyThrows(CommonRuntimeException.class) public ResponseMsg getDecrypt(@RequestParam(value = "content") String content) { final String str = AesUtils.decrypt(content, key); return MsgUtils.buildSuccessMsg(str); }}
测试
从实验的结果上看,我们在获取数据的时候,直接对data里面的数据进行了加密,这种加密方式只有我们自己可以破解,放到网上去,即使只有密钥,也破解不了。
这里只做接口的数据的加密,生产中经常需要加入token,时间戳等进行验证,各位同学自行拓展即可。
--END--
作者:@溪云阁
原创作品,抄袭必究,转载注明出处
如需要源码,转发,关注后私信我
部分图片或代码来源网络,如侵权请联系删除,谢谢!