webapi適用場景
常見的應用包括以下四類,PC客戶端程序,APP程序,網站程序,H5程序。
這些應用需要的數據,服務可由同一個接口服務程序提供,這樣,大大提高了產品多平台設計開發的效率,避免了重復的編碼。
什么是webapi
webapi一般采用restful風格。以tornado為例,url路由配置如下:
url = [ (r'/test', testfc.testHandler) ]
業務邏輯層,返回數據一般為json格式:
class testHandler(RequestHandler): def get(self): self.post() def post(self): result={} if True: result["code"]="200" result["status"]="true" result["result"]="success" else: result["code"]="300" result["status"]="false" result["result"]="fail" self.write(json_encode(result)) return
webapi訪問方式如下:
http://127.0.0.1:9999/test
返回結果:
{"status": "true", "code": "200", "result": "success"}
webapi架構設計
如何確認一個app能夠訪問webapi?
使用AppID驗證app訪問webapi的合法性,AppID為服務端給出的一個ID標志。
我們可以為web/app/winform分別分配一個ID,從而可以確定訪問的合法性,和訪問的渠道。
如何保證app參數的正確性,沒有被篡改?
客戶端使用AppID對應的AppSecert,對參數進行簽名(MD5/SHA等)
服務端使用同樣的方式簽名,和客戶端簽名校驗。
如何防止webapi url被截獲,重新訪問?
客戶端訪問webapi時帶上時間戳參數,服務端對時間戳進行校驗,如10分鍾內的訪問才是有效的。
涉及具體用戶的操作時,如何驗證用戶?
可以使用username,psw參數的方式訪問webapi。但是這種方式很不安全。
使用授權token是很好的解決辦法。在用戶登錄成功時,服務端生成一個授權碼,對應用戶信息。
訪問時帶上token參數,服務端查詢token有效性,和token對應的用戶信息。
使用示例
簽名代碼如下:
#coding:utf-8 __author__ = 'jy' import sys reload(sys) sys.setdefaultencoding('utf-8') sys.path.append('..') import datetime import time import math import hashlib import urllib def md5encode(source): m2 = hashlib.md5() m2.update(source) return m2.hexdigest() def dataSort(data): dataKeys=data.keys() dataKeys.sort() result="" for key in dataKeys: result+=key.strip() result+=data[key].strip() return result def dataSecret(data): secret="af4d2c92-4bb7-11e5-8111-00163e001071" data=secret+data+secret return data def getSign(postdata): try: #升序排列,合並為字符串 dataSign=dataSort(postdata) #加密鑰 dataSign=dataSecret(dataSign) #md5編碼 dataSign=md5encode(dataSign) return dataSign except Exception as error: pass
驗證url參數代碼如下:
#coding:utf-8 __author__ = 'haoy' import sys reload(sys) sys.setdefaultencoding('utf-8') sys.path.append('..') from tornado.escape import json_decode, json_encode import datetime import time import math from utils.sign import * def valiUrldata(data): result={} try: #判斷appKey是否合法 appKey=data.get("appKey","") if appKey!="21ec85ec-30ca-491b-8bfb-c05e479eadc0": result["code"]="300" result["status"]="false" result["result"]="賬號不合法" return result #appKey對應的secret appSecret="af4d2c92-4bb7-11e5-8111-00163e001071" #檢驗時間 timeStamp=data.get("timeStamp","0") timeStamp=int(timeStamp) nowtime=int(round(time.time())) if abs(timeStamp-nowtime)>600: result["code"]="300" result["status"]="false" result["result"]="請求時間戳不合法" return result #校驗簽名 clientSign=data.get("sign","") if clientSign=="": result["code"]="300" result["status"]="false" result["result"]="請求參數簽名不能為空" return result data.pop("sign") serverSign=getSign(data) print serverSign if clientSign!=serverSign: result["code"]="300" result["status"]="false" result["result"]="請求參數簽名不合法" return result result["code"]="200" result["status"]="true" result["result"]="通過驗證" return result except Exception as error: result["code"]="300" result["status"]="false" result["result"]="異常:"+error.message return result
登錄代碼如下:
import uuid import redis class testLoginHandler(BaseHandler): def get(self): self.post() def post(self): #獲取url參數 args=self.request.arguments data={} for key in args: data[key]=self.get_argument(key,"") #驗證url參數 valiResult=valiUrldata(data) if valiResult["code"]=="300": self.write(json_encode(valiResult).decode("unicode_escape")) return username=self.get_argument("username","") psw=self.get_argument("psw","") if username=="shijingjing07" and psw=="123456": token = uuid.uuid1() redisPool = redis.ConnectionPool(host='127.0.0.1',password='123456', port=6379, db=0) cache = redis.Redis(connection_pool=redisPool) cache.setex(token,username,300) result["code"] = 200 result["status"] = "true" result["result"] = token else: result["code"] = 300 result["status"] = "false" result["result"] = "account illegal" self.write(json_encode(result))
返回結果:
{"status": "true", "code": 200, "result": "5720c334-dbcc-11e6-84f1-00163e001071"}
