详解API接口如何安全的传输数据

news2024/9/24 11:30:49

概述

API接口的安全传输是确保数据在API请求和响应之间的传输过程中不被截获、篡改或泄露的重要步骤。以下是一些用于增强API接口安全传输的常见技术和最佳实践:

  1. 使用HTTPS:使用HTTPS协议而不是HTTP,以确保数据在传输过程中的安全性。HTTPS使用SSL/TLS协议对通信进行加密,防止中间人攻击和数据窃听。

  2. 验证HTTPS请求:验证HTTPS请求的来源,确保请求来自授权的客户端。这可以通过检查SSL证书的颁发机构和有效期来实现。

  3. 验证API密钥:验证API请求中包含的API密钥的合法性。这可以通过检查密钥的唯一标识符、有效性和权限来实现。

  4. 使用JSON Web Tokens (JWT):JWT是一种开放标准,用于在双方之间安全地传输信息。JWT包含一组声明,由JSON对象表示,并使用数字签名进行验证。它可以用于API身份验证和授权。

  5. 限制API访问频率:限制API请求的频率和并发数,以防止滥用和拒绝服务攻击。这可以通过设置速率限制和并发限制来实现。

  6. 使用消息身份验证码(MAC):消息身份验证码是一种用于验证消息完整性和认证性的机制。它可以用于防止篡改和重放攻击。

  7. 加密敏感数据:对传输的敏感数据进行加密,例如用户密码和个人信息。这可以通过使用对称加密或公钥加密来实现。

  8. 使用合适的HTTP标头:使用适当的HTTP标头来防止跨站脚本攻击(XSS)和其他安全漏洞。例如,设置"X-XSS-Protection: 1; mode=block"标头来启用浏览器的内置XSS保护机制。

  9. 实施访问控制:根据用户的身份和权限,对API请求进行访问控制。这可以通过使用基于角色的访问控制(RBAC)或基于声明的访问控制(ABAC)来实现。

  10. 定期更新和修补:确保API和相关系统得到及时更新和修补,以修复任何已知的安全漏洞。

在Spring中我们通过继承RequestBodyAdviceAdapter实现对于请求的内容进行解密操作,实现ResponseBodyAdvice来对相应内容进行加密处理。接下来将详细讲解数据加解密的实现过程。

定义加密解密的接口:

public interface SecretProcess {    /**   *  <p>数据加密</p>   *  <p>时间:2020年12月24日-下午12:22:13</p>   * @author xg   * @param data 待加密数据   * @return String 加密结果   */  String encrypt(String data) ;    /**   *  <p>数据解密</p>   *  <p>时间:2020年12月24日-下午12:23:20</p>   * @author xg   * @param data 待解密数据   * @return String 解密后的数据   */  String decrypt(String data) ;    /**   *  <p>加密算法格式:算法[/模式/填充]</p>   *  <p>时间:2020年12月24日-下午12:32:49</p>   * @author xg   * @return String   */  String getAlgorithm() ;    public static class Hex {        private static final char[] HEX = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',        'a', 'b', 'c', 'd', 'e', 'f' };        public static byte[] decode(CharSequence s) {      int nChars = s.length();      if (nChars % 2 != 0) {        throw new IllegalArgumentException("16进制数据错误");      }      byte[] result = new byte[nChars / 2];      for (int i = 0; i < nChars; i += 2) {        int msb = Character.digit(s.charAt(i), 16);        int lsb = Character.digit(s.charAt(i + 1), 16);        if (msb < 0 || lsb < 0) {          throw new IllegalArgumentException(            "Detected a Non-hex character at " + (i + 1) + " or " + (i + 2) + " position");        }        result[i / 2] = (byte) ((msb << 4) | lsb);      }      return result;    }        public static String encode(byte[] buf) {      StringBuilder sb = new StringBuilder() ;      for (int i = 0, leng = buf.length; i < leng; i++) {        sb.append(HEX[(buf[i] & 0xF0) >>> 4]).append(HEX[buf[i] & 0x0F]) ;      }      return sb.toString() ;    }      }  }

该接口中定义了两个方法分别是加密与解密的方法,还有Hex类 该类用来对数据处理16进制的转换。

定义一个抽象类实现上面的接口,具体的加解密实现细节在该抽象类中

AbstractSecretProcess

public abstract class AbstractSecretProcess implements SecretProcess {    @Resource  private SecretProperties props ;    @Override  public String decrypt(String data) {    try {      Cipher cipher = Cipher.getInstance(getAlgorithm()) ;      cipher.init(Cipher.DECRYPT_MODE, keySpec()) ;      byte[] decryptBytes = cipher.doFinal(Hex.decode(data)) ;      return new String(decryptBytes) ;    } catch (Exception e) {      throw new RuntimeException(e) ;    }  }    @Override  public String encrypt(String data) {    try {      Cipher cipher = Cipher.getInstance(getAlgorithm()) ;      cipher.init(Cipher.ENCRYPT_MODE, keySpec()) ;      return Hex.encode(cipher.doFinal(data.getBytes(Charset.forName("UTF-8")))) ;    } catch (Exception e) {      throw new RuntimeException(e) ;    }  }    /**   *  <p>根据密钥生成不同的密钥材料</p>   *  <p>目前支持:AES, DES</p>   *  <p>时间:2020年12月25日-下午1:02:54</p>   * @author xg   * @param secretKey 密钥   * @param algorithm 算法   * @return Key   */  public Key getKeySpec(String algorithm) {    if (algorithm == null || algorithm.trim().length() == 0) {      return null ;    }    String secretKey = props.getKey() ;    switch (algorithm.toUpperCase()) {      case "AES":        return new SecretKeySpec(secretKey.getBytes(), "AES") ;      case "DES":        Key key = null ;        try {          DESKeySpec desKeySpec = new DESKeySpec(secretKey.getBytes()) ;          SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("DES") ;          key = secretKeyFactory.generateSecret(desKeySpec);        } catch (Exception e) {          throw new RuntimeException(e) ;        }        return key ;      default:        return null ;    }  }    /**   *  <p>生成密钥材料</p>   *  <p>时间:2020年12月25日-上午11:35:03</p>   * @author xg   * @return Key 密钥材料   */  public abstract Key keySpec() ;  }

该抽象类中提供了2中对称加密的密钥还原,分表是AES和DES算法。一个抽象方法,该抽象方法

keySpec该方法需要子类实现(具体使用的是哪种对称加密算法)。

具体加密算法的实现类

AESAlgorithm

public class AESAlgorithm extends AbstractSecretProcess {
  @Override  public String getAlgorithm() {    return "AES/ECB/PKCS5Padding";  }    @Override  public Key keySpec() {    return this.getKeySpec("AES") ;  }
}

SecretProperties

@Configurationpublic class SecretConfig {    @Bean  @ConditionalOnMissingBean(SecretProcess.class)  public SecretProcess secretProcess() {    return new AESAlgorithm() ;  }    @Component  @ConfigurationProperties(prefix = "secret")  public static class SecretProperties {        private Boolean enabled ;    private String key ;
    public Boolean getEnabled() {      return enabled;    }
    public void setEnabled(Boolean enabled) {      this.enabled = enabled;    }
    public String getKey() {      return key;    }
    public void setKey(String key) {      this.key = key;    }      }  }

配置文件中如下配置:

secret:  key: aaaabbbbccccdddd #密钥  enabled: true #是否开启加解密功能

在项目中可能不是所有的方法都要进行数据的加密解密出来,所以接下来定义一个注解,只有添加有该注解的Controller类或是具体接口方法才进行数据的加密解密,如下:

SIProtection

@Target({ElementType.METHOD, ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Mapping@Documentedpublic @interface SIProtection {
}

对请求内容进行解密出来,通过RequestBodyAdvice

DecryptRequestBodyAdivce

@ControllerAdvice@ConditionalOnProperty(name = "secret.enabled", havingValue = "true")public class DecryptRequestBodyAdivce extends RequestBodyAdviceAdapter {
  @Resource  private SecretProcess secretProcess ;    @Override  public boolean supports(MethodParameter methodParameter, Type targetType,      Class<? extends HttpMessageConverter<?>> converterType) {    return methodParameter.getMethod().isAnnotationPresent(SIProtection.class)         || methodParameter.getMethod().getDeclaringClass().isAnnotationPresent(SIProtection.class) ;  }
  @Override  public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType,      Class<? extends HttpMessageConverter<?>> converterType) throws IOException {    String body = secretProcess.decrypt(inToString(inputMessage.getBody())) ;    return new HttpInputMessage() {      @Override      public HttpHeaders getHeaders() {        return inputMessage.getHeaders();      }      @Override      public InputStream getBody() throws IOException {        return new ByteArrayInputStream(body.getBytes()) ;      }    } ;  }    private String inToString(InputStream is) {    byte[] buf = new byte[10 * 1024] ;    int leng = -1 ;    StringBuilder sb = new StringBuilder() ;    try {      while ((leng = is.read(buf)) != -1) {        sb.append(new String(buf, 0, leng)) ;      }      return sb.toString() ;    } catch (IOException e) {      throw new RuntimeException(e) ;    }  }
}

注意这里的:@ConditionalOnProperty(name = "secret.enabled", havingValue = "true")注解,只有开启了加解密功能才会生效。注意这里的supports方法

对响应内容加密出来

EncryptResponseBodyAdivce

@ControllerAdvice@ConditionalOnProperty(name = "secret.enabled", havingValue = "true")public class EncryptResponseBodyAdivce implements ResponseBodyAdvice<Object>  {
  @Resource  private SecretProcess secretProcess ;
  @Override  public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {    return returnType.getMethod().isAnnotationPresent(SIProtection.class)         || returnType.getMethod().getDeclaringClass().isAnnotationPresent(SIProtection.class) ;  }
  @Override  public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,      Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,      ServerHttpResponse response) {    if (body == null) {      return body ;    }    try {      String jsonStr = new ObjectMapper().writeValueAsString(body) ;      return secretProcess.encrypt(jsonStr) ;    } catch (Exception e) {      throw new RuntimeException(e) ;    }  }}

Controller接口

@PostMapping("/save")@SIProtectionpublic R save(@RequestBody Users users) {  return R.success(usersService.save(users)) ;} // 这对具体方法进行加解密
@RestController@RequestMapping("/users")@SIProtection public class UsersController { // 对该Controller中的所有方法进行加解密处理}

前端

引入第三方插件:crypto-js

工具方法加解密:

/** * 加密方法 * @param data 待加密数据 * @returns {string|*} */encrypt (data) {  let key = CryptoJS.enc.Utf8.parse(Consts.Secret.key)  if (typeof data === 'object') {    data = JSON.stringify(data)  }  let plainText = CryptoJS.enc.Utf8.parse(data)  let secretText = CryptoJS.AES.encrypt(plainText, key, {mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7}).ciphertext.toString()  return secretText},/** * 解密数据 * @param data 待解密数据 */decrypt (data) {  let key = CryptoJS.enc.Utf8.parse(Consts.Secret.key)  let secretText = CryptoJS.enc.Hex.parse(data)  let encryptedBase64Str = CryptoJS.enc.Base64.stringify(secretText)  let result = CryptoJS.AES.decrypt(encryptedBase64Str, key, {mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7}).toString(CryptoJS.enc.Utf8)  return JSON.parse(result)}

配置:

let Consts = {  Secret: {    key: 'aaaabbbbccccdddd', // 必须16位(前后端要一致,密钥)    urls: ['/users/save']  }}export default Consts

这里的urls表示对那些请求进行拦截出来(加解密),这里也可以配置 "*" 表示对所有的请求出来。

axios请求前和响应后对数据进行加解密出来:

发送请求前:

axios.interceptors.request.use((config) => {  let uri = config.url  if (uri.includes('?')) {    uri = uri.substring(0, uri.indexOf('?'))  }  if (window.cfg.enableSecret === '1' && config.data && (Consts.Secret.urls.indexOf('*') > -1 || Consts.Secret.urls.indexOf(uri) > -1)) {    let data = config.data    let secretText = Utils.Secret.encrypt(data)    config.data = secretText  }  return config}, (error) => {  let errorMessage = '请求失败'  store.dispatch(types.G_SHOW_ALERT, {title: '请求失败', content: errorMessage, showDetail: false, detailContent: String(error)})  return Promise.reject(error)})axios.interceptors.response.use((response) => {  let uri = response.config.url  if (uri.includes('?')) {    uri = uri.substring(0, uri.indexOf('?'))  }  if (window.cfg.enableSecret === '1' && response.data && (Consts.Secret.urls.indexOf('*') > -1 || Consts.Secret.urls.indexOf(uri) > -1)) {    let data = Utils.Secret.decrypt(response.data)    if (data) {      response.data = data    }  }  return response}, (error) => {  console.error(`test interceptors.response is in, ${error}`)  return Promise.reject(error)})

这里的 window.cfg.enableSecret 配置是我自己项目中有个配置文件配置是否开启,这个大家可以根据自己的环境来实现。

测试:

图片

这里可以看到前端发起的请求内容已经被加密了

响应内容:

图片

完毕!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1019677.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

入户式防汛报警器是什么?

入户式防汛报警器是一款能够和雨量气象监测设备搭配的仪器&#xff0c;是集实时监测、信息显示、多时段声光报警功能的监测报警设备&#xff0c;支持不同级别的声光报警。 当监测要素超过设定的危险警戒值时设备发出报警声&#xff0c;提醒居民做好防汛&#xff08;转移&#…

无所不知的Mac效率神器 Alfred 4,一顶神器的小帽子!

肯定很多人在工作、学习或者写东西的时候&#xff0c;总想着怎样可以提高效率&#xff0c;节约时间。现在每天用得最多的电脑&#xff0c;怎样才能够让工作效率更高呢&#xff1f;带着这样的一个目的&#xff0c;使用了很多的小工具&#xff0c;有些可以简化工作&#xff0c;有…

JDK、JRE和JVM

JDK&#xff08; java 开发工具 &#xff09;&#xff1a; Java Develpment Kit&#xff0c;主要包含了工具程序与API、JRE JRE&#xff08;java运行时环境 &#xff09;&#xff1a; Java Runtime Environment&#xff0c;主要包含了JavaSE核心类库和Java虚拟机&#xff08;JV…

深入浅出学Verilog--数字电路基础内容

什么是数字系统&#xff1f; 数字系统是一种表示数值的方式。它是一组使我们能够计数、测量和计算数值量的规则、符号和过程。最常见的数字系统是使用数字 0-9 的十进制系统和仅使用 0 和 1 的二进制系统。其他数字系统包括八进制、十六进制和罗马数字。每个数字系统都有其独特…

URL 路径中包含百分号需要在 Swift 中的特殊处理

概览 在 Swift 中访问或处理 URL 路径常常遇见路径中包含百分号(%)的情况,此时我们有必要做特殊处理,否则 URL 的请求和解析可能会发生错误。 如果 URL 路径中包含用百分号前缀表示的特殊字符,在之后的路径解析中可能会出现意想不到的问题。 在本篇博文中,您将学到以下…

数字展厅如何设计与策划,一文了解搭建数字展厅要哪些步骤

引言&#xff1a; 数字展厅&#xff0c;作为一种现代化的宣传工具&#xff0c;已经在商业界崭露头角。我们将了解什么是数字展厅&#xff0c;然后探讨数字展厅设计的关键要素&#xff0c;接着讲述数字展厅策划的核心原则&#xff0c;最后详细介绍数字展厅的搭建过程。 一&…

C# OpenVino Yolov8 Cls 分类

效果 项目 代码 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using OpenCvSharp;namespace OpenVino_Yolov8_Demo {public…

GMS地下水数值模拟及溶质(包含反应性溶质)运移模拟技术教程

详情点击公众号链接&#xff1a;GMS地下水数值模拟及溶质&#xff08;包含反应性溶质&#xff09;运移模拟技术教程 前言 GMS三维地质结构建模 GMS地下水流数值模拟 GMS溶质运移数值模拟与反应性溶质运移模 第一地下水数值模拟理论模块 1.1 地下水渗流运动方程 1.2 地下水数…

docker启动前端

docker启动前端 作为一个前端&#xff0c;代码写完&#xff0c;最后部署到服务器&#xff0c;这是一个必须要了解的过程&#xff0c;今天&#xff0c;我就在docker 中安装nginx 并部署做一个简单的笔记。 首先我在某云中有个云服务器&#xff0c;并登陆 前言 提示&#xff1…

【2023研电赛】西北赛区二等奖:单链全导联便携式心脏健康监测系统

本文为2023年第十八届中国研究生电子设计竞赛技术竞赛类西北赛区二等奖分享&#xff0c;参加极术社区的【有奖活动】分享2023研电赛作品扩大影响力&#xff0c;更有丰富电子礼品等你来领&#xff01;&#xff0c;分享2023研电赛作品扩大影响力&#xff0c;更有丰富电子礼品等你…

SQL性能优化

当你的数据里只有几千几万&#xff0c;那么 SQL 优化并不会发挥太大价值&#xff0c;但当你的数据里去到了几百上千万&#xff0c;SQL 优化的价值就体现出来了&#xff01;因此稍微有些经验的同学都知道&#xff0c;怎么让 MySQL 查询语句又快又好是一件很重要的事情。要让 SQL…

Opencv 4.5.5 linux contrib编译

https://github.com/opencv/opencv/releases https://github.com/opencv/opencv_contrib/releases/tag/4.7.0 版本要一模一样 下载4.5.5的源码 # 0. 将contrib文件夹移动到opencv文件夹中去 mkdir build cd build# 1. cmake生成配置文件&#xff0c;其中OPENCV_EXTRA_MODUL…

内网桌面如何让外网电脑连接?快解析内网映射域名设置

异地电脑远程桌面连接局域网服务器需求场景时&#xff0c;大多人会想到在目标本地的路由器上做端口映射设置。其实路由映射方法只适用于有公网IP的网络环境。当所在联网环境下是内部网络&#xff0c;内网环境下无公网IP或无公网IP权限时&#xff0c;路由映射方法就不生效了&…

【谢希尔 计算机网络】第2章 物理层

目录 通信基础 基本概念 两个公式lim 奈氏准则 香农定理 奈氏准则 VS 香农定理 编码与调制 ​编辑 物理层下面的传输媒体 导引型传输媒体 1. 双绞线 2. 同轴电缆 3. 光缆 非导引型传输媒体 无线电微波通信 卫星通信 无线局域网使用的 ISM 频段 信道复用技术 …

RS485总线浪涌解决方案!|深圳比创达EMC

在各种通讯方式当中&#xff0c;RS485总线是较为常见的一种&#xff0c;因其接口简单、系统运行稳定和通讯速率高等特点被广泛应用。但由于RS485总线一般传输距离较长&#xff0c;且经常暴露在外&#xff0c;所以非常容易受到电磁干扰&#xff0c;而浪涌干扰已经成为485通信使用…

【漏洞复现】广联达OA漏洞合集(信息泄露+SQL注入+文件上传)

文章目录 声明广联达OA存在信息泄露一、漏洞概述二、漏洞复现三、修复建议广联达Linkworks办公OA SQL注入漏洞后台文件上传漏洞一、产品简介二、漏洞概述三、复现环境四、修复建议 声明 请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工…

无涯教程-JavaScript - MOD函数

描述 MOD函数返回数字除以除数后的余数。输出与除数的符号相同。 语法 MOD (number, divisor)争论 Argument描述Required/OptionalNumberThe number for which you want to find the remainder.RequiredDivisorThe number by which you want to divide number.Required No…

Spring注解 bean基础

一、Spring注解 bean扫描与注入 <dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>4.3.12.RELEASE</version></dependency>1. 组件注册Configuration和Bean的注入 1&…

FPGA计数器边界问题解析

FPGA计数器边界问题解析 一次作者在处理AMBE2000数据接收过程中&#xff0c;遇到一个问题&#xff0c;对该计数器边界总是模糊不清。现在予以说明&#xff0c;以警示以后工作时书写错误代码。 AMBE2000数据一旦准备好后&#xff0c;一次会输出24个字&#xff0c;其中第1个字0x…

swift 问答app

结构体 mvc模式 不变性 试一试