微信小程序的登入与授权
官方文档:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html
小程序登录
小程序可以通过微信官方提供的登录能力方便地获取微信提供的用户身份标识,快速建立小程序内的用户体系。
登录流程
说明:
调用 wx.login() 获取 临时登录凭证code ,并回传到开发者服务器。
调用 auth.code2Session 接口,换取 用户唯一标识 OpenID 和 会话密钥 session_key。
之后开发者服务器可以根据用户标识来生成自定义登录态,用于后续业务逻辑中前后端交互时识别用户身份。 注意:
会话密钥 session_key 是对用户数据进行 加密签名 的密钥。为了应用自身的数据安全,开发者服务器不应该把会话密钥下发到小程序,也不应该对外提供这个密钥。
临时登录凭证 code 只能使用一次
小程序端执行wx.login后在回调函数中就能拿到上图的code,然后把这个code传给我们后端程序,后端拿到这个这个code后,可以请求code2Session接口拿到用的openid和session_key,openid是用户在微信中唯一标识,我们就可以把这个两个值(val)存起来,然后返回一个键(key)给小程序端,下次小程序请求我们后端的时候,带上这个key,我们就能找到这个val,就可以,这样就把登入做好了。
总结:小程序中执行wx.login,获取code,在后端请求code2Session接口,传入code参数,拿到openid和session_key。
1.wx.login() (该函数在app.js中)
调用接口获取登录凭证(code)。通过凭证进而换取用户登录态信息,包括用户的唯一标识(openid)及本次登录的会话密钥(session_key)等。用户数据的加解密通讯需要依赖会话密钥完成。更多使用方法详见 小程序登录。
参数
object.success 回调函数
参数
Object res
示例代码 (获取code值传递到后端)
var _this=this
wx.login({
success: res => {
// 发送 res.code 到后台换取 openId, sessionKey, unionId
wx.request({
url: _this.globalData.Url+'/login/', #后台接口
data:{"code":res.code}, #传递给后台使用的code
header:{"content-type":"application/json"},
method:"POST",
success:function(res){
console.log(res)
wx.setStorageSync("login_key",res.data.data.login_key) #本地存储
} })
}
}) globalData: {
Url:"http://127.0.0.1:8000",
userInfo: null
}
2.code2Session
登录凭证校验。通过 wx.login 接口获得临时登录凭证 code 后传到开发者服务器调用此接口完成登录流程。更多使用方法详见 小程序登录。
请求地址
GET https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code
请求参数
返回值
errcode 的合法值
示例代码
settings.py (配置请求代码)
AppId="wx29fad388b1f51644" AppSecret="d00c23ad3faf96ca218c20f6aaece7a7" code2Session="https://api.weixin.qq.com/sns/jscode2session?appid={}&secret={}&js_code={}&grant_type=authorization_code"
pay_mchid =''
pay_apikey = 'xi34nu5jn7x2uujd8u4jiijd2u5d6j8e'
wx_login.py (传入小程序获得的code,请求code2Session,得到openid和session_key)
from app01.wx import settings
import requests def login(code):
response=requests.get(settings.code2Session.format(settings.AppId,settings.AppSecret,code))
data=response.json()
if data.get("openid"):
return data
else:
return False
models.py
class Wxuser(models.Model):
id = models.AutoField(primary_key=True)
openid=models.CharField(max_length=255)
name = models.CharField(max_length=50)
avatar = models.CharField(max_length=200)
language = models.CharField(max_length=50)
province = models.CharField(max_length=50)
city = models.CharField(max_length=50)
country = models.CharField(max_length=50)
#gender = models.CharField(max_length=50)
creat_time = models.DateTimeField(auto_now_add=True)
update_time = models.DateTimeField(auto_now=True)
def __str__(self):
return self.openid
views/User.py
from rest_framework.views import APIView
from rest_framework.response import Response
from app01.wx import wx_login
from django.core.cache import cache
import hashlib,time
from app01 import models
from app01.wx import WXBizDataCrypt
from app01.my_ser import User_ser class Login(APIView):
def post(self,request):
param=request.data #获取小程序传递过来的参数
if param.get("code"):
data=wx_login.login(param.get("code")) #小程序传递过来的code放入codeSession请求,来获得openid和session_key
if data:
val=data['openid']+"&"+data["session_key"] #获取到openid和session_key做一下拼接
key=data["openid"]+str(int(time.time())) #生成一个key传给小程序
md5=hashlib.md5()
md5.update(key.encode("utf-8"))
key=md5.hexdigest() #加密
cache.set(key,val) #存入后端的redis中
has_user=models.Wxuser.objects.filter(openid=data['openid']).first() #判断数据库中不存在该openid就保存在数据库中
if not has_user:
models.Wxuser.objects.create(openid=data['openid']) #存在后端数据库中
return Response({
"code":200,
"msg":"ok",
"data":{"login_key":key} #传递给小程序数据key,下次小程序请求拿key取值
})
else:
return Response({"code": 200, "msg": "code无效"})
else:
return Response({"code":200,"msg":"缺少参数"})
至此,微信登录就算完成了,但是上面提及的session_key还没用到。这个会在下面用户授权使用到,用来解密用户的数据。
微信授权获取用户
后端获取微信用户信息流程
因为session_key会过期,所以我们使用之前先用checksession先检测一下是否过期,如果过期了再重新生成。
官方文档:https://developers.weixin.qq.com/miniprogram/dev/api/open-api/login/wx.checkSession.html
1.wx.checkSession
检查登录态是否过期。
通过 wx.login 接口获得的用户登录态拥有一定的时效性。用户越久未使用小程序,用户登录态越有可能失效。反之如果用户一直在使用小程序,则用户登录态一直保持有效。具体时效逻辑由微信维护,对开发者透明。开发者只需要调用 wx.checkSession 接口检测当前用户登录态是否有效。
登录态过期后开发者可以再调用 wx.login 获取新的用户登录态。调用成功说明当前 session_key 未过期,调用失败说明 session_key 已过期。更多使用方法详见 小程序登录。
参数
示例代码
test.wxml
<button open-type="getUserInfo" bindgetuserinfo="info">授权登录</button>
test.js
info:function(res){
wx.checkSession({ #检测session_key是否过期
success() {
//session_key 未过期,并且在本生命周期一直有效
wx.getUserInfo({ #没有过期,可以查询用户的个人信息
success:function(res){
wx.request({ #向后台发起请求
url: app.globalData.Url+"/getinfo/",
data: {"encryptedData": res.encryptedData,"iv":res.iv,"login_key":wx.getStorageSync("login_key")},
method :"POST",
header:{"content-type":"application/json"},
success:function(res){
console.log(res)
}
})
}
}) },
fail() {
//session_key 已经失效,需要重新执行登录流程
wx.login() //重新登录
}
})
}
2.wx.getSetting() 获取用户当前的授权状态
当你想获取用户的信息,或者想让用户做一些操作,你要经过用户的同意。调用wx.getSetting来判断用户是否已经授权,如果没有授权,就要让他点击按钮授权,同意之后你就能进行下面的操作了。返回值中只会出现小程序已经向用户请求过的权限。
参数
object.success回调函数
参数
示例代码
lu:function(){
wx.getSetting({ #查询用户已经授权的所有操作
success(res) {
if (!res.authSetting['scope.record']) { #回调函数authSetting查询用户是否授权了"scope.record" 这个scope
wx.authorize({
scope: 'scope.record',
success() {
// 用户已经同意小程序使用录音功能,后续调用 wx.startRecord 接口不会弹窗询问
wx.startRecord() #调用小程序录音功能
}
})
}else{
wx.startRecord()
}
}
})
}
回调函数AuthSetting官方文档:https://developers.weixin.qq.com/miniprogram/dev/api/open-api/setting/AuthSetting.html
注意事项:
1.wx.authorize({scope:"scope.userInfo"}),不会弹出授权窗口,请在页面使用<button_open-type="getUserInfo">
2.需要授权 scope.userLocation、scope.userLocationBackground 时必须配置地理位置用途说明(https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html#permission)
针对于上面的代码中,还有wx.authorize
提前向用户发起授权请求。调用后会立刻弹窗询问用户是否同意授权小程序使用某项功能或获取用户的某些数据,但不会实际调用对应接口。如果用户之前已经同意授权,则不会出现弹窗,直接返回成功。更多用法详见 用户授权。
参数
3.wx.getUserinfo() 获取用户信息,解密获取一些用户敏感信息
调用前需要用户授权scope.userInfo
参数
object.lang的合法值
object.success回调函数
参数
接口调整说明
在用户未授权的情况下调用此接口,将不再出现授权弹窗,会直接进入fail回调。在用户已经授权店额情况下调用此接口,可成功获取用户信息。
示例代码
小程序代码
info:function(res){
wx.checkSession({
success() {
//必须是已经授权情况下使用
wx.getUserInfo({
success:function(res){ #接口成功回调函数
wx.request({ #向后台发起请求,发送数据
url: app.globalData.Url+"/getinfo/", #向后台路由发起请求
data: {"encryptedData": res.encryptedData,"iv":res.iv,"login_key":wx.getStorageSync("login_key")},
method :"POST",
header:{"content-type":"application/json"},
success:function(res){
console.log(res)
}
})
}
}) },
fail() {
//session_key 已经失效,需要重新执行登录流程
wx.login() //重新登录
}
})
}
后台
小程序把需要的数据都传递过来了,现在需要在后台校验与解密开放数据。
微信会对这些开放的数据做签名和加密处理。开发者后台拿到开放数据后可以对数据进行校验签名和解密,来保证数据不被篡改。
签名校验以及数据加解密涉及用户的会话密钥 session_key。 开发者应该事先通过 wx.login 登录流程获取会话密钥 session_key 并保存在服务器。为了数据不被篡改,开发者不应该把 session_key 传到小程序客户端等服务器外的环境。
数据签名校验
为了确保开放接口返回用户数据的安全性,微信会对明文数据进行签名。开发者可以根据业务需要对数据包进行签名校验,确保数据的完整性。
- 通过调用接口(如 wx.getUserInfo)获取数据时,接口会同时返回 rawData、signature,其中 signature = sha1( rawData + session_key )
- 开发者将 signature、rawData 发送到开发者服务器进行校验。服务器利用用户对应的 session_key 使用相同的算法计算出签名 signature2 ,比对 signature 与 signature2 即可校验数据的完整性。
如 wx.getUserInfo的数据校验:
接口返回的rawData:
{
"nickName": "Band",
"gender": 1,
"language": "zh_CN",
"city": "Guangzhou",
"province": "Guangdong",
"country": "CN",
"avatarUrl": "http://wx.qlogo.cn/mmopen/vi_32/1vZvI39NWFQ9XM4LtQpFrQJ1xlgZxx3w7bQxKARol6503Iuswjjn6nIGBiaycAjAtpujxyzYsrztuuICqIM5ibXQ/0"
}
用户的 session-key:
HyVFkGl5F5OQWJZZaNzBBg==
用于签名的字符串为:
{"nickName":"Band","gender":1,"language":"zh_CN","city":"Guangzhou","province":"Guangdong","country":"CN","avatarUrl":"http://wx.qlogo.cn/mmopen/vi_32/1vZvI39NWFQ9XM4LtQpFrQJ1xlgZxx3w7bQxKARol6503Iuswjjn6nIGBiaycAjAtpujxyzYsrztuuICqIM5ibXQ/0"}HyVFkGl5F5OQWJZZaNzBBg==
使用sha1得到的结果为
75e81ceda165f4ffa64f4068af58c64b8f54b88c
加密数据解密算法
接口如果涉及敏感数据(如wx.getUserInfo当中的 openId 和 unionId),接口的明文内容将不包含这些敏感数据。开发者如需要获取敏感数据,需要对接口返回的加密数据(encryptedData) 进行对称解密。 解密算法如下:
- 对称解密使用的算法为 AES-128-CBC,数据采用PKCS#7填充。
- 对称解密的目标密文为 Base64_Decode(encryptedData)。
- 对称解密秘钥 aeskey = Base64_Decode(session_key), aeskey 是16字节。
- 对称解密算法初始向量 为Base64_Decode(iv),其中iv由数据接口返回。
微信官方提供了多种编程语言的示例代码((点击下载)。每种语言类型的接口名字均一致。调用方式可以参照示例。
另外,为了应用能校验数据的有效性,会在敏感数据加上数据水印( watermark )
watermark参数说明:
如接口 wx.getUserInfo 敏感数据当中的 watermark:
{
"openId": "OPENID",
"nickName": "NICKNAME",
"gender": GENDER,
"city": "CITY",
"province": "PROVINCE",
"country": "COUNTRY",
"avatarUrl": "AVATARURL",
"unionId": "UNIONID",
"watermark":
{
"appid":"APPID",
"timestamp":TIMESTAMP
}
}
注:
- 解密后得到的json数据根据需求可能会增加新的字段,旧字段不会改变和删减,开发者需要预留足够的空间
会话密钥 session_key 有效性
开发者如果遇到因为 session_key 不正确而校验签名失败或解密失败,请关注下面几个与 session_key 有关的注意事项。
- wx.login 调用时,用户的 session_key 可能会被更新而致使旧 session_key 失效(刷新机制存在最短周期,如果同一个用户短时间内多次调用 wx.login,并非每次调用都导致 session_key 刷新)。开发者应该在明确需要重新登录时才调用 wx.login,及时通过 auth.code2Session 接口更新服务器存储的 session_key。
- 微信不会把 session_key 的有效期告知开发者。我们会根据用户使用小程序的行为对 session_key 进行续期。用户越频繁使用小程序,session_key 有效期越长。
- 开发者在 session_key 失效时,可以通过重新执行登录流程获取有效的 session_key。使用接口 wx.checkSession可以校验 session_key 是否有效,从而避免小程序反复执行登录流程。
- 当开发者在实现自定义登录态时,可以考虑以 session_key 有效期作为自身登录态有效期,也可以实现自定义的时效性策略。
后台代码示例
加密数据解密代码 (在微信小程序文档中下载的)
WXBizDataCrypt.py
import base64
import json
from Crypto.Cipher import AES
from app01.wx import settings class WXBizDataCrypt:
def __init__(self, appId, sessionKey):
self.appId = appId
self.sessionKey = sessionKey def decrypt(self, encryptedData, iv):
# base64 decode
sessionKey = base64.b64decode(self.sessionKey)
encryptedData = base64.b64decode(encryptedData)
iv = base64.b64decode(iv) cipher = AES.new(sessionKey, AES.MODE_CBC, iv) decrypted = json.loads(self._unpad(cipher.decrypt(encryptedData))) if decrypted['watermark']['appid'] != self.appId:
raise Exception('Invalid Buffer') return decrypted def _unpad(self, s):
return s[:-ord(s[len(s)-1:])] @classmethod
def getInfo(cls,encryptedData,iv,session_key): return cls(settings.AppId,session_key).decrypt(encryptedData, iv)
serializers.py
from rest_framework.serializers import ModelSerializer from app01 import models
class User_ser(ModelSerializer):
class Meta:
model=models.Wxuser
fields="__all__"
views.py
class GetInfo(APIView):
def post(self,request):
param=request.data
if param['encryptedData'] and param['iv'] and param['login_key']: #小程序传递的参数
openid,seesion_key=cache.get(param['login_key']).split("&")
data=WXBizDataCrypt.WXBizDataCrypt.getInfo(param['encryptedData'] ,param['iv'] ,seesion_key) #传入解密算法获取更多用户信息
save_data={ #用户信息
"name":data['nickName'],
"avatar":data['avatarUrl'],
"language":data['language'],
"province":data['province'],
"city":data['city'],
"country":data['country'],
}
models.Wxuser.objects.filter(openid=openid).update(**save_data) #存入后台数据库
data=models.Wxuser.objects.filter(openid=openid).first()
data=User_ser.User_ser(instance=data,many=False).data #serializer校验
return Response({"code":200,"msg":"缺少参数","data":data}) #用户信息传到小程序
else:
return Response({"code":200,"msg":"缺少参数"})
微信小程序的登入与授权的更多相关文章
- 微信小程序(一),授权页面搭建
wxml代码如下: <!--pages/index2/index2.wxml--> <view class="index2Container"> <i ...
- 微信小程序(二)登录授权实现
相对于上一节,这一节主要是动态获取数据,主要是对登陆信息的接收,以及页面获取授权按钮的相对相应(未授权时,显示,授权后不显示) 关键在于状态值的判断,以及对页面的不同响应(m-->v) wxml ...
- 微信小程序之wx.getLocation再次授权问题解决
首先,在page外定义一个公共函数用于发送获取位置的请求 var getLocation = function (that) { wx.getLocation({ type: 'wgs84', suc ...
- 微信小程序之登录连接django,以及用户的信息授权认证
小结: 1 如何自定义组件 - 组件和页面一样,也是由四个文件组成,所以我们自定义组件的时候,模拟pages文件夹,把所有的所有的组件都放在一个文件夹中,每个组件又由一个文件夹包裹,方便管理,在对应目 ...
- 微信小程序-用户拒绝授权使用 wx.openSetting({}) 重新调起授权用户信息
场景模拟:用户进入微信小程序-程序调出授权 选择拒绝之后,需要用到用户授权才能正常使用的页面,就无法正常使用了. 解决方法:在用户选择拒绝之后,弹窗提示用户 拒绝授权之后无法使用,让用户重新授权(微信 ...
- 微信小程序开发BUG经验总结
摘要: 常见的微信小程序BUG! 小程序开发越来越热,开发中遇到各种各样的bug,在此总结了一些比较容易掉进去的坑分享给大家. 1. new Date跨平台兼容性问题 在Andriod使用new Da ...
- 微信小程序登入流程
微信小程序登入流程 一.首先前端先传code去后端 wx.login({ success(res) { if (res.code) { //发起网络请求 wx.request({ url: app.g ...
- 完整微信小程序授权登录页面教程
完整微信小程序授权登录页面教程 1.前言 微信官方对getUserInfo接口做了修改,授权窗口无法直接弹出,而取而代之是需要创建一个button,将其open-type属性绑定getUseInfo方 ...
- 关于微信小程序拒绝授权后,重新授权并获取用户信息
最近公司做了一些有关微信小程序的项目,涉及到授权获取用户基本信息,但是在拒绝授权之后就不会再出现授权窗口: 看网上也有很多人遇到了同样的问题,所以记录下来我的处理方法,供大家和自己学习和记录: 当调用 ...
随机推荐
- STATUS_STACK_BUFFER_OVERRUN不一定是栈缓冲区溢出
STATUS_STACK_BUFFER_OVERRUN异常一般是指栈缓冲区溢出的溢出,代码为0xC0000409,消息提示一般为“Security check failure or stack buf ...
- PHP正则表达式提取html超链接中的href地址
$preg='/<a .*?href="(.*?)".*?>/is'; preg_match_all($preg,$str,$array2); ;$i<count ...
- 这里是DDOSvoid的blog
由于博主已经退役,博客现由 @一扶苏一 代为维护. DDOSvoid 在生前退役前写下了大量的 blog 存在本地,现在由他的弟子 一扶苏一 整理编纂成为题单慢慢上传,是为<论语>< ...
- udf也能用Python
具体步骤见<fluent加载第三方(C++,Fortran等)动态链接库> 我们对导入的动态链接库进行改动 打开VS2013 完成了上述过程以后,还需要配置Python 首先需要安装Pyt ...
- 百度编辑器(ueditor)踩坑,图片转存无法使用
在使用 百度编辑器 的过程中碰到了一些问题,图片转存功能无法使用, 即便是疯狂地在官方 Demo.文档.论坛甚至是 GitHub 上也没找到理想的答案.(┗|`O′|┛) (真是日了狗) 问题描述 默 ...
- `ll/sc` 指令在`linux`中的软件实现
load-link与store-conditional (LL/SC)是一对用于并发同步访问内存的CPU指令.Load-link返回内存位置处的当前值,随后的store-conditional在该内存 ...
- Java 面向对象(九)
常用类之Random Random类位于 java.util 包中,主要用于生成伪随机数 Random类将种子数作为随机算法的起源数字,计算生成伪随机数,其与生成的随机数字的区间无关 创建Random ...
- Spark(五十三):Spark RPC初尝试使用
基本用法主要掌握一点就行: master slave模式运用:driver 就是master,executor就是slave. 如果executor要想和driver交互必须拿到driver的Endp ...
- [LINUX] 快速回收连接
i /etc/sysctl.conf 编辑文件,加入以下内容:net.ipv4.tcp_syncookies = 1net.ipv4.tcp_tw_reuse = 1net.ipv4.tcp_tw_r ...
- Centos7搭建FTP服务详细过程
Centos7搭建FTP服务详细过程https://blog.csdn.net/sinat_30802291/article/details/81706152