<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>

          API接口的安全設(shè)計(jì)驗(yàn)證:ticket,簽名,時(shí)間戳

          共 6453字,需瀏覽 13分鐘

           ·

          2021-01-23 12:56

          作者:一劍天門(mén)

          cnblogs.com/dslx/p/14116294.html

          與前端對(duì)接的API接口,如果被第三方抓包并進(jìn)行惡意篡改參數(shù),可能會(huì)導(dǎo)致數(shù)據(jù)泄露,甚至?xí)淮鄹臄?shù)據(jù),我主要圍繞時(shí)間戳,token,簽名三個(gè)部分來(lái)保證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.但這依然存在問(wèn)題,可能會(huì)被進(jìn)行惡意無(wú)限制訪問(wèn),這時(shí)我們需要引入一個(gè)時(shí)間戳參數(shù),如果超時(shí)即是無(wú)效的。

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

          開(kāi)放接口

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

          ?*
          ?*?Description:?開(kāi)放的接口
          ?*?@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值,接下去任何接口的訪問(wèn)都需要這個(gè)參數(shù)。我們把它放置在redis內(nèi),有效期為10分鐘,在ticket即將超時(shí),無(wú)感知續(xù)命。延長(zhǎng)使用時(shí)間,如果用戶在一段時(shí)間內(nèi)沒(méi)進(jìn)行任何操作,就需要重新登錄系統(tǒng)。

          @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是否過(guò)期
          ????????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ù)訪問(wèn)

          引入一個(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ì)比,如果超過(guò)一分鐘,就拒絕本次請(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是否即將過(guò)期,進(jìn)行續(xù)命操作
          ????????????if?(redisTemplate.opsForValue().getOperations().getExpire(ticket)?!=?-2?&&?redisTemplate.opsForValue().getOperations().getExpire(ticket)?????????????????redisTemplate.opsForValue().set(ticket,values,10L,?TimeUnit.MINUTES);
          ????????????}
          ????????????System.out.println(SignUtils.getTimestamp());
          ????????????//判斷是否重復(fù)訪問(wèn),存在重放攻擊的時(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;
          ????}
          }

          訪問(wèn)

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

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

          demo代碼 https://github.com/hwc4110/spring-demo1221


          點(diǎn)擊閱讀全文前往微服務(wù)電商教程
          瀏覽 20
          點(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>
                  熟女国语对白 | 少妇一级婬片50分钟 | 男人色天堂网 | 正在播放久久 | 操骚人妻 |