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);
        }
    }
});
相关推荐
这是个栗子4 小时前
uni-app微信小程序开发:高频核心 API(三)
微信小程序·小程序·uni-app
黄华SJ520it1 天前
预约上门系统开发:懒人经济下的商业机遇与技术实践
小程序·系统开发
软件技术新观察2 天前
2026年北京教育医疗小程序与APP定制开发:十大服务商实力测评
大数据·小程序
万岳科技程序员小金2 天前
真人数字人小程序如何开发?AI数字人平台搭建流程全面解析
人工智能·小程序·ai数字人系统源码·ai数字人平台搭建·ai数字人小程序开发
didiplus2 天前
我在GitHub刷到一个诗词API,顺手写了款小程序
微信小程序
言乐62 天前
Python实现可运行解密游戏游戏框架
python·游戏·小程序·游戏程序·关卡设计
2501_915106323 天前
iOS 软件测试工具性能监控、日志分析 KeyMob、Instruments等
android·ios·小程序·https·uni-app·iphone·webview
云迈科技-软件定制开发3 天前
2026 AI智能体小程序APP开发怎么做:从场景规划到上线交付的完整参考
大数据·人工智能·小程序
小码哥0683 天前
医院陪诊小程序怎么开发-医院陪诊小程序源码功能-微信小程序 医院健康陪诊陪护系统
微信小程序·小程序·医院陪诊·陪诊系统·陪诊陪诊