thinkphp8结合jwt与微信小程序接口鉴权

环境:debian13\php8.3\tp8; jwt版本:7.1;mySQL:8.4/9.7

bash 复制代码
composer require firebase/php-jwt
sql 复制代码
CREATE TABLE `user` (
    `id` int unsigned NOT NULL AUTO_INCREMENT,
    `openid` varchar(64) NOT NULL DEFAULT '',
    `nickname` varchar(100) NOT NULL DEFAULT '',
    `avatar` varchar(255) NOT NULL DEFAULT '',
    `created_at` datetime DEFAULT NULL,
    `updated_at` datetime DEFAULT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_openid` (`openid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
php 复制代码
#app/api/route/app.php

<?php

declare(strict_types=1);

use think\facade\Route;

Route::post('login', 'Auth/login');
Route::get('user', 'Auth/user');
php 复制代码
# app/api/config/jwt.php

<?php

declare(strict_types=1);

return [
    'key'       => env('JWT.KEY', ''),
    'algorithm' => 'HS256',
    'expire'    => 7200,
    'issuer'    => 'wechat-mini-program',
];

# app/api/config/wechat.php
<?php

declare(strict_types=1);

return [
    'app_id'     => env('WECHAT.APP_ID', ''),
    'app_secret' => env('WECHAT.APP_SECRET', ''),
];
php 复制代码
# app/api/controller/Auth.php

<?php

declare(strict_types=1);

namespace app\api\controller;

use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use think\facade\Db;
use think\facade\Http;
use think\Request;
use think\response\Json;
use Throwable;

class Auth
{
    /**
     * 微信小程序登录
     */
    public function login(Request $request): Json
    {
        $code = trim((string) $request->post('code', ''));

        if ($code === '') {
            return json([
                'code' => 400,
                'msg'  => '缺少微信登录 code',
                'data' => null,
            ], 400);
        }

        try {
            // 使用 code 换取 openid 和 session_key
            $response = Http::get(
                'https://api.weixin.qq.com/sns/jscode2session',
                [
                    'appid'      => config('wechat.app_id'),
                    'secret'     => config('wechat.app_secret'),
                    'js_code'    => $code,
                    'grant_type' => 'authorization_code',
                ]
            );

            $wechatData = $response->json();

            if (empty($wechatData['openid'])) {
                return json([
                    'code'  => 400,
                    'msg'   => '微信登录失败',
                    'data'  => null,
                    'error' => $wechatData,
                ], 400);
            }

            $openid = $wechatData['openid'];

            // 查询本地用户
            $user = Db::name('user')
                ->where('openid', $openid)
                ->find();

            // 第一次登录,创建本地用户
            if (!$user) {
                $userId = Db::name('user')->insertGetId([
                    'openid'     => $openid,
                    'created_at' => date('Y-m-d H:i:s'),
                    'updated_at' => date('Y-m-d H:i:s'),
                ]);

                $user = [
                    'id'     => $userId,
                    'openid' => $openid,
                ];
            }

            $now = time();

            $payload = [
                'iss' => config('jwt.issuer'),
                'iat' => $now,
                'nbf' => $now,
                'exp' => $now + (int) config('jwt.expire'),

                // JWT 中保存本地用户 ID
                'sub' => (string) $user['id'],

                'user' => [
                    'id' => $user['id'],
                ],
            ];

            $token = JWT::encode(
                $payload,
                config('jwt.key'),
                config('jwt.algorithm')
            );

            return json([
                'code' => 0,
                'msg'  => '登录成功',
                'data' => [
                    'token'      => $token,
                    'token_type' => 'Bearer',
                    'expires_in' => config('jwt.expire'),
                    'user_id'    => $user['id'],
                ],
            ]);
        } catch (Throwable $exception) {
            return json([
                'code'  => 500,
                'msg'   => '服务器处理失败',
                'data'  => null,
                'error' => $exception->getMessage(),
            ], 500);
        }
    }

    /**
     * 验证 JWT
     */
    public function user(Request $request): Json
    {
        $authorization = $request->header('authorization', '');

        if (!preg_match('/^Bearer\s+(.+)$/i', $authorization, $matches)) {
            return json([
                'code' => 401,
                'msg'  => '请传入 Bearer Token',
                'data' => null,
            ], 401);
        }

        try {
            $token = trim($matches[1]);

            $decoded = JWT::decode(
                $token,
                new Key(
                    config('jwt.key'),
                    config('jwt.algorithm')
                )
            );

            $userId = (int) $decoded->sub;

            $user = Db::name('user')
                ->where('id', $userId)
                ->field('id,nickname,avatar')
                ->find();

            if (!$user) {
                return json([
                    'code' => 404,
                    'msg'  => '用户不存在',
                    'data' => null,
                ], 404);
            }

            return json([
                'code' => 0,
                'msg'  => '获取成功',
                'data' => $user,
            ]);
        } catch (Throwable $exception) {
            return json([
                'code'  => 401,
                'msg'   => 'Token 无效或已经过期',
                'data'  => null,
                'error' => $exception->getMessage(),
            ], 401);
        }
    }
}
javascript 复制代码
// utils/request.js

const BASE_URL = 'https://你的域名/api';

export function request({
    url,
    method = 'GET',
    data = {},
    auth = true
}) {
    const token = wx.getStorageSync('token');

    return new Promise((resolve, reject) => {
        wx.request({
            url: `${BASE_URL}${url}`,
            method,
            data,
            header: {
                'content-type': 'application/json',
                ...(auth && token
                    ? { Authorization: `Bearer ${token}` }
                    : {})
            },
            success: ({ statusCode, data: responseData }) => {
                if (statusCode === 401) {
                    wx.removeStorageSync('token');

                    wx.showToast({
                        title: '登录已过期,请重新登录',
                        icon: 'none'
                    });

                    reject(responseData);
                    return;
                }

                if (statusCode < 200 || statusCode >= 300) {
                    reject(responseData);
                    return;
                }

                resolve(responseData);
            },
            fail: reject
        });
    });
}
javascript 复制代码
// 调用登录接口:
import { request } from '../../utils/request';

wx.login({
    success: async ({ code }) => {
        const result = await request({
            url: '/login',
            method: 'POST',
            data: { code },
            auth: false
        });

        wx.setStorageSync('token', result.data.token);
    }
});

// 调用需要登录的接口:
import { request } from '../../utils/request';

Page({
    async onLoad() {
        try {
            const result = await request({
                url: '/user'
            });

            console.log(result.data);
        } catch (error) {
            console.error(error);
        }
    }
});
相关推荐
黄华SJ520it11 小时前
二二复制定点裂变双轨商城系统开发:原理、架构与实战指南
运维·小程序·架构·系统开发
驳是11 小时前
入坑 UniApp 写小程序,一些感受
微信小程序
小码哥06814 小时前
2026陪诊小程序与APP开发技术分析
大数据·人工智能·小程序
00后程序员张16 小时前
使用Instruments工具深入分析iOS应用性能与启动时间优化
android·macos·ios·小程序·uni-app·cocoa·iphone
程序喵大人18 小时前
【C++进阶】STL容器与迭代器 - 10 把容器、迭代器和数据流串成一个小程序
开发语言·c++·容器·小程序·迭代器·stl
不如摸鱼去2 天前
Wot UI 2.3.0 发布:二维码组件来了,Open Wot 与 wot-starter 同步更新
前端·ui·微信小程序·前端框架·uni-app
2501_916007472 天前
Python实现HTTPS爬虫的完整指南:使用requests、BeautifulSoup、Selenium和Scrapy
爬虫·python·ios·小程序·https·uni-app·iphone
FungLeo2 天前
Taro 开发小程序, 把 @ 配成 src 路径别名,webpack / tsconfig / scss 三处同步的正确姿势
webpack·小程序·taro
万亿少女的梦1682 天前
基于微信小程序、Express与MongoDB的校园失物招领系统设计
mongodb·微信小程序·node.js·express·系统设计
灵枢时代2 天前
小程序在生鲜配送行业,如何设计夜间预约和次日达的订单处理流程?
数据结构·人工智能·小程序