<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          接口的安全設(shè)計(jì)要素:ticket,簽名,時(shí)間戳

          共 6404字,需瀏覽 13分鐘

           ·

          2021-02-02 06:59

          不點(diǎn)藍(lán)字,我們哪來故事?

          每天 11 點(diǎn)更新文章,餓了點(diǎn)外賣,點(diǎn)擊 ??《無(wú)門檻外賣優(yōu)惠券,每天免費(fèi)領(lǐng)!》


          概述

          與前端對(duì)接的API接口,如果被第三方抓包并進(jìn)行惡意篡改參數(shù),可能會(huì)導(dǎo)致數(shù)據(jù)泄露,甚至?xí)淮鄹臄?shù)據(jù),我主要圍繞時(shí)間戳,token,簽名三個(gè)部分來保證API接口的安全性

          1.用戶成功登陸站點(diǎn)后,服務(wù)器會(huì)返回一個(gè)token,用戶的任何操作都必須帶了這個(gè)參數(shù),可以將這個(gè)參數(shù)直接放到header里。

          2.客戶端用需要發(fā)送的參數(shù)和token生成一個(gè)簽名sign,作為參數(shù)一起發(fā)送給服務(wù)端,服務(wù)端在用同樣的方法生成sign進(jìn)行檢查是否被篡改。

          3.但這依然存在問題,可能會(huì)被進(jìn)行惡意無(wú)限制訪問,這時(shí)我們需要引入一個(gè)時(shí)間戳參數(shù),如果超時(shí)即是無(wú)效的。

          4.服務(wù)端需要對(duì)token,簽名,時(shí)間戳進(jìn)行驗(yàn)證,只有token有效,時(shí)間戳未超時(shí),簽名有效才能被放行。

          開放接口

          沒有進(jìn)行任何限制,簡(jiǎn)單粗暴的訪問方式,這樣的接口方式一般在開放的應(yīng)用平臺(tái),查天氣,查快遞,只要你輸入正確對(duì)應(yīng)的參數(shù)調(diào)用,即可獲取到自己需要的信息,我們可以任意修改參數(shù)值。

          /*
          ?*?Description:?開放的接口
          ?*?@author?huangweicheng
          ?*?@date?2020/12/21
          */

          @RestController
          @RequestMapping("/token")
          public?class?TokenSignController?{

          ????@Autowired
          ????private?TokenSignService?tokenSignService;

          ????@RequestMapping(value?=?"openDemo",method?=?RequestMethod.GET)
          ????public?List?openDemo(int?personId){
          ????????return?tokenSignService.getPersonList(personId);
          ????}
          }

          Token認(rèn)證獲取

          用戶登錄成功后,會(huì)獲取一個(gè)ticket值,接下去任何接口的訪問都需要這個(gè)參數(shù)。我們把它放置在redis內(nèi),有效期為10分鐘,在ticket即將超時(shí),無(wú)感知續(xù)命。延長(zhǎng)使用時(shí)間,如果用戶在一段時(shí)間內(nèi)沒進(jìn)行任何操作,就需要重新登錄系統(tǒng)。擴(kuò)展:記一次token安全認(rèn)證的實(shí)踐

          @RequestMapping(value?=?"login",method?=?RequestMethod.POST)
          ????public?JSONObject?login(@NotNull?String?username,?@NotNull?String?password){
          ????????return?tokenSignService.login(username,password);
          ????}

          登錄操作,查看是否有這個(gè)用戶,用戶名和密碼匹配即可成功登錄。

          /**?
          ?????*?
          ?????*?Description:驗(yàn)證登錄,ticket成功后放置緩存中,
          ?????*?@param
          ?????*?@author?huangweicheng
          ?????*?@date?2020/12/31???
          ????*/
          ?
          ????public?JSONObject?login(String?username,String?password){
          ????????JSONObject?result?=?new?JSONObject();
          ????????PersonEntity?personEntity?=?personDao.findByLoginName(username);
          ????????if?(personEntity?==?null?||?(personEntity?!=?null?&&?!personEntity.getPassword().equals(password))){
          ????????????result.put("success",false);
          ????????????result.put("ticket","");
          ????????????result.put("code","999");
          ????????????result.put("message","用戶名和密碼不匹配");
          ????????????return?result;
          ????????}
          ????????if?(personEntity.getLoginName().equals(username)?&&?personEntity.getPassword().equals(password)){
          ????????????String?ticket?=?UUID.randomUUID().toString();
          ????????????ticket?=?ticket.replace("-","");
          ????????????redisTemplate.opsForValue().set(ticket,personEntity.getLoginName(),10L,?TimeUnit.MINUTES);
          ????????????result.put("success",true);
          ????????????result.put("ticket",ticket);
          ????????????result.put("code",200);
          ????????????result.put("message","登錄成功");
          ????????????return?result;
          ????????}
          ????????result.put("success",false);
          ????????result.put("ticket","");
          ????????result.put("code","1000");
          ????????result.put("message","未知異常,請(qǐng)重試");
          ????????return?result;
          ????}

          Sign簽名

          把所有的參數(shù)拼接一起,在加入系統(tǒng)秘鑰,進(jìn)行MD5計(jì)算生成一個(gè)sign簽名,防止參數(shù)被人惡意篡改,后臺(tái)按同樣的方法生成秘鑰,進(jìn)行簽名對(duì)比。

          /**
          ?????*?@param?request
          ?????*?@return
          ?????*/

          ????public?static?Boolean?checkSign(HttpServletRequest?request,String?sign){
          ????????Boolean?flag=?false;
          ????????//檢查sigin是否過期
          ????????Enumeration?pNames?=??request.getParameterNames();
          ????????Map?params?=?new?HashMap();
          ????????while?(pNames.hasMoreElements())?{
          ????????????String?pName?=?(String)?pNames.nextElement();
          ????????????if("sign".equals(pName))?continue;
          ????????????String?pValue?=?(String)request.getParameter(pName);
          ????????????params.put(pName,?pValue);
          ????????}
          ????????System.out.println("現(xiàn)在的sign-->>"?+?sign);
          ????????System.out.println("驗(yàn)證的sign-->>"?+?getSign(params,secretKeyOfWxh));
          ????????if(sign.equals(getSign(params,?secretKeyOfWxh))){
          ????????????flag?=?true;
          ????????}
          ????????return?flag;
          ????}

          重復(fù)訪問

          引入一個(gè)時(shí)間戳參數(shù),保證接口僅在一分鐘內(nèi)有效,需要和客戶端時(shí)間保持一致。

          public?static?long?getTimestamp(){
          ????????long?timestampLong?=?System.currentTimeMillis();

          ????????long?timestampsStr?=?timestampLong?/?1000;

          ????????return?timestampsStr;
          ????}

          需要跟當(dāng)前服務(wù)器時(shí)間進(jìn)行對(duì)比,如果超過一分鐘,就拒絕本次請(qǐng)求,節(jié)省服務(wù)器查詢數(shù)據(jù)的消耗

          攔截器

          每次請(qǐng)求都帶有這三個(gè)參數(shù),我們都需要進(jìn)行驗(yàn)證,只有在三個(gè)參數(shù)都滿足我們的要求,才允許數(shù)據(jù)返回或被操作。

          public?class?LoginInterceptor?implements?HandlerInterceptor?{

          ????@Autowired
          ????private?RedisTemplate?redisTemplate;

          ????@Override
          ????public?boolean?preHandle(HttpServletRequest?request,?HttpServletResponse?response,Object?handler)?throws?IOException?{
          ????????JSONObject?jsonObject?=?new?JSONObject();
          ????????String?ticket?=?request.getParameter("ticket");
          ????????String?sign?=?request.getParameter("sign");
          ????????String?ts?=?request.getParameter("ts");
          ????????if?(StringUtils.isEmpty(ticket)?||?StringUtils.isEmpty(sign)?||?StringUtils.isEmpty(ts)){
          ????????????jsonObject.put("success",false);
          ????????????jsonObject.put("message","args?is?isEmpty");
          ????????????jsonObject.put("code","1001");
          ????????????PrintWriter?printWriter?=?response.getWriter();
          ????????????printWriter.write(jsonObject.toJSONString());
          ????????????return?false;
          ????????}
          ????????//如果redis存在ticket就認(rèn)為是合法的請(qǐng)求
          ????????if?(redisTemplate.hasKey(ticket)){
          ????????????System.out.println(redisTemplate.opsForValue().getOperations().getExpire(ticket));
          ????????????String?values?=?(String)?redisTemplate.opsForValue().get(ticket);
          ????????????//判斷ticket是否即將過期,進(jìn)行續(xù)命操作
          ????????????if?(redisTemplate.opsForValue().getOperations().getExpire(ticket)?!=?-2?&&?redisTemplate.opsForValue().getOperations().getExpire(ticket)?20){
          ????????????????redisTemplate.opsForValue().set(ticket,values,10L,?TimeUnit.MINUTES);
          ????????????}
          ????????????System.out.println(SignUtils.getTimestamp());
          ????????????//判斷是否重復(fù)訪問,存在重放攻擊的時(shí)間窗口期
          ????????????if?(SignUtils.getTimestamp()?-?Long.valueOf(ts)?>?600){
          ????????????????jsonObject.put("success",false);
          ????????????????jsonObject.put("message","Overtime?to?connect?to?server");
          ????????????????jsonObject.put("code","1002");
          ????????????????PrintWriter?printWriter?=?response.getWriter();
          ????????????????printWriter.write(jsonObject.toJSONString());
          ????????????????return?false;
          ????????????}
          ????????????//驗(yàn)證簽名
          ????????????if?(!SignUtils.checkSign(request,sign)){
          ????????????????jsonObject.put("success",false);
          ????????????????jsonObject.put("message","sign?is?invalid");
          ????????????????jsonObject.put("code","1003");
          ????????????????PrintWriter?printWriter?=?response.getWriter();
          ????????????????printWriter.write(jsonObject.toJSONString());
          ????????????????return?false;
          ????????????}
          ????????????return?true;
          ????????}else?{
          ????????????jsonObject.put("success",false);
          ????????????jsonObject.put("message","ticket?is?invalid,Relogin.");
          ????????????jsonObject.put("code","1004");
          ????????????PrintWriter?printWriter?=?response.getWriter();
          ????????????printWriter.write(jsonObject.toJSONString());
          ????????}
          ????????return?false;
          ????}
          }

          訪問

          先登錄系統(tǒng),獲取合法的ticket

          生成一個(gè)合法的sign驗(yàn)證,獲取測(cè)試ts,訪問openDemo,即可正常訪問。還可以將參數(shù)加密,將http換成https,就不一 一展開了。

          demo代碼

          https://github.com/hwc4110/spring-demo1221



          往期推薦

          看看人家那后端API接口寫得,那叫一個(gè)優(yōu)雅!

          美團(tuán)面試官:生成訂單后一段時(shí)間不支付訂單會(huì)自動(dòng)關(guān)閉的功能該如何實(shí)現(xiàn)?越詳細(xì)越好~

          大周末的,不給自己加個(gè)雞腿?

          ElasticSearch 面試 4 連炮,這誰(shuí)頂?shù)米。?/a>

          下方二維碼關(guān)注我

          技術(shù)草根堅(jiān)持分享?編程,算法,架構(gòu)

          看完文章,餓了點(diǎn)外賣,點(diǎn)擊 ??《無(wú)門檻外賣優(yōu)惠券,每天免費(fèi)領(lǐng)!》

          朋友,助攻一把!點(diǎn)個(gè)在看
          瀏覽 38
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产精品在线视频免费 | SM在线观看免费版 | 另类视频在线观看 | 三级片91久久精品欧美亚洲三级片 | 免费在线黄色小视频 |