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);
        }
    }
});
相关推荐
光影少年2 小时前
Taro 是如何解析入口配置 app.config.ts 和页面配置的?
微信小程序·小程序·typescript·reactjs·百度小程序·taro
用户788477316345 小时前
门店核销闭环:小程序扫码 + .NET 幂等接口 + WPF 看板,一天能跑通
微信小程序·c#
DS小龙哥7 小时前
从呼吸心率到离床报警:60G雷达睡眠仪全栈开发实录(硬件+MQTT+小程序)DIY一台全功能睡眠监护仪
stm32·单片机·嵌入式硬件·物联网·小程序·智能家居
扶风ff10 小时前
练题簿在线免费刷题:刷题、背题、错题复习,一个小程序安排日常备考
学习·小程序
扶风ff10 小时前
练题簿在线免费刷题:共享题库、学习任务、在线考试,岗位培训更方便
学习·小程序
愚公搬代码12 小时前
【愚公系列】《微信小程序项目实战(AI编程+视频图解)》014-莫凡商城小程序项目视图容器组件的应用
微信小程序·小程序·ai编程
星光开发者1 天前
基于决策树随机森林算法的小说推荐系统的设计与实现-计算机毕设【课程设计】78485
vue.js·算法·微信小程序·django·php·课程设计·express
程xu袁1 天前
只有一段副歌,AI写歌选小程序、Suno还是海绵音乐?
小程序·suno·ai音乐·ai音乐工具·ai生成音乐·ai写歌工具
专业程序开发源1 天前
springboot生命故事书制作小程序65290-计算机课程设计、毕业设计
java·vue.js·spring boot·后端·mysql·小程序·课程设计
QQ_21696290961 天前
基于微信小程序的智能膳食分析系统
java·大数据·spring boot·微信小程序·小程序·旅游