在app開放接口api的設計中,避免不了的就是安全性問題,由於大多數接口涉及到用戶的我的信息以及一些敏感的數據,因此對這些接口須要進行身份的認證,python
那麼這就須要用戶提供一些信息,好比用戶名密碼等,可是爲了安全起見讓用戶暴露的明文密碼次數越少越好,咱們通常在web項目中,大多數採用保存的session中,web
而後在存一份到cookie中,來保持用戶的回話有效性。可是在app提供的開放接口中,後端服務器在用戶登陸後如何去驗證和維護用戶的登錄有效性呢?後端
對於敏感的api接口,需使用https協議api
https是在http超文本傳輸協議加入SSL層,它在網絡間通訊是加密的,因此須要加密證書。https協議須要ca證書,通常須要交費。緩存
用戶登陸後向服務器提供用戶認證信息(如帳戶和密碼),服務器認證完後給客戶端返回一個PID令牌,用戶再次獲取信息時,安全
帶上此令牌,若是令牌正取,則返回數據。對於獲取Token信息後,訪問用戶相關接口,客戶端請求的url須要帶上以下參數:服務器
① 時間戳:timestamp ② PID令牌:PID(在這咱們給定義爲PID)
而後將全部用戶請求的參數(包括timestamp,pid),而後更具MD5加密(能夠加點鹽),生成動態的url。cookie
而後登錄後每次調用用戶信息時,帶上timestamp,pid參數。網絡
加上時間戳和pid後的URL:http://127.0.0.1:8888/index?pid=d073dae99f70b0cda2fa1ef8d25c527f|1475117419.5424652|0session
就變成一個動態的並且相對的具備高安全的,保證數據安全的訪問。
1. api請求客戶端想服務器端一次發送用用戶認證信息(用戶名和密碼),服務器端請求到改請求後,驗證用戶信息是否正確。
若是正確:則返回一個惟一不重複的字符串,而後在Redis(任意緩存服務器)中維護這個用戶信息關係,以便其餘api對pid的校驗。
若是錯誤:則返回錯誤碼。
2.服務器設計一個url請求攔截規則
①判斷是否包含timestamp,pid參數,若是不含有返回錯誤碼。 ②根據用戶請求的url參數,服務器端按照一樣的規則生成動態的URL,對比請求的動態url與服務端生成的是否相等,相等則放行容許訪問。 ③判斷服務器接到請求的時間和參數中的時間戳是否相差很長一段時間(時間自定義如十秒),若是超過則說明該url已通過期。 ④記錄下每次請求的動態URL,規定一個動態的URL只能訪問一次,檢測每次請求的url是否請求過,去過存在就返回錯誤代碼(處理url被攔截而且在十秒內請求的訪問)。 ⑤此url攔截只需對獲取身份認證的url放行(如登錄url),剩餘全部的url都需攔截。
3.按期處理保存下來的動態請求URL
服務端規定的規則
#!/usr/bin/env python # -*- coding:utf-8 -*- import tornado.ioloop import tornado.web import hashlib import time access_record = [] # 建立第一次登陸過URL列表 PID_LIST = [ # pid列表 'qwe', 'ioui', '234s', ] class MainHandler(tornado.web.RequestHandler): def get(self): # 獲取url中所有數據 pid = self.get_argument('pid', None) # 獲取變量 m5, client_time, i = pid.split('|') # 得到數據,以「|」分割開 print(m5, client_time, i) server_time = time.time() # 服務端的當前時間 # 時間超過10s禁止 if server_time > float(client_time) + 10: # 服務端的當前時間大於客戶端當前時間加10秒,表示過時不容許訪問 self.write('gun') return # 處理10s內容重複的請求 if pid in access_record: # 若是客戶端請求的動態URL在第一次登陸過的URL列表中 self.write('gun') return access_record.append(pid) # 容許經過的url添加到列表中 pid = PID_LIST[int(i)] # 得到客戶端發來的pid後面攜帶的數字 ramdom_str = "%s|%s" % (pid, client_time) # 把客戶的pid與當前時間戳拼接 h = hashlib.md5() # MD5加密值 h.update(bytes(ramdom_str, encoding='utf-8')) # 把客戶的pid與當前時間戳拼接一個字符串再盡心md5加密 server_m5 = h.hexdigest() # 服務端生成的動態URL # print(m5,server_m5) if m5 == server_m5: # 客戶生成的與服務端生成的進行對比 self.write("Hello, world") else: self.write('gun') application = tornado.web.Application([ (r"/index", MainHandler), ]) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start()
客戶端按規則生成符合的
#!/usr/bin/env python # -*- coding:utf-8 -*- import time import requests import hashlib PID = 'qwe' # 客戶的PID current_time = time.time() # 當前時間戳 ramdom_str = "%s|%s" % (PID, current_time) # 把pid與當前時間戳拼接成一個字符串 h = hashlib.md5() # md5加密 h.update(bytes(ramdom_str, encoding='utf-8')) # 把pid與當前時間戳拼接成一個字符串再進行md5加密 UID = h.hexdigest() # 加密後的字符串 q = "%s|%s|0" % (UID, current_time) # 在把這個字符串後面拼接一個數值 0 url = 'http://127.0.0.1:8888/index?pid=%s' % q # 生成最後生成的動態url print(url) ret = requests.get(url) print(ret.text)
測試效果代碼
#!/usr/bin/env python # -*- coding:utf-8 -*- import requests ret = requests.get('http://127.0.0.1:8888/index?pid=c2539948caa7b7fe0d00fcd9d75b7574|1474341577.4938722|0') print(ret.text)
這是比較粗超的API認證機制,能夠初步瞭解。