写本篇的原因是因为之前开发用的都不是微信小程序给的session作为token鉴权的,这次开发打算使用多端同步的uniapp开发小程序,方便后面转多端,所以我想尝试新的东西,另外在热榜中我看到一篇文章用"
access_token
作为token来请求验证接口、checkSession用来检测access_token有无过期",不得不使我感叹,现在的技术er这么差了吗?简直就是误人子弟!!
我们来说说为什么不能用access_token作为token
- 【官方回答】access_token 是小程序全局唯一后台接口调用凭据,调用绝大多数后台接口时都需使用。开发者可以通过 getAccessToken 接口获取并进行妥善保存。 -【官方回答】
获取小程序全局唯一后台接口调用凭据,token有效期为7200s,开发者需要进行妥善保存。
所以说,access_token 只是用来调用一些微信提供的api服务的,并且access_token 只有两个小时,你把access_token当作小程序的token?不仅不满足暴露这个问题,时间上也有限制
我们再来说说checkSession是用来检测什么的?
- 登录态过期后开发者可以再调用 wx.login 获取新的用户登录态。调用成功说明当前 session_key 未过期,调用失败说明 session_key 已过期。
所以!checkSession是用来检测session_key而不是access_token的,access_token是根据小程序的appid和secret确定的,没有单一用户代表性
token 顾名思义就是令牌,也就是一种身份标志。用于和服务器确定身份,它具有时效性,超过有效时间身份标志就会失效。
通过小程序客户端发起的**wx.login()** 获取临时登录凭证code ,并回传到开发者服务器,通过微信提供的 auth.code2Session 接口,换取 用户唯一标识 openid、 会话密钥 session_key。并通过以session_key为名,openid为值将数据存放到redis中,在这里我将时间设置为48h
上述两个步骤保证小程序端的token都是最新的,缺点是不能及时性作废原先在服务器存储的数据只能等redis过期
以Thinkphp5.0.24为案例
用于接收前端wx.login方法获得的code换回openid和session_key,并通过以session_key为名,openid为值将数据存放到redis中,在这里我将时间设置为48h
<?php
//小程序登录
$appid="";//小程序id
$secret="";//密钥
$code=$_GET['code'];
curl_get("https://api.weixin.qq.com/sns/jscode2session?appid=$appid&secret=$secret&js_code=$code&grant_type=authorization_code");
function curl_get($url){
$header = array(
'Accept: application/json',
);
$curl = curl_init();
//设置抓取的url
curl_setopt($curl, CURLOPT_URL, $url);
//设置头文件的信息作为数据流输出
curl_setopt($curl, CURLOPT_HEADER, 0);
// 超时设置,以秒为单位
curl_setopt($curl, CURLOPT_TIMEOUT, 1);
// 超时设置,以毫秒为单位
// curl_setopt($curl, CURLOPT_TIMEOUT_MS, 500);
// 设置请求头
curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
//设置获取的信息以文件流的形式返回,而不是直接输出。
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
//执行命令
$data = curl_exec($curl);
// 显示错误信息
if (curl_error($curl)) {
print "Error: " . curl_error($curl);
die(
json_encode(
array(
'code' => 100,
'msg' => '请求出错!'
),480));
} else {
// 打印返回的内容
$result=json_decode($data,true);
if (array_key_exists("errcode",$result))
{
// echo "键存在!";
if ($result['errcode']==0) {
// code...
// 开启redius
ini_set('session.save_handler', 'redis');
ini_set('session.save_path', 'tcp://127.0.0.1:6379');
$redis = new redis();
$redis->connect('127.0.0.1', 6379);
//写入redius session_key名命的openid数据 默认存储2天
curl_close($curl);
$redis->set($result['session_key'],$result['openid'],24*60*60*2);
die(
json_encode(
array(
'code' => 200,
'msg' => $result
),480)
);
} else {
die(
json_encode(
array(
'code' => 100,
'msg' => '获取token失败!'.$result['errmsg']
),480));
}
}
else
{
// echo "键不存在!";
die(
json_encode(
array(
'code' => 100,
'msg' => '获取token失败'
),480));
}
}
}
?>
用来检测服务器端的token是否存在,以便于让小程序做出重新登录操作
<?php
namespace app\index\controller;
use think\Db;
use think\cache\driver\Redis;
use app\index\controller\Base;
class Api extends Base
{
// 验证session_key是否过期(服务器默认48h,到期后自动删除,查询不到表示过期)
public function check_session()
{
$session_key=input('session_key');
$redis = new Redis();
//读取数据
$result= $redis->get($session_key);
if ($result) {
// 存在记录
die(
json_encode(
array(
'code' => 200,
'msg' => 'token验证通过'
),480)
);
} else {
// 已被处理或者不存在 请求重新登陆
die(
json_encode(
array(
'code' => 100,
'msg' => 'token验证失败,请重新登录'
),480)
);
}
}
}
/**
* token.js,全局校验方法,可以自己补充
*/
export default {
login: function(session) {
let that = this;
uni.showLoading({
title: '登录中...'
});
uni.login({
provider: 'weixin',
success: loginRes => {
console.log(loginRes);
that.code = loginRes.code;
// 将用户登录code传递到后台置换用户SessionKey、OpenId等信息
uni.request({
url: 'https://serverhost/wx_token.php', //仅为示例,并非真实接口地址。
data: {
code:loginRes.code
},
method: 'GET',
header: {
'content-type': 'application/x-www-form-urlencoded' //自定义请求头信息
},
success: (res) => {
console.log(res.data);
console.log(res.data.msg.session_key)
uni.hideLoading()
//存取session
uni.setStorageSync('session', res.data.msg.session_key);
//openid只需要服务通过session请求redis即可
}
});
},
fail: () => {
uni.showToast({ title: '获取 code 失败', icon: 'none' });
}
});
},
//检测token 每次发起业务请求检测即可
check_token: function(session) {
let that=this;
//微信检测
uni.checkSession({
success () {
//session_key 未过期,并且在本生命周期一直有效
console.log("未过期")
//没有过期在判断下存储是否存在 后需提交业务需要用到
const session = uni.getStorageSync('session');
if (session=='') {
console.log("session不存在");
that.login()
}else{
//检测服务器的
console.log("session存在-校验合法性");
//验证检测服务器session有效性
uni.request({
url: 'https://serverhost/index.php/index/Api/check_session', //仅为示例,并非真实接口地址。
data: {
session_key:session
},
method: 'POST',
header: {
'content-type': 'application/x-www-form-urlencoded' //自定义请求头信息
},
success: (res) => {
if (res.data.code!=200)
{
//服务器token已过期 重新登录
console.log("服务器token已过期 重新登录");
that.login()
}else{
console.log("服务器token有效");
}
}
});
}
},
fail () {
// session_key 已经失效,需要重新执行登录流程
console.log("session过期")
that.login()
}
})
}
}
import token from '@/sdk/token.js'
// 挂载到全局
Vue.prototype.$token = token
this.$token.check_token()
uniapp打包成微信小程序运行后
以上已经对所有的可能做了一个实验,除了【更新新的token后,上次的token并不能及时失效】这个问题,找不到其他毛病了
前端小程序每次发起业务请求时,先调用一次封装好的【check_token】用于检查本地有误存储token、token是否已经过期(微信决定)、服务器端redis是否存在(不存在没必要发起,因为还是会被拒绝)
服务器端验证token是否有效只需要对token进行查询即可,存在即为成功,直接取出openid书写业务逻辑代码,失败让小程序重新登陆,这些根据返回码即可
还是看演示吧
服务器端验证token是否有效只需要对token进行查询即可,存在即为成功,直接取出openid书写业务逻辑代码,失败让小程序重新登陆,这些根据返回码即可
<?php
// 访问路由 https://***/index.php/index/Api/index
namespace app\index\controller;
use think\Db;
use think\cache\driver\Redis;
use app\index\controller\Base;
class Index extends Base
{
public function index()
{
// 实例 获取openid
$token=input('token');
$redis = new Redis();
$result= $redis->get($token);
if ($result) {
die(
json_encode(
array(
'code' => 200,
'data'=>$result,
'msg' => '数据请求成功'
),480)
);
} else {
die(
json_encode(
array(
'code' => 100,
'data'=>'',
'msg' => 'token失效或不存在!请重新获取'
),480)
);
}
}
}
发起正确的请求
$redis->set('name','value','3600');//添加记录前两个分别表示名和值,后者单位秒
$redis->get($session_key);//根据名查询值
以上就是今天对uniapp结合微信小程序携带Token请求接口无感知的登录方案,如果您喜欢请收藏起来!