学习笔记20251230

JWT的使用

流程图

flowchart TD A[生成token] --> B(基类服务<br>BaseService->createToken) B --> C[Jwt工具类<br>JwtAuthUtil->createToken] D[验证token<br>请求头携带token] --> F[权限中间件<br>AuthMidd->handle] F --> G[权限服务<br>AuthService->parseToken] G --> H[Jwt工具类<br>JwtAuthUtil->parseToken<br>JwtAuthUtil->verifyToken]

使用firebase/php-jwt包进行封装

// composer require firebase/php-jwt

<?php
namespace app\utils;

use Firebase\JWT\JWT;
use Firebase\JWT\Key;

class JwtAuth
{
    protected $secretKey = 'your-secret-key';

    protected $token = null;

    /**
     * @param $id
     * @return string
     */
    public function getToken($id): string
    {
        // 配置信息

        $issuedAt = time();
        $expirationTime = $issuedAt + 3600; // 1小时过期
        $serverName = "yourdomain.com";

        // 创建 payload
        $payload = [
            'iat' => $issuedAt,           // 签发时间
            'iss' => $serverName,         // 签发者
            'nbf' => $issuedAt,           // 生效时间
            'exp' => $expirationTime,     // 过期时间
            'data' => [                   // 自定义数据
                'userId' => $id
            ]
        ];

        // 生成 token
        return JWT::encode($payload, $this->secretKey, 'HS256');
    }

    /**
     * 解析token
     * @param string $token
     * @return array
     */
    public function parseToken(string $token = ''): array
    {
        $this->token = $token;
        list($headb64, $bodyb64, $cryptob64) = explode('.', $this->token);
        $payload = JWT::jsonDecode(JWT::urlsafeB64Decode($bodyb64));
        return [$payload->data->userId];
    }

    /**
     * @return void
     */
    public function verifyToken()
    {
        JWT::$leeway = 60;
        $decoded = JWT::decode($this->token, new Key($this->secretKey, 'HS256'));
        $this->token = null;
    }
}

// BaseServices类设计

<?php

namespace app\services;

use app\utils\JwtAuth;

abstract class BaseServices
{
    public function createToken(int $id)
    {
        $jwtAuth = app()->make(JwtAuth::class);
        return $jwtAuth->getToken($id);
    }

}

// 验证token设计

<?php
namespace app\middleware;

use app\Request;
use app\services\user\AuthService;
use Closure;

class AuthTokenMiddleware
{
    /**
     * @param Request $request
     * @param Closure $next
     * @return mixed
     */
    public function handle(Request $request, Closure $next)
    {
        $token = $this->extractToken($request);
        /** @var AuthService $authService */
        $authService = app()->make(AuthService::class);
        $data = $authService->parseToken($token);
        $request->macro('userInfo', function () use (&$data) {
            return $data;
        });

        return $next($request);
    }
    /**
     * @param Request $request
     * @return string|null
     */
    private function extractToken(Request $request): ?string
    {
        // 从 Authorization 头获取
        $header = $request->header('Authorization');
        if (preg_match('/Bearer\s+(.*)$/i', $header, $matches)) {
            return $matches[1];
        }

        // 从查询参数获取
        return $request->get('token');
    }
}

// AuthService

<?php

namespace app\services\user;

use app\dao\user\UserDao;
use app\services\BaseServices;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use app\exception\ApiException;
use app\utils\JwtAuth;

class AuthService extends baseServices
{
    /**
     * @var UserDao 
     */
    protected $useDao;

    /**
     * @param UserDao $userDao
     */
    public function __construct(UserDao $userDao)
    {
        $this->useDao = $userDao;
    }

    /**
     * @param string $token
     * @return array
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function parseToken(string $token = ''): array
    {
        if (!$token || $token === 'undefined') {
            throw new ApiException(10001);
        }

        /** @var JwtAuth $jwtAuth */
        $jwtAuth = app()->make(JwtAuth::class);
        //设置解析token
        [$id] = $jwtAuth->parseToken($token);

        //验证token
        try {
            $jwtAuth->verifyToken();
        } catch (\Throwable $e) {
            throw new ApiException([10002, $e->getMessage()]);
        }

        $userInfo = $this->useDao->getById($id);
        return $userInfo->hidden(['pwd'])->toArray();
    }
}

Trait的使用

Trait 在PHP中是一种水平代码复用机制,用于解决单继承语言的局限性。简单说,它就像一组可以“复制粘贴”到多个类中的方法集合,让不同继承链的类能共享相同的行为

核心对比:Trait vs 抽象类 vs 接口

特性 Trait 抽象类 (Abstract Class) 接口 (Interface)
核心目的 代码复用 (水平注入) 定义模板/契约 (垂直继承) 定义纯契约 (要求实现)
如何关联 use 关键字 (在类内部) extends 关键字 implements 关键字
数量限制 一个类可以use多个Trait 单继承 (只能extends一个) 多实现 (可implements多个)
包含内容 方法、属性(均可包含实现) 抽象方法、具体方法、属性 方法签名、常量(PHP 8.0前)
实例化 不能直接实例化 不能直接实例化 不能直接实例化
关系比喻 “技能书”:给英雄装备不同的技能 “职业蓝图”:规定一个职业必须会什么,并提供部分基础 “资格证书”:只声明需要考核的技能项,不教你怎么做
  1. Macroable的使用

    spatie/macroable 是知名 PHP 开发团队 Spatie 发布的一个轻量级开源库。它的核心功能是通过一个 Trait,让你能在运行时为 PHP 类动态地添加新方法,而无需修改原始类的代码。

    demo

    //composer require spatie/macroable
    
    
    use Spatie\Macroable\Macroable;
    
    class MyClass {
        use Macroable;
    }
    
    // 动态添加一个方法
    MyClass::macro('sayHello', function (string $name): string {
        return "Hello, {$name}!";
    });
    
    $instance = new MyClass();
    echo $instance->sayHello('World'); // 输出:Hello, World!
    

    Macroable使用场景

    • 在Request对象添加额外数据

      比如

      
      namespace app;
      
      // 应用请求对象类
      use Spatie\Macroable\Macroable;
      
      class Request extends \think\Request
      {
          // 使用MacroableTrait
          use Macroable;
      }
      
      // 应用
      $data = user::get(1);
      $request->macro('userInfo', function () use (&$data) {
          return $data;
      });
      
  2. 复用通用方法片段

    这是Trait最直接的用途。将多个类中重复出现的“代码片段”提取出来。

    // 一个处理时间戳的Trait,很多模型都可能需要
    trait TimeStampTrait {
        public function getCreateTimeTextAttr($value, $data) {
            return date('Y-m-d H:i:s', $data['create_time']);
        }
        public function setCreateTimeAttr($value) {
            return strtotime($value);
        }
    }
    
    // 在用户模型中复用
    class UserModel {
        use TimeStampTrait; // 瞬间拥有了时间戳处理能力
        // ... 其他用户相关的逻辑
    }
    
    // 在商品模型中同样复用
    class ProductModel {
        use TimeStampTrait; // 同上,无需重复编写
        // ...
    }
    
  3. 实现“多继承”效果
    由于PHP是单继承,一个类无法同时继承两个父类。Trait可以模拟这种需求。

trait LoggableTrait {
    public function log($message) {
        // 写入日志的实现
        file_put_contents('app.log', $message, FILE_APPEND);
    }
}

trait CacheableTrait {
    public function cache($key, $value) {
        // 缓存操作的实现
        Cache::set($key, $value);
    }
}

// UserService 需要同时拥有日志和缓存能力
class UserService {
    use LoggableTrait, CacheableTrait; // 组合多个“能力”

    public function updateUser($id, $data) {
        $this->log("开始更新用户 {$id}"); // 来自 LoggableTrait
        // ... 更新逻辑
        $this->cache("user_{$id}", $data); // 来自 CacheableTrait
    }
}
posted @ 2026-01-27 23:04  需要成长的小哥  阅读(42)  评论(0)    收藏  举报