有沒有遇到這樣子的接口,放到互聯網上面去,誰都可以調用,誰都可以訪問,完全就是公開的,這樣子的接口,如果只是普通的數據,其實可以考慮,只是可以考慮,但是,一般情況下,我們是不允許這樣子做的。
接口安全防什么
1、防止惡意調用攻擊
2、防止篡改信息攻擊
3、防攔截攻擊,數據被截取后進行修改后重新放回去
4、防止數據泄漏攻擊
什么是抓包
抓包(packet capture)就是將網絡傳輸發送與接收的數據包進行截獲、重發、編輯、轉存等操作,也用來檢查網絡安全。抓包也經常被用來進行數據截取等。
這是百度百科給我們的解釋,當我們一些放到互聯網上的數據,直接采用明文的話,就很容易被抓包,然后進行修改或者被惡意植入木馬,這是比較惡心的行為,今天我們就來研究一下怎么樣對接口進行數據加密。
POM文件
<!-- springboot核心web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 配置包,用于配置屬性文件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<!-- 統一API包 -->
<dependency>
<groupId>com.boots</groupId>
<artifactId>module-boots-api</artifactId>
<version>2.0.0.RELEASE</version>
</dependency>
編寫加密解密工具類
/**
* 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)
@Documented
public @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日
*/
@ControllerAdvice
public class ResponseEncryptAdvice implements ResponseBodyAdvice<Object> {
@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<GetDeVO>
*/
@ApiOperation(value = "獲取加密數據")
@GetMapping(value = "/getEncrypt", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@SneakyThrows(CommonRuntimeException.class)
@ResponseEncrypt
public ResponseMsg<GetEncryptVO> getEncrypt() {
final GetEncryptVO vo = new GetEncryptVO();
vo.setId("b037123c");
vo.setUserName("xnwqr98urx");
return MsgUtils.buildSuccessMsg(vo);
}
/**
* 獲取解密數據
* @author 溪云閣
* @return ResponseMsg<GetDeVO>
*/
@ApiOperation(value = "獲取解密數據")
@GetMapping(value = "/getDecrypt", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@SneakyThrows(CommonRuntimeException.class)
public ResponseMsg<String> getDecrypt(@RequestParam(value = "content") String content) {
final String str = AesUtils.decrypt(content, key);
return MsgUtils.buildSuccessMsg(str);
}
}
測試
從實驗的結果上看,我們在獲取數據的時候,直接對data里面的數據進行了加密,這種加密方式只有我們自己可以破解,放到網上去,即使只有密鑰,也破解不了。
這里只做接口的數據的加密,生產中經常需要加入token,時間戳等進行驗證,各位同學自行拓展即可。
--END--
作者:@溪云閣
原創作品,抄襲必究,轉載注明出處
如需要源碼,轉發,關注后私信我
部分圖片或代碼來源網絡,如侵權請聯系刪除,謝謝!