日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

如何設計一個安全的對外接口,老司機總結了這幾點...

來源: 責編: 時間:2024-01-15 17:12:14 222觀看
導讀博主之前做過恒豐銀行代收付系統(相當于支付接口),包括現在的oltpapi交易接口和虛擬業務的對外提供數據接口。總之,當你做了很多項目寫了很多代碼的時候,就需要回過頭來,多總結總結,這樣你會看到更多之前寫代碼的時候看不到

博主之前做過恒豐銀行代收付系統(相當于支付接口),包括現在的oltpapi交易接口和虛擬業務的對外提供數據接口。總之,當你做了很多項目寫了很多代碼的時候,就需要回過頭來,多總結總結,這樣你會看到更多之前寫代碼的時候看不到的東西,也能更明白為什么要這樣做。e0c28資訊網——每日最新資訊28at.com

e0c28資訊網——每日最新資訊28at.com

做接口需要考慮的問題

1.什么是接口

接口無非就是客戶端請求你的接口地址,并傳入一堆該接口定義好的參數,通過接口自身的邏輯處理,返回接口約定好的數據以及相應的數據格式。e0c28資訊網——每日最新資訊28at.com

2.接口怎么開發

接口由于本身的性質,由于和合作方對接數據,所以有以下幾點需要在開發的時候注意:e0c28資訊網——每日最新資訊28at.com

(1) 定義接口入參:寫好接口文檔e0c28資訊網——每日最新資訊28at.com

(2) 定義接口返回數據類型:一般都需要封裝成一定格式,確定返回json還是xml報文等e0c28資訊網——每日最新資訊28at.com

e0c28資訊網——每日最新資訊28at.com

見如下返回數據定義格式:e0c28資訊網——每日最新資訊28at.com

package com.caiex.vb.model; import java.io.Serializable;import javax.xml.bind.annotation.XmlAccessType;import javax.xml.bind.annotation.XmlAccessorType;import javax.xml.bind.annotation.XmlType; @XmlAccessorType(XmlAccessType.FIELD)@XmlType(name = "Result", propOrder = { "resultCode", "resultMsg" })public class Result implements Serializable { private static final long serialVersionUID = 10L; protected int resultCode; protected String resultMsg;  public int getResultCode() {  return this.resultCode; }  public void setResultCode(int value) {  this.resultCode = value; }  public String getResultMsg() {  return this.resultMsg; }  public void setResultMsg(String value) {  this.resultMsg = value; }}
package com.caiex.vb.model; import java.io.Serializable; public class Response implements Serializable {  private static final long serialVersionUID = 2360867989280235575L;  private Result result;  private Object data;  public Result getResult() {  if (this.result == null) {   this.result = new Result();  }  return result; }  public void setResult(Result result) {  this.result = result; }  public Object getData() {  return data; }  public void setData(Object data) {  this.data = data; } }

(3) 確定訪問接口的方式,get or post等等,可以根據restful接口定義規則RESTful API:RESTful APIe0c28資訊網——每日最新資訊28at.com

(4) 定義一套全局統一并通用的返回碼,以幫助排查問題:e0c28資訊網——每日最新資訊28at.com

 public static int NO_AGENT_RATE = 1119;  //未找到兌換率  public static int SCHEME_COMMIT_FAIL = 4000;  //方案提交失敗  public static int SCHEME_CONFIRMATION = 4001;  //方案確認中  public static int SCHEME_NOT_EXIST = 4002;  //方案不存在  public static int SCHEME_CANCEL= 4005;  //方案不存在  //。。。。

(5) 統一的異常處理:應該每個系統都需要一套統一的異常處理e0c28資訊網——每日最新資訊28at.com

package com.caiex.vb.interceptor; import javax.servlet.http.HttpServletRequest; import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.web.bind.annotation.ControllerAdvice;import org.springframework.web.bind.annotation.ExceptionHandler;import org.springframework.web.bind.annotation.ResponseBody; import com.caiex.vb.model.Response; @ControllerAdvice@ResponseBodypublic class GlobalExceptionHandler {  private  Logger  logger = LoggerFactory.getLogger(this.getClass());      /**     * 所有異常報錯     * @param request     * @param exception     * @return     * @throws Exception     */    @ExceptionHandler(value=Exception.class)      public Response allExceptionHandler(HttpServletRequest request,              Exception exception) throws Exception      {       logger.error("攔截到異常:", exception);        Response response = new Response();        response.setData(null);        response.getResult().setResultCode(9999);        response.getResult().setResultMsg("系統繁忙");        return response;      }   }

(6) 攔截器鏈設置:合作方訪問接口的時候,會根據你接口定義好的傳參訪問你的接口服務器,但是會存在接口參數類型錯誤或者格式不對,必傳參數沒傳的問題,甚至一些惡意請求,都可以通過攔截器鏈進行前期攔截,避免造成接口服務的壓力。e0c28資訊網——每日最新資訊28at.com

還有很重要的一點,加簽驗簽也可以在攔截器設置。繼承WebMvcConfigurerAdapter實現springboot的攔截器鏈。實現HandlerInterceptor方法編寫業務攔截器。e0c28資訊網——每日最新資訊28at.com

package com.caiex.vb.interceptor;  import javax.annotation.Resource;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils;import org.apache.logging.log4j.LogManager;import org.apache.logging.log4j.Logger;import org.springframework.stereotype.Component;import org.springframework.web.servlet.HandlerInterceptor;import org.springframework.web.servlet.ModelAndView; import com.alibaba.fastjson.JSON;import com.caiex.redis.service.api.RedisApi;import com.caiex.vb.model.Response;import com.caiex.vb.utils.CaiexCheckUtils; @Componentpublic class SignInterceptor extends BaseValidator implements HandlerInterceptor{  private Logger logger = LogManager.getLogger(this.getClass());  @Resource private RedisApi redisApi;   public void afterCompletion(HttpServletRequest arg0,   HttpServletResponse arg1, Object arg2, Exception arg3)   throws Exception {  // TODO Auto-generated method stub   }  public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1,   Object arg2, ModelAndView arg3) throws Exception {  // TODO Auto-generated method stub   }  public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1,   Object arg2) throws Exception {  if(isTestIpAddr(arg0)){   return true;  }  String securityKey = redisApi.hGet("securityKey", arg0.getParameter("agentid"));  if(StringUtils.isEmpty(securityKey)){   Response response = new Response();   response.setData(null);   response.getResult().setResultCode(8001);   response.getResult().setResultMsg("缺少私鑰, 渠道號:" + arg0.getParameter("agentid"));   logger.error("缺少私鑰, 渠道號:" + arg0.getParameter("agentid"));   InterceptorResp.printJson(arg1, response);   return false;  }    if(StringUtils.isEmpty(arg0.getParameter("sign")) || !arg0.getParameter("sign").equals(CaiexCheckUtils.getSign(arg0.getParameterMap(), securityKey))){   Response response = new Response();   response.setData(null);   response.getResult().setResultCode(3203);   response.getResult().setResultMsg("參數簽名認證失敗");   logger.error("參數簽名認證失敗:" + JSON.toJSONString(arg0.getParameterMap()) + " securityKey = " + securityKey);   InterceptorResp.printJson(arg1, response);   return false;  }else{   return true;  }   } }
package com.caiex.oltp.config; import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.EnableWebMvc;import org.springframework.web.servlet.config.annotation.InterceptorRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import com.caiex.oltp.interceptor.APILimitRateValidator;import com.caiex.oltp.interceptor.CommonValidator;import com.caiex.oltp.interceptor.DDSAuthValidator;import com.caiex.oltp.interceptor.QueryPriceParamsValidator;import com.caiex.oltp.interceptor.TradeParamsValidator;  @EnableWebMvc@Configuration@ComponentScanpublic class WebAppConfigurer extends WebMvcConfigurerAdapter {    @Bean   CommonValidator commonInterceptor() {         return new CommonValidator();     }    @Bean   DDSAuthValidator ddsAuthInterceptor() {         return new DDSAuthValidator();     }    @Bean   QueryPriceParamsValidator queryPriceParamsInterceptor() {         return new QueryPriceParamsValidator();     }    @Bean   TradeParamsValidator tradeParamsInterceptor() {         return new TradeParamsValidator();     }     @Bean   APILimitRateValidator aPILimitRateInterceptor() {         return new APILimitRateValidator();     }       @Override     public void addInterceptors(InterceptorRegistry registry) {            //訪問速率限制      registry.addInterceptor(aPILimitRateInterceptor())      .addPathPatterns("/*/*");      //.addPathPatterns("/price/getPriceParam");       //參數簽名認證         registry.addInterceptor(ddsAuthInterceptor())         .addPathPatterns("/tradeState/*")         .addPathPatterns("/recycle/*")         .addPathPatterns("/matchInfo/*")         .addPathPatterns("/price/tradeTicketParam");                  //公共參數檢查         registry.addInterceptor(commonInterceptor())         .addPathPatterns("/price/tradeTicketParam")         .addPathPatterns("/tradeState/*")         .addPathPatterns("/recycle/*");                  //詢價參數校驗         registry.addInterceptor(queryPriceParamsInterceptor())         .addPathPatterns("/price/getPriceParam");                  //交易參數檢查         registry.addInterceptor(tradeParamsInterceptor())         .addPathPatterns("/price/tradeTicketParam");                  super.addInterceptors(registry);     }}

(7) token令牌和sign數字簽名實現數據保密性。e0c28資訊網——每日最新資訊28at.com

3.創建令牌(Token)

為保證請求的合法性,我們提供第三方創建令牌接口,某些接口需要通過token驗證消息的合法性,以免遭受非法攻擊。e0c28資訊網——每日最新資訊28at.com

token過期時間目前暫時定為1天,由于考慮到合作方往往是分布式環境,多臺機器都有可能申請token,為了降低合作方保證token一致性的難度,調用接口創建token成功以后一分鐘以內,再次請求token返回的數據是一樣的。e0c28資訊網——每日最新資訊28at.com

4.獲取私鑰

獲取用于數字簽名的私鑰,第三方獲取的私鑰需妥善保存,并定期更新,私鑰只參與數字簽名,不作為參數傳輸。e0c28資訊網——每日最新資訊28at.com

  • 數字簽名方式:參數簽名;簽名方式:所有值不為null的參數(不包括本參數)均參與數字簽名,按照“參數名+參數值+私鑰”的格式得到一個字符串,再將這個字符串MD5一次就是這個參數的值。(示例:h15adc39y9ba59abbe56e057e60f883g),所以需要先獲取私鑰。
  • 驗簽方式:將用戶的所有非null參數放入定義好排序規則的TreeSet中進行排序,再用StringBuilder按照按照“參數名+參數值+私鑰”的格式得到一個字符串(私鑰從redis拿),再將這個字符串MD5一次就是這個參數的值。將這個值與用戶傳來的sign簽名對比,相同則通過,否則不通過。
private String createToken(){  String utk = "Msk!D*"+System.currentTimeMillis()+"UBR&FLP";  logger.info("create token   --- "+Md5Util.md5(utk));  return Md5Util.md5(utk); }

(8) 接口限流e0c28資訊網——每日最新資訊28at.com

有時候服務器壓力真的太大,以防交易接口被擠死,就可以對一些其他不影響主要業務功能并且計算量大的接口做限流處理。RateLimit--使用guava來做接口限流,當接口超過指定的流量時,就不處理該接口的請求。詳細可看RateLimit。也可參考其他限流框架。e0c28資訊網——每日最新資訊28at.com

(9) 協議加密,http升級成https;e0c28資訊網——每日最新資訊28at.com

為什么要升級呢,為了保證數據的安全性。當使用https訪問時,數據從客戶端到服務斷,服務端到客戶端都加密,即使黑客抓包也看不到傳輸內容。當然還有其他好處,這里不多講。但這也是開發接口項目需要注意的一個問題。e0c28資訊網——每日最新資訊28at.com

如何提高接口的高并發和高可用

接口開發好了,接下來就討論接口的可用性問題。首先我們要將高并發和高可用區分一下,畢竟高可用是在可用的情況,只是很慢或者效率不高。其實也可以歸為一類問題,但是不重要啦,重要的是怎么提高你寫的接口的訪問速度和性能。e0c28資訊網——每日最新資訊28at.com

1.接口的高并發解決方案(其實沒有唯一答案,業界針對不同業務也有很多不同的方法)

當訪問一個接口獲取數據時,發現返回很慢,或者總是超時,如果排除網絡的原因,那就是接口服務器壓力太大,處理不過來了。在世界杯期間,我們查看后臺日志總是connection by reset和borker pipe和一些超時問題。e0c28資訊網——每日最新資訊28at.com

這時候,你可能遇到了高并發和高可用問題。但是,不管遇到什么問題,都不能臆斷和亂改,你得需要找到慢的原因,才能對癥下藥,亂改可能會導致其他問題的出現。首先,解決高并發問題的三個方向是負載均衡,緩存和集群。e0c28資訊網——每日最新資訊28at.com

(1) 負載均衡e0c28資訊網——每日最新資訊28at.com

我們使用的是阿里云服務器的負載均衡,后臺分布式服務管理,我們運維小哥哥搭建了一套k8s,可以自由在k8s上擴展服務節點,各個服務結點也能隨內存的使用自動漂移,不用多說,k8s真的很厲害,感興趣的同學可以詳細去學。那么問題來了,阿里云的負載均衡怎么對應到k8s的負載均衡呢?e0c28資訊網——每日最新資訊28at.com

這個涉及到了k8s的service暴露的一些特點,簡單說就是k8s把所有集群的服務都通過指定的內部負載均衡,在指定的服務器上暴露,然后我們又把這幾個服務器接在阿里云負載均衡下,這個涉及的細節和配置很多。當然,除nginx外,還有其他負載均衡解決方案,軟件硬件都有,硬件如f5等。e0c28資訊網——每日最新資訊28at.com

阿里云的nginx負載均衡,我們使用的是加權輪詢策略,其實輪詢是最低效的方式;e0c28資訊網——每日最新資訊28at.com

這就是最基本的負載均衡實例,但這不足以滿足實際需求;目前Nginx服務器的upstream模塊支持6種方式的分配:e0c28資訊網——每日最新資訊28at.com

負載均衡策略:e0c28資訊網——每日最新資訊28at.com

輪詢
e0c28資訊網——每日最新資訊28at.com

默認方式
e0c28資訊網——每日最新資訊28at.com

weighte0c28資訊網——每日最新資訊28at.com

權重方式e0c28資訊網——每日最新資訊28at.com

ip_hashe0c28資訊網——每日最新資訊28at.com

依據ip分配方式e0c28資訊網——每日最新資訊28at.com

least_conne0c28資訊網——每日最新資訊28at.com

最少連接方式e0c28資訊網——每日最新資訊28at.com

fair(第三方)e0c28資訊網——每日最新資訊28at.com

響應時間方式e0c28資訊網——每日最新資訊28at.com

url_hash(第三方)e0c28資訊網——每日最新資訊28at.com

依據URL分配方式e0c28資訊網——每日最新資訊28at.com

(2) 集群e0c28資訊網——每日最新資訊28at.com

首先,通過排查問題,發現是oltpapi接口服務處理請求很慢,大量請求過來,總是超時和中斷連接,這時候,我們想著最簡單的方法就是加機器,給oltp接口服務多加幾臺機器。e0c28資訊網——每日最新資訊28at.com

嗯,一切都很完美,如預期進行,但是加到一定數量,你發現,怎么不起效果,異步響應還是很慢,或者更直觀的說,消息隊列出現了嚴重的消息堆積。這時候,你發現出現了新的問題或者瓶頸,這個問題已經不是說加oltp服務器能解決了,那么,就需要去重新定位問題。發現是消息堆積,消息堆積就是生產者過快,導致消費者消費不過來,這時候,你就需要增加消費者的消費數量。給風控系統多加幾臺機器,讓消費者和生產者達到一定平衡。e0c28資訊網——每日最新資訊28at.com

這里有個誤區,你可能以為是rocketmq的broker數量過少,增加broker數量,其實當消費者和生產者保持一樣的速度時,消息肯定不對堆積,按照原始的broker數量就足夠。但是增加broker也會使得消息得到盡快的處理,提升一定效率。e0c28資訊網——每日最新資訊28at.com

(3) 緩存e0c28資訊網——每日最新資訊28at.com

當加機器不能解決問題時,或者說沒那么多服務器可使用時,那么就要重代碼層面解決高并發問題。Redis 是一個高性能的key-value數據庫,當獲取數據從數據庫拿很慢時,就可以存儲到redis,從redis取值。e0c28資訊網——每日最新資訊28at.com

  • 用ConcurrentHashMap緩存對象,并設置過期時間
  • redis緩存數據,結合spring定時任務定時獲取不會經常改動的key
  • 提高使用redis的效率:比如使用mGet一次獲取多個key
  • ....等

2.接口高可用問題

高可用問題應該上升到整個服務的架構問題上,就是說在搭建整體系統是就應該考慮到。高可用問題是以單點故障,訪問速度慢的問題為主導。見  服務高可用e0c28資訊網——每日最新資訊28at.com

  • redis主從分布式(redis的單點故障和訪問速度的提高和主從備份)
  • 分布式dubbo服務的zookeeper主從集群
  • strom的主從集群
  • ...等

總結

下面對接口開發服務做一些總結:e0c28資訊網——每日最新資訊28at.com

1.是拉還是推

當接口作為數據源時,還要考慮數據是讓合作方主動過來拉還是數據有變化就推送呢,當然是推的效果更好,但是如何有效的推數據,不推重復數據等都是需要根據實際業務考慮的問題。e0c28資訊網——每日最新資訊28at.com

2.多臺分布式服務器上,怎么保證交易的冪等和訂單的唯一性

當接口服務和合作方都處于分布式情況下,就很容易出現一個訂單號申請多次交易請求,但是根據冪等性,一張彩票只能交易一次,并且每次不管何時請求,結果都應該一樣不會改變。這種情況下,我們怎么保證唯一性呢,我們需要把該訂單和訂單狀態存redis,每次請求時去看是否訂單已存在。但可能這次交易不成功,下次這張票還可以繼續交易,可以生成新的訂單號啊。e0c28資訊網——每日最新資訊28at.com

redis的setNX是一個很好的解決方案,意思是當存在該key時,返回false,當沒有時,該key和value插入成功。用作檢查訂單是否正在提交,如果是,則阻塞本次請求,避免重復提交 ,可以設置過期時間3s。提交之前鎖定訂單,防止重復提交。e0c28資訊網——每日最新資訊28at.com

3.處理時間超過10s,自動返回該訂單交易失敗

總之,博主發現,在高并發場景下,導致服務崩潰的原因還是redis和數據庫,可能是redis讀寫太慢,或者數據庫的一些sql使用不當,或者沒建索引導致讀寫很慢。e0c28資訊網——每日最新資訊28at.com

總之,這是一條很漫長的路,我們都需要慢慢積累經驗和學習前人更優秀的解決辦法。e0c28資訊網——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-61910-0.html如何設計一個安全的對外接口,老司機總結了這幾點...

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 未來世界的12個軟件開發預測

下一篇: 谷歌裁員千人,攪動硅谷!終身編程終結,我們何以茍活?

標簽:
  • 熱門焦點
  • MIX Fold3包裝盒泄露 新機本月登場

    小米的全新折疊屏旗艦MIX Fold3將于本月發布,近日該機的真機包裝盒在網上泄露。從圖上來看,新的MIX Fold3包裝盒在外觀設計方面延續了之前的方案,變化不大,這也是目前小米旗艦
  • 小米官宣:2023年上半年出貨量中國第一!

    今日早間,小米電視官方微博帶來消息,稱2023年小米電視上半年出貨量達到了中國第一,同時還表示小米電視的巨屏風暴即將開始。“公布一個好消息2023年#小米電視上半年出貨量中國
  • 帥氣純真少年!日本最帥初中生選美冠軍出爐

    日本第一帥哥初一生選美大賽冠軍現已正式出爐,冠軍是來自千葉縣的宗田悠良。日本一直熱衷于各種選美大賽,從“最美JK”起到“最美女星&r
  • 把LangChain跑起來的三個方法

    使用LangChain開發LLM應用時,需要機器進行GLM部署,好多同學第一步就被勸退了,那么如何繞過這個步驟先學習LLM模型的應用,對Langchain進行快速上手?本片講解3個把LangChain跑起來
  • 一個注解實現接口冪等,這樣才優雅!

    場景碼猿慢病云管理系統中其實高并發的場景不是很多,沒有必要每個接口都去考慮并發高的場景,比如添加住院患者的這個接口,具體的業務代碼就不貼了,業務偽代碼如下:圖片上述代碼有
  • WebRTC.Net庫開發進階,教你實現屏幕共享和多路復用!

    WebRTC.Net庫:讓你的應用更親民友好,實現視頻通話無痛接入! 除了基本用法外,還有一些進階用法可以更好地利用該庫。自定義 STUN/TURN 服務器配置WebRTC.Net 默認使用 Google 的
  • Python異步IO編程的進程/線程通信實現

    這篇文章再講3種方式,同時講4中進程間通信的方式一、 Python 中線程間通信的實現方式共享變量共享變量是多個線程可以共同訪問的變量。在Python中,可以使用threading模塊中的L
  • 消費結構調整丨巨頭低價博弈,拼多多還卷得動嗎?

    來源:征探財經作者:陳香羽隨著流量紅利的退潮,電商的存量博弈越來越明顯。曾經主攻中高端與品質的淘寶天貓、京東重拾“低價”口號。而過去與他們錯位競爭的拼多多,靠
  • 超級標準版旗艦!iQOO 11S全球首發iQOO超算獨顯芯片

    上半年已接近尾聲,截至目前各大品牌旗下的頂級旗艦都已悉數亮相,而下半年即將推出的頂級旗艦已經成為了數碼圈爆料的主流,其中就包括全新的iQOO 11S系
Top 主站蜘蛛池模板: 横山县| 宜丰县| 江西省| 赤城县| 和平区| 商洛市| 商都县| 项城市| 蛟河市| 广宁县| 微山县| 河西区| 龙江县| 湘潭市| 永和县| 辽阳县| 安顺市| 南昌市| 丰都县| 肇州县| 赣州市| 古交市| 沙田区| 林西县| 元氏县| 保康县| 上林县| 虞城县| 富宁县| 双城市| 仙桃市| 邹平县| 翁源县| 财经| 金堂县| 泗阳县| 金昌市| 曲沃县| 肇东市| 鹤岗市| 宜黄县|