范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

如何保证API接口安全?

  一、摘要
  在实际的业务开发过程中,我们常常会碰到需要与第三方互联网公司进行技术对接,例如支付宝支付对接、微信支付对接、高德地图查询对接等等服务,如果你是一个创业型互联网,大部分可能都是对接别的公司api接口。
  当你的公司体量上来了时候,这个时候可能有一些公司开始找你进行技术对接了,转变成由你来提供api接口,那这个时候,我们应该如何设计并保证API接口安全呢?  二、方案介绍
  最常用的方案,主要有两种:  token方案  接口签名  2.1、token方案
  其中 token 方案,是一种在web端使用最广的接口鉴权方案,我记得在之前写过一篇《手把手教你,使用JWT实现单点登录》的文章,里面介绍的比较详细,有兴趣的朋友可以看一下,没了解的也没关系,我们在此简单的介绍一下 token 方案。
  从上图,我们可以很清晰的看到,token 方案的实现主要有以下几个步骤:  1、用户登录成功之后,服务端会给用户生成一个唯一有效的凭证,这个有效值被称为token  2、当用户每次请求其他的业务接口时,需要在请求头部带上token  3、服务端接受到客户端业务接口请求时,会验证token的合法性,如果不合法会提示给客户端;如果合法,才会进入业务处理流程。
  在实际使用过程中,当用户登录成功之后,生成的token存放在redis中时是有时效的,一般设置为2个小时,过了2个小时之后会自动失效,这个时候我们就需要重新登录,然后再次获取有效token。
  token方案,是目前业务类型的项目当中使用最广的方案,而且实用性非常高,可以很有效的防止黑客们进行抓包、爬取数据。
  但是 token 方案也有一些缺点!最明显的就是与第三方公司进行接口对接的时候,当你的接口请求量非常大,这个时候 token 突然失效了,会有大量的接口请求失败。
  这个我深有体会,我记得在很早的时候,跟一家中、大型互联网公司进行联调的时候,他们提供给我的接口对接方案就是token方案,当时我司的流量高峰期时候,请求他们的接口大量报错,原因就是因为token失效了,当token失效时,我们会调用他们刷新token接口,刷新完成之后,在token失效与重新刷新token这个时间间隔期间,就会出现大量的请求失败的日志,因此在实际API对接过程中,我不推荐大家采用 token方案。  2.2、接口签名
  接口签名,顾名思义,就是通过一些签名规则对参数进行签名,然后把签名的信息放入请求头部,服务端收到客户端请求之后,同样的只需要按照已定的规则生产对应的签名串与客户端的签名信息进行对比,如果一致,就进入业务处理流程;如果不通过,就提示签名验证失败。
  在接口签名方案中,主要有四个核心参数:  1、appid表示应用ID,其中与之匹配的还有appsecret,表示应用密钥,用于数据的签名加密,不同的对接项目分配不同的appid和appsecret,保证数据安全  2、timestamp 表示时间戳,当请求的时间戳与服务器中的时间戳,差值在5分钟之内,属于有效请求,不在此范围内,属于无效请求  3、nonce 表示临时流水号,用于防止重复提交验证  4、signature 表示签名字段,用于判断接口请求是否有效。
  其中签名的生成规则,分两个步骤:  第一步:对请求参数进行一次md5加密签名  //步骤一 String 参数1 = 请求方式 + 请求URL相对地址 + 请求Body字符串; String 参数1加密结果= md5(参数1) 第二步:对第一步签名结果,再进行一次md5加密签名  //步骤二 String 参数2 = appsecret + timestamp + nonce + 参数1加密结果; String 参数2加密结果= md5(参数2)
  参数2加密结果,就是我们要的最终签名串。
  接口签名方案,尤其是在接口请求量很大的情况下,依然很稳定。
  换句话说,你可以将接口签名看作成对token方案的一种补充。
  但是如果想把接口签名方案,推广到前后端对接,答案是:不适合。
  因为签名计算非常复杂,其次,就是容易泄漏appsecret!
  说了这么多,下面我们就一起来用程序实践一下吧!  二、程序实践2.1、token方案
  就像上文所说,token方案重点在于,当用户登录成功之后,我们只需要生成好对应的token,然后将其返回给前端,在下次请求业务接口的时候,需要把token带上。
  具体的实践,也可以分两种:  第一种:采用uuid生成token,然后将token存放在redis中,同时设置有效期2哥小时  第二种:采用JWT工具来生成token,这种token是可以跨平台的,天然支持分布式,其实本质也是采用时间戳+密钥,来生成一个token。
  下面,我们介绍的是第二种实现方式。
  首先,编写一个jwt 工具。  public class JwtTokenUtil {     //定义token返回头部     public static final String AUTH_HEADER_KEY = "Authorization";     //token前缀     public static final String TOKEN_PREFIX = "Bearer ";     //签名密钥     public static final String KEY = "q3t6w9z$C&F)J@NcQfTjWnZr4u7x";     //有效期默认为 2hour     public static final Long EXPIRATION_TIME = 1000L*60*60*2;     /**      * 创建TOKEN      * @param content      * @return      */     public static String createToken(String content){         return TOKEN_PREFIX + JWT.create()                 .withSubject(content)                 .withExpiresAt(new Date(System.currentTimeMillis() + EXPIRATION_TIME))                 .sign(Algorithm.HMAC512(KEY));     }     /**      * 验证token      * @param token      */     public static String verifyToken(String token) throws Exception {         try {             return JWT.require(Algorithm.HMAC512(KEY))                     .build()                     .verify(token.replace(TOKEN_PREFIX, ""))                     .getSubject();         } catch (TokenExpiredException e){             throw new Exception("token已失效,请重新登录",e);         } catch (JWTVerificationException e) {             throw new Exception("token验证失败!",e);         }     } }
  接着,我们在登录的时候,生成一个token,然后返回给客户端。  @RequestMapping(value = "/login", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8"}) public UserVo login(@RequestBody UserDto userDto, HttpServletResponse response){     //...参数合法性验证     //从数据库获取用户信息     User dbUser = userService.selectByUserNo(userDto.getUserNo);     //....用户、密码验证     //创建token,并将token放在响应头     UserToken userToken = new UserToken();     BeanUtils.copyProperties(dbUser,userToken);     String token = JwtTokenUtil.createToken(JSONObject.toJSONString(userToken));     response.setHeader(JwtTokenUtil.AUTH_HEADER_KEY, token);     //定义返回结果     UserVo result = new UserVo();     BeanUtils.copyProperties(dbUser,result);     return result; }
  最后,编写一个统一拦截器,用于验证客户端传入的token是否有效。  @Slf4j public class AuthenticationInterceptor implements HandlerInterceptor {     @Override     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {         // 从http请求头中取出token         final String token = request.getHeader(JwtTokenUtil.AUTH_HEADER_KEY);         //如果不是映射到方法,直接通过         if(!(handler instanceof HandlerMethod)){             return true;         }         //如果是方法探测,直接通过         if (HttpMethod.OPTIONS.equals(request.getMethod())) {             response.setStatus(HttpServletResponse.SC_OK);             return true;         }         //如果方法有JwtIgnore注解,直接通过         HandlerMethod handlerMethod = (HandlerMethod) handler;         Method method=handlerMethod.getMethod();         if (method.isAnnotationPresent(JwtIgnore.class)) {             JwtIgnore jwtIgnore = method.getAnnotation(JwtIgnore.class);             if(jwtIgnore.value()){                 return true;             }         }         LocalAssert.isStringEmpty(token, "token为空,鉴权失败!");         //验证,并获取token内部信息         String userToken = JwtTokenUtil.verifyToken(token);         //将token放入本地缓存         WebContextUtil.setUserToken(userToken);         return true;     }     @Override     public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {         //方法结束后,移除缓存的token         WebContextUtil.removeUserToken();     } }
  在生成token的时候,我们可以将一些基本的用户信息,例如用户ID、用户姓名,存入token中,这样当token鉴权通过之后,我们只需要通过解析里面的信息,即可获取对应的用户ID,可以省下去数据库查询一些基本信息的操作。
  同时,使用的过程中,尽量不要存放敏感信息,因为很容易被黑客解析!  2.2、接口签名
  同样的思路,站在服务端验证的角度,我们可以先编写一个签名拦截器,验证客户端传入的参数是否合法,只要有一项不合法,就提示错误。
  具体代码实践如下:  public class SignInterceptor implements HandlerInterceptor {      @Autowired     private AppSecretService appSecretService;      @Autowired     private RedisUtil redisUtil;      @Override     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)             throws Exception {         //appId验证         final String appId = request.getHeader("appid");         if(StringUtils.isEmpty(appId)){             throw new CommonException("appid不能为空");         }         String appSecret = appSecretService.getAppSecretByAppId(appId);         if(StringUtils.isEmpty(appSecret)){             throw new CommonException("appid不合法");         }         //时间戳验证         final String timestamp = request.getHeader("timestamp");         if(StringUtils.isEmpty(timestamp)){             throw new CommonException("timestamp不能为空");         }         //大于5分钟,非法请求         long diff = System.currentTimeMillis() - Long.parseLong(timestamp);         if(Math.abs(diff) > 1000 * 60 * 5){             throw new CommonException("timestamp已过期");         }         //临时流水号,防止重复提交         final String nonce = request.getHeader("nonce");         if(StringUtils.isEmpty(nonce)){             throw new CommonException("nonce不能为空");         }         //验证签名         final String signature = request.getHeader("signature");         if(StringUtils.isEmpty(nonce)){             throw new CommonException("signature不能为空");         }         final String method = request.getMethod();         final String url = request.getRequestURI();         final String body = StreamUtils.copyToString(request.getInputStream(), Charset.forName("UTF-8"));         String signResult = SignUtil.getSignature(method, url, body, timestamp, nonce, appSecret);         if(!signature.equals(signResult)){             throw new CommonException("签名验证失败");         }         //检查是否重复请求         String key = appId + "_" + timestamp + "_" + nonce;         if(redisUtil.exist(key)){             throw new CommonException("当前请求正在处理,请不要重复提交");         }         //设置5分钟         redisUtil.save(key, signResult, 5*60);         request.setAttribute("reidsKey",key);     }      @Override     public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)             throws Exception {         //请求处理完毕之后,移除缓存         String value = request.getAttribute("reidsKey");         if(!StringUtils.isEmpty(value)){             redisUtil.remove(value);         }     }  }
  签名工具类 SignUtil : public class SignUtil {      /**      * 签名计算      * @param method      * @param url      * @param body      * @param timestamp      * @param nonce      * @param appSecret      * @return      */     public static String getSignature(String method, String url, String body, String timestamp, String nonce, String appSecret){         //第一层签名         String requestStr1 = method + url + body + appSecret;         String signResult1 = DigestUtils.md5Hex(requestStr1);         //第二层签名         String requestStr2 = appSecret + timestamp + nonce + signResult1;         String signResult2 = DigestUtils.md5Hex(requestStr2);         return signResult2;     } }
  签名计算,可以换成 hamc 方式进行计算,思路大致一样。 三、小结
  上面介绍的token和接口签名方案,对外都可以对提供的接口起到保护作用,防止别人篡改请求,或者模拟请求。
  但是缺少对数据自身的安全保护,即请求的参数和返回的数据都是有可能被别人拦截获取的,而这些数据又是明文的,所以只要被拦截,就能获得相应的业务数据。
  对于这种情况,推荐大家对请求参数和返回参数进行加密处理,例如RSA、AES等加密工具。
  同时,在生产环境,采用 https 方式进行传输,可以起到很好的安全保护作用!

硬盘数据如何安全复制?有了它,轻松搞定1。前言为什么,买什么样的需求(SATA硬盘一对多克隆,5盘位同时读写)2。包装,外观,材质(塑料金属底座),大小(大小适中,重量轻,方便移动),视觉(黑色),触感(周围磨砂塑料,批量数据复制,有了这个神器,轻松搞定前言为什么买这个产品,出于什么样的需求(硬盘对拷,一拖三,硬盘读写)产品包装,外观,材质,大小,视觉,触感,配件安装安装拆卸(即插即用)功能功能特性突出卖点(一拖三,安装方便,脱机创意街拍,轻松上阵,飞宇G6MAX稳定器,不止是稳CiaoBello,我是老房。年底了,年假不用就废啦,抓紧和土豪老板夫妇俩来个说走就走的游玩,一游就游去了泉州。土豪朋友给我的理由也很土豪,我想去吃正宗的佛跳墙。为了旅拍,他横竖纠手机壳也可硬核给华为Mate30Pro手机穿上Defense外衣CiaoBella,我是老房。曾经有一份真挚的感情摆在我的面前我没有珍惜,等我失去的时候才追悔莫及,人间最痛苦的事莫过于此老房的剧情是主力机iPhone叉一直是裸奔,去年被家里的皮一二三步走,轻松玩转小米智能家居CiaoBella,我是老房。得益于小米生态链的强大布局,以及米家APP丰富的功能和拓展性,目前小米系的智能家居也成为了越来越多家庭的选择。智能家居到底智能在哪里?使用起来方便不方优雅绽放健康生活Jeep女士智能小红表开箱CiaoBella,我是老房。其实以前也想给老婆买块智能手表,但市面上的智能手表大多数造型还都较为硬朗,虽然功能丰富但也另一方面使得手表的体积偏大且略有分量,并不是非常适合对手表智有线转无线高性价比直选耳机OPPOEncoW31上手CiaoBella,我是老房。近年来,随着越来越多的手机取消3。5毫米耳机接口,真无线(TWS)蓝牙耳机得到了飞速的发展,市场竞争也愈发的激烈。充分的竞争使得技术得到飞速发展,产品要人造凉爽更要天然舒适米家直流变频落地扇1XCiaoTutti,我是老房。当回家锁上家门的时候还是冬天,还没过春节。当能出门的时候,油菜花都开了!一样的,等到荷花开也是很快的事了。减缓熵增,减少能耗,利国利民。降低感染的风险绘本网课做手工,健康方便耐折腾的儿童学习桌面分享CiaoBella,我是老房。家里小朋友已经4岁,是时候给他准备一套属于自己的桌面空间了。和咱们大人不一样,小朋友的儿童书桌要以结实耐用为主,桌面布局要以健康抗造为重,要关注功能性健康升级玩法多,办公娱乐两不误,多功能双屏办公桌面及灯光布局CiaoBello,我是老房。在公司的时候其实老房一直在使用双显示器办公,不过主要是一台笔记本电脑外加一台15寸的老款显示器。虽然配置稍微差了点,但多一个屏幕的便利性以及对工作效率从意式浓缩到卡布奇诺,德龙全自动咖啡机,一键满足你的咖啡需求CiaoBella,我是老房。这句开篇词其实是一句意大利语,意为你好美女。是的,当年在意大利的留学经历,也对老房的咖啡饮食观产生了深远的影响。是的,在意大利这个发明摩卡壶的国家,当
你们都知道一万多元的手机有哪些型号吗?过万元的手机那一定是最有品牌影响力的了,那全世界都有哪些品牌和哪些型号入选呢?估计有很多人都猜出来了是哪3个品牌的手机了,不错,就是这3个!第一个苹果13Pro性能方面苹果13Pr如果我们真有在工业领域赶超美西方的那一天,唯一的赶超方式是啥?中国,需要更多的曹德旺,任正非中华民族骨风的企业,走向世界,屹立不败。需要高端科技含金量的中国力量,走向世界。如果我们真的有在工业领域赶超美西方的那一天,唯一的赶超方式是啥?这个问联想要是真出事,最终会是谁来买单?一个国有技术型企业被L传志hh的成了一个组装厂,肯定里有不可告人的勾当!彻底查,采访采访柳们。垂杨柳买单,别想着让国家和老百姓买单!老百姓都买单N多次了首先必须澄清联想有没有问题,围炉夜话,2nm芯片研发成功,看西方的产业布局2nm由IBM研发成功,看到西方发达国家的产业布局!围炉夜话百度与谷歌两家公司成立时间差不多,开始的研发方向一致搜索引擎!二十年后这两家公司发展格局差距巨大,谷歌公司成为了世界顶级电商不死中国不富,真是这样吗?电商淘宝不死,实体不兴,中国不富。这是一位大佬说的话,你相信吗?其实淘宝也好,微信也好,短视频抖音也好。都是互联网技术自然生长的商业形式。是社会发展的必然历程。为什么会产生电商以及风光大基地二期加码,新能源加速期来临近日,国家能源局再次下发关于组织拟纳入国家第二批以沙漠,戈壁荒漠地区为重点的大型风电光伏基地项目的通知,要求各省在12月15提前上报第二批新能源大基地的名单,文件提出1)按照国家统渗透测试报告自动生成工具公众号白帽子左一领取配套练手靶场安全全套课程及工具背景在安服仔的日子里,发现其他人输出的渗透测试报告结果不规范,主要在报告质量内容字体及修复方案中存在诸多问题,而且大部分安服仔需要带货主播成正式工种李佳琦和薇娅们转正了近日,人社部中央网信办国家广播电视总局共同发布了互联网营销师国家职业技能标准。这是互联网营销师成为新职业后的又一个重要时刻。2020年,人社部等部门发布了互联网营销师新职业信息,其Excel文件操作工具类直接上代码packagecom。sccin。spboot。utilsimportcom。sccin。spboot。pojo。exception。RunMsgExceptionimp付费流量是可拉升自然搜索流量的,如何操作?为什么付费推广也不能带动自然流量?我们都知道直通车肯定是可以拉动自然搜索流量的提升的,但为什么有些人做了没效果呢?其实现在的自然搜索流量也都是个性化的,这个个性化就是产品的标签要符C深入理解指针变量做函数参数的己值与他值及对数组的操作1函数体内指针变量的己值与他值if1includestdio。hincludestdlib。hvoidfoo(intpa,intan)paanpa解引用修改其指向的值,传址的解引用