framework文书档案翻译,django编辑微信小程序

2019-10-06 08:01 来源:未知

本文只说明如何从HTTP请求中解析tokentoken验证是一种web常用的身份验证手段,在这里不讨论它的具体实现我需要在golang里实现token验证,Web框架是Gin(当然这与框架没有关系)

微信小程序后端与普通web的区别

REST framework的Request类扩展自标准的HttpRequest,增加了REST framework灵活的请求解析和请求验证支持。

  1. request获取tokenstring
  2. tokenstring转化为未解密的token对象
  3. 未解密的token对象解密得到解密后的token对象
  4. 解密后的token对象里取参数

微信小程序的后端开发和普通的restful API 大致上相同,只不过要注意以下几点限制


该函数根据request,获得tokenstring,并转为未解密token对象,解密后得到解密token对象

必须使用HTTPS协议请求后端服务器

请求解析

REST framework的Request对象提供了灵活的请求解析,让你可以像一般处理普通form数据一样处理带JSON数据或者媒体类型的request。

import github.com/dgrijalva/jwt-go/request``request.ParseFromRequest(req *http.Request, extractor Extractor, keyFunc jwt.Keyfunc)

  1. req即为http请求
  2. extractor 是一个实现了Extractor接口的对象,该接口需要实现的函数是ExtractToken(*http.Request) (string, error),用于从http请求中提取tokenstring
  3. keyFunc是一个函数,需要接受一个“未解密的token”,并返回Secretkey的字节和错误信息

不支持COOKIE

.data

request.data返回request body解析后的内容。这类似于标准的request.POSTrequest.FILES属性除了下面几个区别:

  • 它包含了所有解析后的内容,包括文件类型和非文件类型的输入
  • 它支持除了POST类型以外的其他HTTP方法的内容解析,意味着你可以访问PUTPATCH类型reqeust的内容。
  • 它提供REST framework灵活的请求解析,而不单单支持from数据。举例说你可以像处理form数据一样处理JSON数据。
    详细信息,参考parsers documentation
func GetToken(r *http.Request) (token *jwt.Token, err error) { //由request获取token t := T{} // t是已经实现extract接口的对象,对request进行处理得到tokenString并生成为解密的token // request.ParseFromRequest的第三个参数是一个keyFunc,具体的直接看源代码 // 该keyFunc参数需要接受一个“未解密的token”,并返回Secretkey的字节和错误信息 // keyFunc被调用并传入未解密的token参数,返回解密好的token和可能出现的错误 // 若解密是正确的,那么返回的token.valid = true return request.ParseFromRequest(r, t, func(token *jwt.Token) (interface{}, error) { return []byte(Secretkey), nil })}

func GetIdFromClaims(key string, claims jwt.Claims) string { v := reflect.ValueOf if v.Kind() == reflect.Map { for _, k := range v.MapKeys() { value := v.MapIndex if fmt.Sprintf("%s", k.Interface == key { return fmt.Sprintf("%v", value.Interface } } } return ""}// 示例 :GetIdFromClaims("username", token.claims) 其中token是已经解密的token

不支持django内置的user登录, 因为它使用的是微信的用户系统

.query_params

request.query_params 是比request.GET更贴切的一个同义词
为了清楚起见,在你的代码里,我们推荐你使用request.query_params来代替Django's标准request.GET。这样做可以让你的代码更准确和明了-任何HTTP方法类型都可能包含查询信息,不紧紧是GET请求。

以下列举了在jwt解析过程所调用的jwt-go库函数

应对方法

.parsers

APIView类或者@api_view 装饰器将保证这个属性会被自动赋予一系列的Parser实例,基于这个view的parser_class集合或者基于DEFAULT_PARSER_CLASSES设置
你一般不会访问这个属性


如果一个客户端发送了有缺陷的内容,那么访问request.data也许会引起ParseError。默认情况下REST framework的APIView类或者@api_view装饰器将会捕获这个错误然后返回400 Bad Request的response

如果一个客户端发送了一个包含有不能解析的content-type那么将会引起一个UnsupportedMediaType异常,这个异常将会被捕获并且返回一个415 Unsupported Media Type response


func: request.ParseFromRequest(req, extractor, keyFunc)(*Token, error)

struct: request.fromRequestParser{req, extractor, claims=nil, parser=nil}func: parser.ParseWithClaims(tokenString, claims, keyFunc)(*Token, error)

parser.ParseUnverified(tokenString string, claims Claims) (token *Token, parts []string, err error)

对于HTTPS的限制, 很简单, 去godaddy等网站申请一个https证书, 下载后使用nginx指定即可, 可以参照此文章, 这篇文章的证书是自己生成的, 这里需要替换为申请的证书

内容协商

request暴漏了一些属性是允许你去判断内容协商阶段的结果。这将允许你去实现一些行为比如给不同的媒体类型选择一个不同的序列化方案

不支持cookie, django原生的session机制就会失效

.accepted_renderer

内容协商阶段选出来的渲染器实例

不支持django内置的user登录, 因为它使用的是微信的用户系统

.accepted_media_type

是一个字符串用来表明内容协商阶段接受的媒体类型


对于上边这两个问题,我使用了JWT来保证了用户的在线验证. 那么什么是JWT呢, 可以看下推酷的这篇文章了解一下, 简要的来说就是用户登录以后, 原先保存在cookie里边的一个随机的sessionid变成了保存在http头部的Authorization字段的一个token值, 这个值是服务端自身加密的, 客户端无需解密, 只要服务端知道这个token对应这个用户就好, 当然这里也有很多的附加功能, 比如超时等等, 不再赘述

验证

REST framework提供了灵活的,针对每一个请求的验证,可以给你的这样的能力:

  • 不同部分的API使用不同的验证策略
  • 支持使用多个验证策略
  • 同时提供即将到来的request相关的user和token信息

然而网上有很多的jwt开源项目, 比如django-jwt,rest-framework-jwt, 你都可以使用, 但是却不适用于微信小程序, 为什么这么说? 是因为这两个项目都和django内置的user相耦合, 在上边提出的第三个问题的地方会引起问题, 在尝试了rest-framework-jwt发现问题后, 我毅然决定自己使用pyjwt实现一个用户登录/鉴权的组件.

.user

request.user一般返回django.contrib.auth.models.User的一个实例,尽管它的行为取决于你使用的验证策略。
如果request没有验证,那么request.user的默认值的jdango.contrib.auth.models.AnonymousUser
更多信息,查看authentication documentation

首先要知道微信小程序的登录流程是:

.auth

request.auth返回任何额外的授权上下文。reqeust.auth的具体行为取决于使用的认证策略,但一般来说它会是reqeuset请求授权得到token的实例。
更多详细信息请看 authentication documentation

客户端请求用户授权

www.weide1946.com,.authenticators

APIView类或者@api_view装饰器将会保证这个属性被自动设置为一列Authentication的实例,基于authentication_classes集合或者基于DEFAULT_AUTHENTICATORS设置。
你一般不需要访问这个属性


用户授权成功

浏览器增强

REST framework支持一些浏览器增强比如说是基于浏览器的PUT,PATCH和DELETE表单

客户端或得到用户基本信息(包括code,没有openid的用户明文信息, 有openid的加密后的信息, 解密向量iv)

.method

request.method返回请求的HTTP方法大写的字符串表达。
基于浏览器PUT,PATCH,DELETE表单被支持

客户端把返回的信息发送到服务器

.content_type

request.content_type,返回一个代表HTTP请求body媒体类型的字符串对象,或者一个空串如果没有媒体类型提供的话。你一般不需要直接访问请求的内容类型,因为一般来说你会基于REST框架的默认解析行为
如果你确实需要访问request的媒体类型,你应该使用.content_type属性优先于使用request.META.get('HTTP_CONTENT_TYPE'),因为它提供基于浏览器的非form内容。
.stream
request.stream返回request body的流的展现方式
你一般不会直接访问request的内容,因为你一般会基于REST 框架的默认请求解析实现


服务器使用code去微信服务器换取session_key

标准HttpRequest属性

因为REST framework的Request类扩展自Django's的HttpRequest,所有其他的标准的属性和方法都是可用的。举例来说request.METArequest.session字典都和之前一样
注意因为实现的原因,Request类不是直接继承子HttpRequest类,而是使用组合扩展了它

服务器用这个session_key iv去解密用户密文, 得到用户完整信息(基本 openid)

将用户在服务器登录, 维持用户session(这里的失效时间微信约定是30天)

看起来好像步骤很多, 但是我们没有必要去重新造轮子, 早有人已经写好认证流程python-weixin我们可以省去4-6的步骤, 那么剩下的问题就是如何解决第7步也就是维持session的问题了.

因为jwt始终使用http头部的token进行验证这里我的思路是:

微信用户登录后, 返回客户端token, 并在缓存创建用户的session信息

客户端请求时附加http头Authorization=JWT

使用中间件检验http头的token, 审查通过则在request上追加一个jwt_user属性(这里不想覆盖django自带user), 同时去缓存寻找这个用户的session信息, 加载到request.jwt_session, 审查如果不通过则jwt_user设置为None, jwt_session为一个空的session对象

使用认证方法, 对于需要进行登录的接口检验用户是否为None

到这一步就已经完成了整个的用户鉴权/session持久化流程, 如果你只是为了了解django开发小程序注意的点到这里已经可以结束了, 下边我要介绍的是我实现的一套中间件逻辑, 有兴趣可以拿去直接使用.

这里中间件我已经实现了参见django-jwt-session-auth, 调用模块内部的jwt_login函数, 登录你的用户时会返回一个token, 这个token将要返回客户端, 同时它也会做user的session缓存动作.下一次客户端带着Authorization=JWT 调用的时候中间件会直接将对应的user和session加载到request.jwt_user和request.jwt_session, 这里你需要设置的只有设置两个值:USER_TO_PAYLOAD和PAYLOAD_TO_USER两个方法:

*USER_TO_PAYLOAD: 根据当前登录的用户生成一个字典payload方法*PAYLOAD_TO_USER: 根据你之前生成的payload找到对应的用户

注: 2017.1.15我这个组件还没有完整的readme, 后续会加上

最后, 关于认证器, 如果你使用rest-framework可以直接继承BaseAuthentication在authenticate方法里校验jwt_user是否为None即可, 如果直接使用django原生的view, 可以写一个装饰器装饰在类view的dispatch方法上或直接装饰在函数的view上.

示例

# django-jwt-session-auth设置, 放在settings.py文件中JWT_AUTH = {'PAYLOAD_TO_USER':'user.auth.payload_to_user','USER_TO_PAYLOAD':'user.auth.user_to_payload',}# rest-framework验证器classWechatUserAuthentication(BaseAuthentication):defauthenticate(self, request):ifnotrequest.jwt_user: msg =u'请先授权'raiseexceptions.AuthenticationFailedreturn(request.jwt_user, request.jwt_user.uuid)# 原生django验证装饰器deflogin_required:@wrapsdefverify_login(request, *args, **kwargs):ifrequest.jwt_user:returnfunc(request, *args, **kwargs)else:# 返回HTTP_401returnverify_login

版权声明:本文由韦德娱乐1946_韦德娱乐1946网页版|韦德国际1946官网发布于网络编程,转载请注明出处:framework文书档案翻译,django编辑微信小程序