App開放接口api安全性的設計與實現
Posted on 2016-09-29 11:49 琪齊 閱讀(4121) 評論(0) 編輯 收藏html
閱讀目錄python
回到目錄web
前言
在app開放接口api的設計中,避免不了的就是安全性問題,由於大多數接口涉及到用戶的我的信息以及一些敏感的數據,因此對這些接口須要進行身份的認證,後端
那麼這就須要用戶提供一些信息,好比用戶名密碼等,可是爲了安全起見讓用戶暴露的明文密碼次數越少越好,咱們通常在web項目中,大多數採用保存的session中,api
而後在存一份到cookie中,來保持用戶的回話有效性。可是在app提供的開放接口中,後端服務器在用戶登陸後如何去驗證和維護用戶的登錄有效性呢?緩存
回到目錄安全
設計
對於敏感的api接口,需使用https協議服務器
https是在http超文本傳輸協議加入SSL層,它在網絡間通訊是加密的,因此須要加密證書。https協議須要ca證書,通常須要交費。cookie
回到目錄網絡
一、原理
用戶登陸後向服務器提供用戶認證信息(如帳戶和密碼),服務器認證完後給客戶端返回一個PID令牌,用戶再次獲取信息時,
帶上此令牌,若是令牌正取,則返回數據。對於獲取Token信息後,訪問用戶相關接口,客戶端請求的url須要帶上以下參數:
① 時間戳:timestamp
② PID令牌:PID(在這咱們給定義爲PID)
而後將全部用戶請求的參數(包括timestamp,pid),而後更具MD5加密(能夠加點鹽),生成動態的url。
而後登錄後每次調用用戶信息時,帶上timestamp,pid參數。
加上時間戳和pid後的URL:http://127.0.0.1:8888/index?pid=d073dae99f70b0cda2fa1ef8d25c527f|1475117419.5424652|0
就變成一個動態的並且相對的具備高安全的,保證數據安全的訪問。
二、具體實現
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(
'|'
)
# 得到數據,以「|」分割開
(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
(url)
ret
=
requests.get(url)
(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'
)
(ret.text)
這是比較粗超的API認證機制,能夠初步瞭解。