小程序与laravel笔记之用户登录token篇

小程序与laravel之用户登录token篇

后端laravel,前端采用微信小程序,关于登录模块,看文档及问度娘,加上写代码近一个周,终于搞出一点眉目。特记下来,别忘了。

环境配置

  • Laravel 8.16.1
  • 微信小程序基础库 2.14

本文注意事项

为了防止混淆。我把自己的服务器端称为laravel端。

关于Laravel Sanctum 与Possport

先看两个扩展包的介绍

Laravel Sanctum 为 SPA(Single Page Application,单页面应用)、移动 App 以及基于令牌的简单 API 提供了一个轻量级的认证系统。Sanctum 允许为应用的每个用户账户生成多个 API 令牌,这些令牌可用于授予权限/作用域来指定对应令牌允许执行的操作。

在 Laravel 中,实现基于传统表单的登陆和授权已经非常简单,但是如何满足 API 场景下的授权需求呢?在 API 场景里通常通过令牌来实现用户授权,而非维护请求之间的 Session 状态。在 Laravel 项目中使用 Passport 可以轻而易举地实现 API 授权认证,Passport 可以在几分钟之内为你的应用程序提供完整的 OAuth2 服务端实现。Passport 是基于由 Andy Millington 和 Simon Hamp 维护的 League OAuth2 server 建立的。

都挺好,但是我在Possport搞了好长一段时间,总是出问题。So~,果断转为Sanctum

关于Sanctum的安装

不多介绍,直接官方文档或学院君文档走起。

向users表中加字段

1、命令行

php artisan make:migration add_字段_to_表名_table --table=表名

2、在源码中找到\database\migrations\日期_add_表名.php

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class AddOpenidToUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->string('openid');
            $table->string('nick_name');
            $table->string('avatar_url');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('users', function (Blueprint $table) {
            //
        });
    }
}

3、运行命令

php artisan migrate

完活

关于从laravel端获取token

首先我们从微信端拿到code,把code传给laravel端,从laravel端向微信端发送获取openid的请求。

拿code

第1步,从微信端拿code,在微信小程序中进行。 我直接在utils目录下写了一个工具文件。

const wxlogin =()=>{
    return new Promise((resolve,rejct)=>{
        wx.login({
            timeout:10000,
            success:(result)=>{
                resolve(result)
            },
            fail:(error)=>{
                rejct(error)
            }
        })
    })
}
const wxGetUserInfo = ()=>{
    return new Promise((resolve,rejct)=>{
        wx.getUserInfo({
          success:(result)=>{
              resolve(result)
          },
          fail:(err)=>{
              rejct(err)
          }
        })
    })
}

module.exports = {
    wxlogin:wxlogin,
    wxGetUserInfo:wxGetUserInfo
}

将code发出去

第2步,将获取的code及相关信息发送到laravel端

  async handleGetWxuserInfo() {
    try {
      const {
        encryptedData,
        iv,
        rawData,
        signature,
        userInfo
      } = await wxGetUserInfo(); 
      const {nickName,avatarUrl}=userInfo;

      wx.setStorageSync('userinfo', userInfo);
      const {
        code
      } = await wxlogin(); //这个就是引用第1步写的代码里的功能

      

      const systemInfo = wx.getSystemInfoSync(); 
      const device_name = systemInfo.model; //scantum扩展包生成token时需要一个设备名称
      //下面的request方法,是我将wx.request进行了封装,基本上还是wx.request的功能
      const res = await request({
        url: 'sanctum/wxtoken', //这个就是laravel端获取token的路径
        data:{code,nickName,avatarUrl,device_name},
        method:'POST'
      });
      if(res.statusCode!=200){
        throw {errMsg:'网络错误'+res.statusCode};
      }else{
        wx.hideLoading()
        console.log(res);
        const {plainTextToken:access_token,expires_in,token_type} = res.data;
        if(access_token){
            wx.setStorageSync('access_token',access_token)          
            wx.reLaunch({
              url: '/pages/workbench/workbench',
            })
         
        }else{
          throw {errMsg:"没有从服务器获取token,请联系管理员"}
        }
      }
    } catch (error) {
      wx.hideLoading()
      wx.showModal({
        title: '出现问题',
        content: error.errMsg,
        showCancel: false
      })
    }
  }

利用code获取openid并返回token

第3步,在laravel端向微信端发送获取openid的请求

先整一个controller吧

php artisan make:controller UserController

UserController.php代码如下:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\User;
use Illuminate\Support\Facades\Hash;

class UserController extends Controller
{
    //
    public function getTokenByWxcode(Request $request)
    {
        $request->validate([
            'code' => 'required',
            'device_name' => 'required'
        ]);

        try {
            $param=array();
            $param[]='appid='.env('APPID'); //微信给开发者app的appid
            $param[]='secret='.env('SECRET'); //微信给开发者app的secret
            $param[]='js_code='.$request->code;
            $param[]='grant_type=authorization_code';
            $params=implode('&', $param);    //用&符号连起来
            $url = env('WECHAT_GET_OPEN_ID').'?'.$params;
            // 'https://api.weixin.qq.com/sns/jscode2session'
            //请求接口
            $client = new \GuzzleHttp\Client([
                'timeout' => 60
            ]);
            $res = $client->request('GET', $url);
            //openid和session_key
            $data = json_decode($res->getBody()->getContents(), true);
        } catch (\Throwable $th) {
            //throw $th;
        }     
       
        $result = User::where('openid', $data['openid'])->first();
        
        if ($result==null) { //首次登录,存openid及一些信息
            $user = new User;
            $user->name = $data['openid']; //将openid给用户名,以后可以判断让用户自己修改
            $user->email = time().'@370785.top';//给首次登录的用户虚拟一个邮箱地址
            $user->openid = $data['openid'];
            $user->password=Hash::make($data['openid']);
            $user->nick_name = $request->nickName;
            $user->avatar_url = $request->avatarUrl;
            $user->save(); 
            //scantum的优势就是简单,就在实例中直接createtoken就可以出来一个token
            return $user->createToken($request->device_name);

        } elseif ($result->nick_name !=null) { //非首次登录,且已授权,存用户信息
            
            return $result->createToken($request->device_name);
        }
        return $data; //这个返回值毫无意义,先留在这吧
    }
}

routes/web.php加上

use App\Http\Controllers\UserController;

Route::Post('/sanctum/wxtoken',[UserController::class, 'getTokenByWxcode']);

代码到这,几乎完成,可能会出419错误,可见下节。

但流程还没有结束,再回过头看第2步的代码,就有将token存缓存等操作了。

当然这个controller还有优化的空间,第一次使用laravel难免还存在一些问题。

关于419错误

在发送Post请求时,出现419错误。

因为默认有csrf验证。

所要关闭一些,上app\Http\Middleware\VerifyCsrfToken.php代码

<?php

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;

class VerifyCsrfToken extends Middleware
{
    /**
     * The URIs that should be excluded from CSRF verification.
     *
     * @var array
     */
    protected $except = [
        //
        '/sanctum/wxtoken',//这个路径为我不想进行验证的路由
    ];
}

关于Route [login] not defined 报错

这小节是从某csdn大神中获取来的,是我在折腾possport扩展包时遇到的问题。不舍得删除,留着吧。

Laravel Passport API token 验证,出现 Route [login] not defined 报错。报错显示确实不太友好,容易把错误引入到其他方向。

我刚开始看,也没看明白,不知道这个错误是啥,laravel 不应该强制用户定义 login 路由啊。通过测试,才知道是未登录导致的错误。

然后,根据 laravel 的报错提醒,我们简单追下源码,分析下问题:

错误位置:

1.laravel/framework/src/Illuminate/Routing/UrlGenerator.php:389
调用 route() 方法根据路由名 login,生成 url 报错
2.route() 方法的调用位置
app/Http/Middleware/Authenticate.php:18

 protected function redirectTo($request)
 {
    if (! $request->expectsJson()) {
	    return route('login');
    }
}

3.我们在控制器的构造方法中,使用了 passport 的中间件

$this->middleware('auth:api', ['except' => []]);

从这里,基本就可以看出,就是调用中间件的 redirectTo() 方法,因为没定义 ‘login’ 路由导致了错误

分析:

$request->expectsJson() - 期望返回 json 格式,如果未返回,就返回要重定向的链接地址
这里,我们使用 passport api,所以很明显的是 $request->expectsJson() 未通过导致的原因
继续看 request 的 expectsJson() 方法

public function expectsJson() {
   return ($this->ajax() && ! $this->pjax() && $this->acceptsAnyContentType()) || $this->wantsJson();
}

涉及了几个方法:ajax()pjax()acceptsAnyContentType()wantsJson()
都是检测是否是返回 json 格式,具体自己看

最终解决方法:

api 请求 header 添加:

Accept: application/json

然后就会抛出 AuthenticationException 异常,我们可以在 app/Exceptions/Handler.php捕获异常,重新定义渲染

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值