文章目录
- 1.自定义分享接口信息([文档](https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#62))
- 2.判断用户是否关注了公众号([文档](https://developers.weixin.qq.com/doc/offiaccount/User_Management/Get_users_basic_information_UnionID.html#UinonId))
- 3.获取用户信息([文档](https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html))
目前我用到的接口涉及到两个token和一个ticket。
access_token用于服务器端的请求鉴权,ticket用于前端的请求鉴权。这两个需要缓存。
获取用户信息这个接口有一个单独的access_token。
1.自定义分享接口信息(文档)
前端调用wx.conifg申请自定义分享这类的接口之前,需要先反问我们后端获取一些信息。
wx.config({
debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: ‘’, // 必填,公众号的唯一标识
timestamp: , // 必填,生成签名的时间戳
nonceStr: ‘’, // 必填,生成签名的随机串
signature: ‘’,// 必填,签名
jsApiList: [] // 必填,需要使用的JS接口列表
});
其中的signature就需要获取一个ticket的信息。
jsapi_ticket
生成签名之前必须先了解一下jsapi_ticket,jsapi_ticket是公众号用于调用微信JS接口的临时票据。
1.正常情况下,jsapi_ticket的有效期为7200秒,通过access_token来获取。由于获取jsapi_ticket的api调用次数非常有限,频繁刷新jsapi_ticket会导致api调用受限,影响自身业务,开发者必须在自己的服务全局缓存jsapi_ticket 。
参考以下文档获取access_token(有效期7200秒,开发者必须在自己的服务全局缓存access_token):
https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html
2.用第一步拿到的access_token 采用http GET方式请求获得jsapi_ticket(有效期7200秒,开发者必须在自己的服务全局缓存jsapi_ticket):
https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi
我理解token和ticket的区别是这样的:
token: 用于服务端的请求验证。
ticket: 用于前端的请求验证。
由于这里的config是前端需要使用的,所以我们需要先申请token。微信的服务器验证我们的服务器和公众号的关系后。再使用我们前端上传的信息去申请ticket给前端。前端再拿着ticket作为令牌去找微信服务器申请相关的JS-SDK权限。
从后端的视角来看,你需要做的是:
我的django代码给大家参考,我没有用redis就直接用文件存储了,真正需要上线的代码用redis用redis可能方便一些:
# 请求处理函数
def weixin_config(request):
url = request.GET['url']
config_info = create_string(url)
config_info['app_id'] = settings.WEIXINAPI["appID"]
json_str = json.dumps(config_info, ensure_ascii=False)
res = HttpResponse(json_str, content_type="application/json")
res.set_cookie('jsapi_ticket', json_str)
return res
# 生成返回字符串
def create_string(url):
random_string = randomone('16')
times = int(time.time()/1000)
ticket = get_ticket()
arg_string = "jsapi_ticket=%s&noncestr=%s×tamp=%s&url=%s" % (ticket, random_string, times, url)
m = hashlib.sha1()
m.update(arg_string.encode('utf8'))
sign = m.hexdigest()
return {'timestamp': times, 'nonceStr': random_string, 'signature': sign}
# 刷新token函数
def flush_token():
token_url = settings.WEIXINAPI["tokenUrl"]
appID = settings.WEIXINAPI["appID"]
appwd = settings.WEIXINAPI["appwd"]
url = "%sgrant_type=client_credential&appid=%s&secret=%s" % (token_url, appID, appwd)
ssl._create_default_https_context = ssl._create_unverified_context
jsondata = urllib.request.urlopen(url).read().decode()
json_dic = json.loads(jsondata)
with open("token.txt", 'w') as f:
json.dump(json_dic, f)
return json_dic["access_token"]
# 刷新ticket函数
def flush_ticket(access_token):
ticket_url = settings.WEIXINAPI["ticketUrl"]
url = '%saccess_token=%s&type=jsapi' % (ticket_url, access_token)
jsondata = urllib.request.urlopen(url).read().decode()
json_dic = json.loads(jsondata)
json_dic["expires_time"] = time.time()
with open("ticket.txt", 'w') as f:
json.dump(json_dic, f)
return json_dic["ticket"]
# ticket7200秒超时
# ticket用于前端调用微信js-sdk接口的凭据
def get_ticket():
if os.path.exists("ticket.txt"):
with open('ticket.txt', 'r') as f:
ticket_dic = json.load(f)
expires_time = ticket_dic['expires_time']
now_time = time.time()
if now_time - expires_time >= 7200:
#更新ticket
access_token = get_token()
ticket = flush_ticket(access_token)
else:
ticket = ticket_dic["ticket"]
else:
access_token = get_token()
ticket = flush_ticket(access_token)
return ticket
# token也是7200秒超时
# ticket用于后台调用微信接口的凭据
def get_token():
if os.path.exists("token.txt"):
with open('token.txt', 'r') as f:
ticket_dic = json.load(f)
expires_time = ticket_dic['expires_time']
now_time = time.time()
if now_time - expires_time >= 7200:
#更新ticket
access_token = flush_token()
else:
access_token = ticket_dic["access_token"]
else:
access_token = flush_token()
return access_token
def randomone(n):
string = ''
if n.isdigit():
for i in range(int(n)):
if i%2 != 0:
tmp = random.randint(0,9)
else:
tmp = str(chr(random.randint(97, 122)))
string = "%s%s" % (string, tmp)
return string
2.判断用户是否关注了公众号(文档)
接口有三个参数,第一个access_token就是第一点里面的token了。
你看这个接口是后台的请求接口,所以他就不需要再使用ticket,只要用token就行了。
openid的获取可以看第三点。
返回结果中的subscribe
标识用户是否订阅该公众号标识,值为0时,代表此用户没有关注该公众号,拉取不到其余信息。如果是1就说明关注了该公众号。
3.获取用户信息(文档)
这个接口由于非常常用,而且命名和上面的token是一样的,也叫access_token
。也是我一直搞混的原因。
这里通过code换取的是一个特殊的网页授权access_token,与基础支持中的access_token(该access_token用于调用其他接口)不同。公众号可通过下述接口来获取网页授权access_token。如果网页授权的作用域为snsapi_base,则本步骤中获取到网页授权access_token的同时,也获取到了openid,snsapi_base式的网页授权流程即到此为止。
这里就直接上代码了,不废话:
前端:
①重定向至授权界面
var now_href_str = window.location.href ;
var url = 'http://open.weixin.qq.com/connect/oauth2/authorize?appid=APP_ID&redirect_uri=' + encodeURI(now_href_str) + '&response_type=code&scope=snsapi_userinfo&state=123#wechat_redirect'
window.location.href = url;
②带上code去后台进行验证
const res = await get(url , {params: { 'code': code }});
后端:
# 获取个人信息接口
# 该接口和公众号后台的token不同,获取的是单独的access_token
# 文档:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html
def get_user_info(request):
if request.method == 'GET':
code = request.GET['code']
access_dict = get_accesstoken(code)
data_dict = {}
try:
# 尝试返回值中有没有errcode,如果有说明token获取失败。
errcode = access_dict['errcode']
data_dict['code'] = 300
data_dict['msg'] = access_dict
res = HttpResponse(json.dumps(data_dict, ensure_ascii=False), content_type="application/json")
return res
except:
logger.info('获取token正常')
# 获取原请求中的认证方式
data_dict['scope'] = access_dict['scope']
# 开始请求用户信息
if access_dict['scope'] == 'snsapi_userinfo':
user_dict = token_get_user_info(access_dict['access_token'], access_dict['openid'])
data_dict['code'] = 200
# 具体信息可以看下面这个地址
# https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html#3
data_dict['openid'] = user_dict['openid']
data_dict['nickname'] = user_dict['nickname']
data_dict['headimgurl'] = user_dict['headimgurl']
data_dict['sex'] = user_dict['sex']
elif access_dict['scope'] == 'snsapi_base':
data_dict['code'] = 201
data_dict['openid'] = access_dict['openid']
res = HttpResponse(json.dumps(data_dict, ensure_ascii=False), content_type="application/json")
return res
def get_accesstoken(code):
appID = settings.WEIXINAPI["appID"]
appwd = settings.WEIXINAPI["appwd"]
url = 'https://api.weixin.qq.com/sns/oauth2/access_token?appid={}&secret={}&code={}&grant_type=authorization_code'
url = url.format(appID, appwd, code)
jsondata = urllib.request.urlopen(url).read().decode()
json_dic = json.loads(jsondata)
return json_dic
def token_get_user_info(access_token, openid):
url = 'https://api.weixin.qq.com/sns/userinfo?access_token={}&openid={}&lang=zh_CN'
url = url.format(access_token, openid)
jsondata = urllib.request.urlopen(url).read().decode()
json_dic = json.loads(jsondata)
return json_dic