webapi应用架构详解

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


posted on 2017-01-26 12:01  迪米特  阅读(2634)  评论(0编辑  收藏  举报

导航