资源来源bilibili

是什么

  • 一种开发一套接口即可供 移动、pc不同平台,不同操作系统提供服务的接口。

查看源图像

查看源图像

  • 结合restful api的后端对前端的请求以及相应进行相应的处理。

查看源图像

  • 在restful api’中不同的url定义是有要求的

查看源图像

token

  • token的意思是“令牌”,是服务端生成的一串字符串,作为客户端进行请求的一个标识。

  • 当用户第一次登录后,服务器生成一个token并将此token返回给客户端,以后客户端只需带上这个token前来请求数据即可,无需再次带上用户名和密码。

简单理解token机制

对于session与token的区别以下是评论中的:

个人愚见:session是存储在服务器端的数据,可以包含很多东西,你可以理解为一个数据容器,需要时读取就行!token相当于通行证(唯一),用于前端向后台进行数据请求时进行判断“你”到底是不是“你”,类似于门禁卡!

import base64
# 加密
import random
import time

from flask import Flask, request

app = Flask(__name__)

users = {
"magigo": ["123456"]
}


# 生成token
def gen_token(uid=None):
# ':'.join([str(uid), str(random.random()), str(time.time() + 7200)]) 是 r''原生字符串 必须转换为 b'' 字节字符串才能 不4
# encode
# print(':'.join([str(uid), str(random.random()), str(time.time() + 7200)]))
# print(':'.join([str(uid), str(random.random()), str(time.time() + 7200)]).encode())
# str = ':'.join([str(uid), str(random.random()), str(time.time() + 7200)])
token = base64.b64encode(':'.join([str(uid), str(random.random()), str(time.time() + 7200)]).encode())
users[uid].append(token)
return token


# 验证token
def verify_token(token):
_token = base64.b64decode(token).decode()
if not users.get(_token.split(':')[0])[-1] == token:
return 1
if float(_token.split(':')[-1]) >= time.time():
return -1
else:
return 0


@app.route("/index", methods=["post", 'get'])
def index():
print(request.headers)
return 'hello'


"""登录模块"""


# 通过request的头部获取 Authorization 获取密码和uid 验证正确就返回一个token

@app.route('/login', methods=["post", 'get'])
def login():
# # base64.b64decode 返回的是 b'' 的bytes字符串 需要decode()
# print(request.headers['Authorization'])
# print(request.headers['Authorization'].split(' '))
# print(request.headers['Authorization'].split(' ')[-1])
# print(request.headers['Authorization'].split(' ')[-1])
# print(base64.b64decode(request.headers['Authorization'].split(' ')[-1]))
# print(base64.b64decode(request.headers['Authorization'].split(' ')[-1]).decode())
# print(base64.b64decode(request.headers['Authorization'].split(' ')[-1]).split(':')[1])
uid, pw = base64.b64decode(request.headers['Authorization'].split(' ')[-1]).decode().split(':')
print("%s %s" % (uid, pw))
if users.get(uid)[0] == pw:
return gen_token(uid)
else:
return 'error'


"""登录后的验证是否登录模块"""


@app.route('/test1', methods=['POST', 'GET'])
def test():
token = request.args.get('token')
if verify_token(token) == 1:
return 'data'
else:
return 'error'


if __name__ == '__main__':
app.run(debug=True)


OAuth

  • OAuth(开放授权)是一个正式的互联网标准协议

  • 允许第三方网站在用户授权的前提下访问在用户服务商那里存储的各种信息。而这种授权无需用户将用户名和密码等提供个该第三方网站。

    (举例,某网站第三方登录功能,不需要临时创建自己的用户账号,直接qq、微信登录,并获取qq或者微信的基本信息,id,昵称,图像等)

  • OAuth允许用户提供一个令牌给第三方网站,一个令牌对应一个特定的第三方网站,同时该令牌只能在特定的时间内访问特定的资源。

原理

image-20210317155144532

  • Client(某网站) 、Resource Owner(用户)、Authorization Server(qq授权服务器 发放token)、Resource Server(代表qq资源服务器,提供资源。 可以是与Autho Server实属同一个)
  • 当用户访问某网站时,A)Client要求资源得授权,B) 用户同意给与授权。相应的认证服务器会发送一个授权码Client的后端服务器,交由其获取token。
  • Client通过授权码访问第三方QQ的授权服务器,表示自己有授予的权限得到token,服务器认证无误后,给予token。
  • Client通过token向资源服务器申请相应的资源。
  • 该token是资源申请的令牌,有时效性,并由用户管理。

授权方式

授权码模式

客户端的后台服务器与认证服务器交互,通过用户允许由认证服务器向Client的后端服务器给与授权码,Client的后端服务器通过该授权码向认证服务器获取token,而不交于Client去获取token。

这种方式是最常用的流程,安全性也最高,它适用于那些有后端的 Web 应用。授权码通过前端传送,令牌则是储存在后端,而且所有与资源服务器的通信都在后端完成。这样的前后端分离,可以避免令牌泄漏。

img

资源来源于

简化模式

没有上图中的Client向认证服务器获取token的步骤,而是由资源服务器发送的消息提取token,通过该token获取资源。

有些 Web 应用是纯前端应用,没有后端。这时就不能用上面的方式了,必须将令牌储存在前端。RFC 6749 就规定了第二种方式,允许直接向前端颁发令牌。这种方式没有授权码这个中间步骤,所以称为(授权码)"隐藏式"或者“简化模式”(implicit)。

密码模式

用户必须把自己的第三方(QQ)账号密码提供该Clien(某网站),该Client不得存储密码。通常这是用户对Client高度信任的情况下。该Client是操作系统的一部分或者是某个知名的大公司等。而认证服务器也只有在其他模式下无法实现的情况下才考虑使用。

客户端模式

Client,用户直接向Clent注册账号,而由Client以自己的名义要求第三方资源的服务器提供资源。这种情况下不存在授权的问题。

  • 授权码模式是功能最完整、流程最严密的授权模式。
  • 简化模式不通过第三方应用程序的服务器,直接在浏览器中向认证服务器申请临牌,跳过了“授权码”这个步骤。
  • 密码模式需要用户向客户端提供自己的用户名和密码
  • 客户端模式指客户端以自己的名义,而不是以用户的名义,向“服务提供商”进行认证

RESTful

  • reqparse是flask_restful库中一个类似于argparse的库,队请求的数据进行验证,支持验证表单数据。

入门例子

# 定义Todo资源类
class Todo(Resource):
def __init__(self):
# 获取request的参数解析器
self.parser = reqparse.RequestParser()
# 设置解析器
self.parser.add_argument(
'name',
type=str,
required=True,
help='parameter name is required'
)

def get(self):
# 获取值
args = self.parser.parse_args()
return args,201

def post(self):
args = self.parser.parse_args()
return args


路由url指定参数

端点(Endpoints)

  • 从根本上来说,端点就是程序中一组逻辑处理单元的ID,该ID对应的代码决定了对此ID请求应该作出何种响应。通常,端点与视图函数同名,但是你也可以修改它,例如:

  • 很多时候在一个 API 中,你的资源可以通过多个 URLs 访问。你可以把多个 URLs 传给 Api 对象的 Api.add_resource() 方法。每一个 URL 都能访问到你的 Resource

api.add_resource(HelloWorld,
'/',
'/hello')

你也可以为你的资源方法指定 endpoint 参数。

api.add_resource(Todo,
'/todo/<int:todo_id>', endpoint='todo_ep')

Endpoint有什么作用

端点通常用作反向查询URL地址(viewfunction**–>endpoint–>**URL)。例如,在flask中有个视图,你想把它关联到另一个视图上(或从站点的一处连接到另一处)。不用去千辛万苦的写它对应的URL地址,直接使用URL_for()就可以啦:

@app.route('/')
def index():
print url_for('give_greeting', name='Mark') # 打印出 '/greeting/Mark'

@app.route('/greeting/<name>')
def give_greeting(name):
return 'Hello, {0}!'.format(name)1234567

备注:*url_for()*中give_greeting是端点名.

来源

解析器设置解析的参数位置

  • 默认下,RequestParser 试着从 flask.Request.values,以及 flask.Request.json 解析值。
  • 在 add_argument() 中使用 location 参数可以指定解析参数的位置。flask.Request 中任何变量都能被使用。例如:
# 仅在 POST body中查找
parser.add_argument("method", location="form")

# 仅在querystring中查找
parser.add_argument("PageSize", location="args")

# 从请求头中查找
parser.add_argument("User-Agent", location="headers")

# 从http cookies中查找
parser.add_argument("session_id", location="cookies")

# 从上传文件中查找
parser.add_argument("picture", type=werkzeug.datastructures.FileStorage, location="files")

# 内部调用get_json(),可通过字典的方式获取键值,如果没有对应的键,那么会返回HTTP 400错误响应
parser.add_argument("name", location="json")

使用 strict=True 调用 parse_args 能够确保当请求包含你的解析器中未定义的参数的时候会抛出一个异常。

args = parser.parse_args(strict=True)