SpringBoot實(shí)現(xiàn)接口數(shù)據(jù)加解密的三種實(shí)戰(zhàn)方案
一、為什么需要接口數(shù)據(jù)加解密?
在金融支付、用戶隱私信息傳輸?shù)葓?chǎng)景中,接口數(shù)據(jù)若以明文傳輸,極易被中間人攻擊竊取。例如:
- 用戶登錄時(shí)的密碼、身份證號(hào)等敏感信息
- 企業(yè)間數(shù)據(jù)交互的核心業(yè)務(wù)參數(shù)
- 移動(dòng)端與后臺(tái)交互的 token 憑證
Spring Boot 提供了多種優(yōu)雅的加解密實(shí)現(xiàn)方案,既能保證數(shù)據(jù)安全,又能最小化業(yè)務(wù)侵入性。本文將從原理到實(shí)戰(zhàn),帶你掌握三種主流實(shí)現(xiàn)方式。
二、核心加解密算法選擇
1. 對(duì)稱加密(AES)
優(yōu)勢(shì):加密速度快,適合大流量數(shù)據(jù)傳輸缺點(diǎn):密鑰需安全存儲(chǔ),適合客戶端與服務(wù)端一對(duì)一場(chǎng)景
// AES 工具類(128位密鑰) public class AESUtils { private static final String KEY = "your_16bit_secret_key"; private static final String ALGORITHM = "AES/ECB/PKCS5Padding"; public static String encrypt(String data) throws Exception { Cipher cipher = Cipher.getInstance(ALGORITHM); cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(KEY.getBytes(), "AES")); return Base64.getEncoder().encodeToString(cipher.doFinal(data.getBytes())); } public static String decrypt(String data) throws Exception { Cipher cipher = Cipher.getInstance(ALGORITHM); cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(KEY.getBytes(), "AES")); return new String(cipher.doFinal(Base64.getDecoder().decode(data))); } }
2. 非對(duì)稱加密(RSA)
優(yōu)勢(shì):密鑰對(duì)機(jī)制,適合證書(shū)認(rèn)證場(chǎng)景缺點(diǎn):加密效率低,通常用于加密對(duì)稱密鑰
// RSA 工具類(生成公鑰私鑰對(duì)) public class RSAUtils { private static final int KEY_SIZE = 1024; private static final String ALGORITHM = "RSA"; public static Map<String, String> generateKeyPair() throws Exception { KeyPairGenerator generator = KeyPairGenerator.getInstance(ALGORITHM); generator.initialize(KEY_SIZE); KeyPair pair = generator.generateKeyPair(); return Map.of( "publicKey", Base64.getEncoder().encodeToString(pair.getPublic().getEncoded()), "privateKey", Base64.getEncoder().encodeToString(pair.getPrivate().getEncoded()) ); } }
三、實(shí)戰(zhàn)方案一:基于 AOP 的透明加解密
1. 核心原理
通過(guò)自定義注解標(biāo)記需要加解密的接口,利用 Spring AOP 在方法調(diào)用前后自動(dòng)處理加解密邏輯,實(shí)現(xiàn)業(yè)務(wù)代碼零侵入。
2. 實(shí)現(xiàn)步驟
(1)定義加解密注解
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Encrypt { // 排除字段(如時(shí)間戳等無(wú)需加密字段) String[] excludeFields() default {}; } @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Decrypt { // 解密失敗是否拋出異常 boolean throwOnFailure() default true; }
(2)編寫(xiě) AOP 切面
@Aspect @Component public class DataEncryptAspect { @Around("@annotation(Encrypt)") public Object encryptAround(ProceedingJoinPoint joinPoint) throws Throwable { // 執(zhí)行原始方法 Object result = joinPoint.proceed(); // 對(duì)響應(yīng)結(jié)果進(jìn)行AES加密 return AESUtils.encrypt(JSON.toJSONString(result)); } @Around("@annotation(Decrypt)") public Object decryptAround(ProceedingJoinPoint joinPoint) throws Throwable { // 獲取請(qǐng)求參數(shù)(假設(shè)參數(shù)為JSON字符串) Object[] args = joinPoint.getArgs(); String encryptedData = (String) args[0]; // 解密請(qǐng)求參數(shù) String decryptedData = AESUtils.decrypt(encryptedData); // 替換原始參數(shù)為解密后的數(shù)據(jù) args[0] = decryptedData; return joinPoint.proceed(args); } }
(3)控制器使用示例
@RestController @RequestMapping("/api") public class UserController { @PostMapping("/register") @Decrypt public UserRegisterResponse register(@RequestBody String encryptedData) { // 處理解密后的明文數(shù)據(jù) UserRegisterRequest request = JSON.parseObject(encryptedData, UserRegisterRequest.class); // 業(yè)務(wù)邏輯... return new UserRegisterResponse("注冊(cè)成功", request.getUserId()); } @GetMapping("/profile") @Encrypt public UserProfile getProfile(@RequestParam String userId) { // 業(yè)務(wù)邏輯獲取用戶信息 return new UserProfile("張三", "138****1234"); } }
3. 方案優(yōu)勢(shì)
- 低侵入性:僅需在接口方法添加注解
- 靈活配置:可自定義排除字段和異常處理策略
- 適用場(chǎng)景:適合對(duì)單個(gè)接口細(xì)粒度控制的場(chǎng)景
四、實(shí)戰(zhàn)方案二:全局過(guò)濾器實(shí)現(xiàn)請(qǐng)求響應(yīng)加解密
1. 核心原理
通過(guò)實(shí)現(xiàn) Filter 或 HandlerInterceptor,在請(qǐng)求進(jìn)入控制器前解密參數(shù),響應(yīng)離開(kāi)前加密結(jié)果,實(shí)現(xiàn)全局統(tǒng)一加解密。
2. 實(shí)現(xiàn)步驟
(1)自定義加解密過(guò)濾器
@Component public class DataEncryptFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // 處理請(qǐng)求解密(假設(shè)請(qǐng)求體為加密的JSON) HttpServletRequest httpRequest = (HttpServletRequest) request; String encryptedBody = IOUtils.toString(httpRequest.getInputStream(), StandardCharsets.UTF_8); String decryptedBody = AESUtils.decrypt(encryptedBody); // 包裝請(qǐng)求體為可重復(fù)讀取的流 HttpServletRequestWrapper requestWrapper = new HttpServletRequestWrapper(httpRequest, decryptedBody); // 處理響應(yīng)加密 final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); HttpServletResponseWrapper responseWrapper = new HttpServletResponseWrapper((HttpServletResponse) response, buffer); chain.doFilter(requestWrapper, responseWrapper); // 對(duì)響應(yīng)結(jié)果加密并寫(xiě)出 String encryptedResult = AESUtils.encrypt(buffer.toString()); response.getWriter().write(encryptedResult); } } // 請(qǐng)求包裝類(重寫(xiě)getInputStream) class HttpServletRequestWrapper extends HttpServletRequestWrapper { private final String body; public HttpServletRequestWrapper(HttpServletRequest request, String body) { super(request); this.body = body; } @Override public ServletInputStream getInputStream() throws IOException { final ByteArrayInputStream bis = new ByteArrayInputStream(body.getBytes()); return new ServletInputStream() { @Override public int read() throws IOException { return bis.read(); } // 省略其他抽象方法實(shí)現(xiàn) }; } }
(2)配置過(guò)濾器生效
@Configuration public class FilterConfig { @Bean public FilterRegistrationBean<DataEncryptFilter> encryptFilterRegistration() { FilterRegistrationBean<DataEncryptFilter> registration = new FilterRegistrationBean<>(); registration.setFilter(new DataEncryptFilter()); registration.addUrlPatterns("/api/v1/**"); // 配置需要加解密的接口路徑 registration.setOrder(Ordered.HIGHEST_PRECEDENCE); // 保證過(guò)濾器優(yōu)先執(zhí)行 return registration; } }
3. 方案優(yōu)勢(shì)
- 全局統(tǒng)一:一次配置,所有接口自動(dòng)加解密
- 高性能:基于流處理,避免反射帶來(lái)的性能損耗
- 適用場(chǎng)景:適合前后端分離項(xiàng)目的全局?jǐn)?shù)據(jù)加密
五、實(shí)戰(zhàn)方案三:自定義 MessageConverter 實(shí)現(xiàn)透明加解密
1. 核心原理
重寫(xiě) Spring MVC 的 HttpMessageConverter,在請(qǐng)求參數(shù)解析和響應(yīng)數(shù)據(jù)序列化階段自動(dòng)完成加解密,與框架深度整合。
2. 實(shí)現(xiàn)步驟
(1)自定義加解密轉(zhuǎn)換器
public class EncryptingHttpMessageConverter extends AbstractHttpMessageConverter<Object> { @Override protected boolean supports(Class<?> clazz) { return true; // 支持所有類型 } @Override protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { // 讀取加密的請(qǐng)求體并解密 String encrypted = IOUtils.toString(inputMessage.getBody(), StandardCharsets.UTF_8); String decrypted = AESUtils.decrypt(encrypted); return JSON.parseObject(decrypted, clazz); } @Override protected void writeInternal(Object object, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { // 將響應(yīng)對(duì)象加密后寫(xiě)出 String plain = JSON.toJSONString(object); String encrypted = AESUtils.encrypt(plain); outputMessage.getBody().write(encrypted.getBytes(StandardCharsets.UTF_8)); } }
(2)注冊(cè)自定義轉(zhuǎn)換器
@Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { converters.add(new EncryptingHttpMessageConverter()); // 保留默認(rèn)轉(zhuǎn)換器(可選) // converters.addAll(Collections.singletonList(new MappingJackson2HttpMessageConverter())); } }
3. 方案優(yōu)勢(shì)
- 框架級(jí)整合:與 Spring MVC 數(shù)據(jù)綁定機(jī)制深度融合
- 類型安全:自動(dòng)處理對(duì)象與加密字符串的轉(zhuǎn)換
- 適用場(chǎng)景:適合對(duì)請(qǐng)求 / 響應(yīng)格式有嚴(yán)格控制的場(chǎng)景
六、三種方案對(duì)比與選型建議
方案一:AOP 注解
侵入性:低
性能:中靈
活性:接口級(jí)控制
適用場(chǎng)景:部分接口需要加解密
方案二:全局過(guò)濾器
侵入性:中性能:高
靈活性:路徑級(jí)控制
適用場(chǎng)景:前后端分離項(xiàng)目全局加密
方案三:MessageConverter
侵入性:高
性能:最高
靈活性:框架級(jí)控制
適用場(chǎng)景:統(tǒng)一請(qǐng)求響應(yīng)格式場(chǎng)景
七、生產(chǎn)環(huán)境最佳實(shí)踐
1. 密鑰管理方案
- 禁止硬編碼:通過(guò) Spring Config 或配置中心(如 Nacos)管理密鑰
- 密鑰輪換:定期生成新密鑰,舊密鑰逐步淘汰
- 硬件安全:敏感系統(tǒng)使用 HSM(硬件安全模塊)存儲(chǔ)密鑰
2. 異常處理機(jī)制
@RestControllerAdvice public class EncryptExceptionHandler { @ExceptionHandler(DecryptionException.class) public ResponseEntity<String> handleDecryptionError(DecryptionException e) { return ResponseEntity.status(HttpStatus.BAD_REQUEST) .body("數(shù)據(jù)解密失?。? + e.getMessage()); } }
3. 性能優(yōu)化技巧
- 壓縮后加密:對(duì)大體積數(shù)據(jù)先壓縮再加密(Gzip 壓縮可減少 50% 數(shù)據(jù)量)
- 異步加解密:使用 CompletableFuture 實(shí)現(xiàn)加解密與業(yè)務(wù)邏輯并行處理
- 緩存加密結(jié)果:對(duì)高頻訪問(wèn)接口的加密結(jié)果進(jìn)行緩存
八、總結(jié)
Spring Boot 提供了從接口級(jí)到框架級(jí)的完整加解密解決方案,核心是根據(jù)業(yè)務(wù)場(chǎng)景選擇合適的實(shí)現(xiàn)方式:
- 追求靈活性選 AOP 注解
- 追求統(tǒng)一性選 全局過(guò)濾器
- 追求框架整合選 MessageConverter
無(wú)論哪種方案,都需注意密鑰安全和異常處理。通過(guò)本文的源碼示例,開(kāi)發(fā)者可快速在項(xiàng)目中落地接口數(shù)據(jù)加解密功能,在保障數(shù)據(jù)安全的同時(shí),最小化對(duì)現(xiàn)有業(yè)務(wù)的影響。
以上就是SpringBoot實(shí)現(xiàn)接口數(shù)據(jù)加解密的三種實(shí)戰(zhàn)方案的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot接口數(shù)據(jù)加解密的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
解決Maven parent.relativePath帶給我的坑
在Linux環(huán)境下使用Maven進(jìn)行項(xiàng)目打包時(shí),可能會(huì)遇到“當(dāng)前目錄沒(méi)有pom文件”的錯(cuò)誤,需要確認(rèn)在包含pom.xml文件的項(xiàng)目目錄下執(zhí)行Maven命令,另外,如果遇到“parent.relativePath points at wrong local POM”錯(cuò)誤,可能是父模塊依賴問(wèn)題2024-09-09java Hibernate 一對(duì)多自身關(guān)聯(lián)問(wèn)題
formBean在提交表單的時(shí)候,域中數(shù)據(jù)庫(kù)在下一次中仍然保留引起的,struts formBean 默認(rèn)的scope為session,手動(dòng)設(shè)置為request,就好了2008-07-07java進(jìn)行數(shù)據(jù)的比較的實(shí)例方法
在本篇文章里小編給大家整理的是一篇關(guān)于java進(jìn)行數(shù)據(jù)的比較的實(shí)例方法,有需要的朋友們可以學(xué)習(xí)下。2021-04-04Spring中事務(wù)幾個(gè)常見(jiàn)的問(wèn)題解決
這篇文章主要介紹了Spring中事務(wù)幾個(gè)常見(jiàn)的問(wèn)題解決,事務(wù)這個(gè)概念是數(shù)據(jù)庫(kù)層面的,Spring只是基于數(shù)據(jù)庫(kù)中的事務(wù)進(jìn)行擴(kuò)展,以及提供了一些能讓程序員更新方便操作事務(wù)的方式2022-08-08SpringBoot初始教程之Servlet、Filter、Listener配置詳解
本篇文章主要介紹了SpringBoot初始教程之Servlet、Filter、Listener配置詳解,具有一定的參考價(jià)值,有興趣的可以了解一下2017-09-09JavaSwing坦克大戰(zhàn)游戲的設(shè)計(jì)和實(shí)現(xiàn)
JavaSwing坦克大戰(zhàn)游戲的設(shè)計(jì)要有圖形用戶界面,界面能夠反映游戲所有的細(xì)節(jié),在最終呈現(xiàn)的游戲中也要滿足所有需求,感興趣的小伙伴一起來(lái)看看吧2021-08-08