小程序与laravel笔记
小程序与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捕获异常,重新定义渲染

6502

被折叠的 条评论
为什么被折叠?



