文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

【Uniapp】小程序携带Token请求接口+无感知登录方案2.0

2023-09-02 14:48

关注

本次改进原文《【Uniapp】小程序携带Token请求接口+无感知登录方案》,在实际使用过程中我发现以下bug

  1. 若token恰好在用户访问接口时到期,就会直接查询为空,不反映token过期问题(例如:弹窗显示订单查询记录为空),并不是因为没有数据而是因为token过期了,接口返回了但是没有在前端显示
  2. token过期后需要重新启动小程序,才会获取到新的token
  3. 获取到token后,原接口不会继续请求,页面页面空白没有任何数据,数据需要下一次刷新才有

问题演示如下:

【审核中】

目录

在这里插入图片描述

吐槽

写本篇的原因是因为之前开发用的都不是微信小程序给的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 是什么?

token 顾名思义就是令牌,也就是一种身份标志。用于和服务器确定身份,它具有时效性,超过有效时间身份标志就会失效。
在这里插入图片描述

设计思路(点击方法可跳转原文档)

通过小程序客户端发起的**wx.login()** 获取临时登录凭证code ,并回传到开发者服务器,通过微信提供的 auth.code2Session 接口,换取 用户唯一标识 openid、 会话密钥 session_key。并通过以session_key为名,openid为值将数据存放到redis中,在这里我将时间设置为48h

  1. 若服务端token失效,客户端登陆状态也会失效,失效后重新登陆执行上述步骤;
  2. 若客户端checkSession失效或者本地数据缓存失效,则也会重新登录
    上述两个步骤保证小程序端的token都是最新的,缺点是不能及时性作废原先在服务器存储的数据只能等redis过期

以上设计逻辑思路满足下图:
在这里插入图片描述

操作流程

后端代码

以Thinkphp5.0.24为案例(20230614新增控制器写法,原来写的是原生php,二选一,建议第一种)

TP控制器登录接口方法(建议)

   //登录接口    //  http://code.taila.club/index.php/index/api/login    // 目录\控制器\方法    public function login()    {         $code=input('code');        if ($code) {            // 存在记录            // $res=Db::table("sys")->where("id","1")->find();                        // $appid=$res["val1"];//小程序id            // $secret=$res["val2"];//密钥            $appid="w***********e";            $secret="6***************6";            $code=$_GET['code'];            $url="https://api.weixin.qq.com/sns/jscode2session?appid=$appid&secret=$secret&js_code=$code&grant_type=authorization_code";             $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,            'data' => '',            'msg' => '请求出错!'        ),480));    } else {        // 打印返回的内容        $result=json_decode($data,true);        if (array_key_exists("errcode",$result))          {        //   echo "键存在!";                die(        json_encode(            array(            'code' => 100,            'data' => '',            'msg' => '获取token失败!'.$result['errmsg']        ),480));                  }        else          {        //   echo "键不存在!";              // 开启redius                //写入redius session_key名命的openid数据 默认存储2天        curl_close($curl);        $Redis=new Redis();        $Redis->set($result['session_key'],$result['openid'],24*60*60*2);         die(        json_encode(            array(            'code' => 200,            'data' => $result,            'msg' => '获取token成功'        ),480));          }                 }                  } else {            // 已被处理或者不存在 请求重新登陆            die(        json_encode(            array(            'code' => 100,            'data' => '',            'msg' => '非法操作'        ),480));        }                       } 
  1. 在public文件夹创建php文件access_token.php(不建议)
    用于接收前端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));          }                 }} ?>
  1. 在tp框架中(application/index/controller)新建Api.php控制器
    用来检测服务器端的token是否存在,以便于让小程序做出重新登录操作
<?phpnamespace 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));        }                       }}

前端小程序封装📦代码

  1. 客户端封装
/** * 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打包成微信小程序运行后

1、不存在本地缓存、不存在redis记录 演示

2、不存在本地缓存演示

在这里插入图片描述

3、服务器端不存在redis记录 演示

以上已经对所有的可能做了一个实验,除了【更新新的token后,上次的token并不能及时失效】这个问题,找不到其他毛病了

如何进行token鉴权

还是看演示吧

服务器端验证token是否有效只需要对token进行查询即可,存在即为成功,直接取出openid书写业务逻辑代码,失败让小程序重新登陆,这些根据返回码即可

<?php// 访问路由 https://***/index.php/index/Api/indexnamespace 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));       }}}

发起正确的请求

在这里插入图片描述
如果在后面加了一个1
在这里插入图片描述

thinkphp5 redis补充

$redis->set('name','value','3600');//添加记录前两个分别表示名和值,后者单位秒$redis->get($session_key);//根据名查询值

2.0改进方案

在上述测试中发现了以下问题:

  1. 若token恰好在用户访问接口时到期,就会直接查询为空,不反映token过期问题(例如:弹窗显示订单查询记录为空),并不是因为没有数据而是因为token过期了,接口返回了但是没有在前端显示
  2. token过期后需要重新启动小程序,才会获取到新的token
  3. 获取到token后,原接口不会继续请求,页面页面空白没有任何数据,数据需要下一次刷新才有

测试过程:
通过删除redis中的记录使token提前到期测试

改进思路:

代码解释: 告知用户token失效,自动获取,然后获取后调用自身方法传递刚刚的参数重新执行

else if(res.data.code==400){that.$token.toast("token已过期,正在获取",1500)that.$token.check_token();setTimeout(function() {that.get_book_detail(id);}, 1200);}

完整代码示例:(参考下方代码就明白了)

get_book_detail(id){//获取图书详情数据库let that=this;that.$token.request("index.php/index/Api/query_book_details","POST",{token:uni.getStorageSync('token'),id:id}).then(res => {console.log(res.data)if (res.data.code==200) {that.$token.toast(res.data.msg,1500)that.book_data=res.data.data;that.get_book_detail_api(res.data.data.book_isbn)// 由于速度更不上,查询大概在2-3s,现在做了调整//that.htmlContent=res.data.data.details.contentIntro.c;// 轮播图var swiper=[res.data.data.book_cover// res.data.data.details.coverImage.middle];that.swiper=swiper;// that.showPopup2 = true;//开启浮窗} else if(res.data.code==400){that.$token.toast("token已过期,正在获取",1500)that.$token.check_token();setTimeout(function() {that.get_book_detail(id);}, 1200);}else{that.$token.toast("未查询到结果",1500)}})},

成功演示如下:
在这里插入图片描述

总结

以上就是今天对uniapp结合微信小程序携带Token请求接口无感知的登录方案,如果您喜欢请收藏起来!

来源地址:https://blog.csdn.net/qq_35230125/article/details/131205079

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-移动开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯