Spring Boot+Angular请求加密响应解密低侵入实现方案咨询
网络层请求响应非明文传输的低代码实现方案(Spring Boot + Angular)
后端(Spring Boot):全局请求解密/响应加密
利用Spring MVC的RequestBodyAdvice和ResponseBodyAdvice实现全局拦截,无需修改单个控制器方法。
1. 实现请求解密处理器
通过RequestBodyAdvice在请求体被解析前完成解密:
@ControllerAdvice public class EncryptedRequestBodyAdvice implements RequestBodyAdvice { @Value("${encryption.aes.key}") private String aesSecretKey; // 从配置文件读取密钥,需符合AES长度要求(16/24/32字节) @Override public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) { // 可通过methodParameter过滤不需要加密的接口,比如健康检查 return !methodParameter.getMethod().getName().contains("health"); } @Override public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException { // 读取加密请求体并解密 byte[] encryptedBytes = StreamUtils.copyToByteArray(inputMessage.getBody()); byte[] decryptedBytes = AESEncryptor.decrypt(encryptedBytes, aesSecretKey); return new HttpInputMessage() { @Override public InputStream getBody() throws IOException { return new ByteArrayInputStream(decryptedBytes); } @Override public HttpHeaders getHeaders() { return inputMessage.getHeaders(); } }; } // 其余默认方法直接返回父类实现即可 }
2. 实现响应加密处理器
通过ResponseBodyAdvice在响应体返回前完成加密:
@ControllerAdvice public class EncryptedResponseBodyAdvice implements ResponseBodyAdvice<Object> { @Value("${encryption.aes.key}") private String aesSecretKey; @Override public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) { return !returnType.getMethod().getName().contains("health"); } @Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { if (body == null) return null; try { // 将响应对象序列化为JSON字符串后加密 String jsonBody = new ObjectMapper().writeValueAsString(body); byte[] encryptedBytes = AESEncryptor.encrypt(jsonBody.getBytes(StandardCharsets.UTF_8), aesSecretKey); // 设置响应头,告知前端数据类型 response.getHeaders().setContentType(MediaType.APPLICATION_OCTET_STREAM); return encryptedBytes; // 若采用Base64传输,可替换为:return Base64.getEncoder().encodeToString(encryptedBytes),并修改Content-Type为application/json } catch (Exception e) { throw new RuntimeException("响应加密失败", e); } } }
3. AES加密工具类
public class AESEncryptor { private static final String ALGORITHM = "AES"; private static final String TRANSFORMATION = "AES/ECB/PKCS5Padding"; public static byte[] encrypt(byte[] data, String secretKey) throws Exception { SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), ALGORITHM); Cipher cipher = Cipher.getInstance(TRANSFORMATION); cipher.init(Cipher.ENCRYPT_MODE, keySpec); return cipher.doFinal(data); } public static byte[] decrypt(byte[] encryptedData, String secretKey) throws Exception { SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), ALGORITHM); Cipher cipher = Cipher.getInstance(TRANSFORMATION); cipher.init(Cipher.DECRYPT_MODE, keySpec); return cipher.doFinal(encryptedData); } }
后端注意事项
- 密钥需妥善存储,生产环境建议使用Spring Cloud Config加密配置或密钥管理服务
- 通过
supports方法精准控制需要加密的接口范围 - 处理解密失败的异常,返回友好的错误响应
前端(Angular):全局请求加密/响应解密
利用Angular的HttpInterceptor实现全局HTTP拦截,无需修改单个请求代码。
1. 安装依赖(可选)
若使用第三方加密库,可安装crypto-js:
npm install crypto-js
2. 实现请求加密拦截器
import { Injectable } from '@angular/core'; import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http'; import { Observable } from 'rxjs'; import { environment } from '../environments/environment'; import * as CryptoJS from 'crypto-js'; @Injectable() export class EncryptInterceptor implements HttpInterceptor { private readonly aesKey = environment.aesSecretKey; intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { // 跳过不需要加密的请求 if (req.url.includes('/health') || req.responseType === 'blob') { return next.handle(req); } let encryptedBody: string; if (req.body) { const jsonStr = JSON.stringify(req.body); encryptedBody = CryptoJS.AES.encrypt(jsonStr, this.aesKey).toString(); } // 克隆请求并替换加密后的请求体 const modifiedReq = req.clone({ body: encryptedBody, headers: req.headers.set('Content-Type', 'application/json') }); return next.handle(modifiedReq); } }
3. 实现响应解密拦截器
import { Injectable } from '@angular/core'; import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpResponse } from '@angular/common/http'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { environment } from '../environments/environment'; import * as CryptoJS from 'crypto-js'; @Injectable() export class DecryptInterceptor implements HttpInterceptor { private readonly aesKey = environment.aesSecretKey; intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { if (req.url.includes('/health') || req.responseType === 'blob') { return next.handle(req); } return next.handle(req).pipe( map(event => { if (event instanceof HttpResponse && event.body) { // 解密响应体并解析为JSON const decryptedStr = CryptoJS.AES.decrypt(event.body, this.aesKey).toString(CryptoJS.enc.Utf8); return event.clone({ body: JSON.parse(decryptedStr) }); } return event; }) ); } }
4. 注册拦截器
在AppModule中注册拦截器:
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; import { EncryptInterceptor } from './encrypt.interceptor'; import { DecryptInterceptor } from './decrypt.interceptor'; import { AppComponent } from './app.component'; @NgModule({ declarations: [AppComponent], imports: [BrowserModule, HttpClientModule], providers: [ { provide: HTTP_INTERCEPTORS, useClass: EncryptInterceptor, multi: true }, { provide: HTTP_INTERCEPTORS, useClass: DecryptInterceptor, multi: true } ], bootstrap: [AppComponent] }) export class AppModule { }
前端注意事项
- 密钥从环境变量读取,避免硬编码
- 拦截器可通过URL、响应类型过滤不需要加密的请求
- 处理解密失败的异常,给出用户提示
工具包推荐
- 后端:Java原生
javax.crypto包(无需额外依赖)、Apache Commons Codec(简化Base64处理) - 前端:
crypto-js(API简洁,易上手)、Web Crypto API(原生支持,无需依赖)
内容的提问来源于stack exchange,提问作者Rikky
相关产品推荐
相关产品推荐

