初始化
缺陷:主题配色需要优化整体的同风格
This commit is contained in:
104
app/Service/wx/WxTokenService.php
Normal file
104
app/Service/wx/WxTokenService.php
Normal file
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service\wx;
|
||||
|
||||
use App\Models\business\WxUserModel;
|
||||
use App\Service\common\UtilsService;
|
||||
use Firebase\JWT\JWT;
|
||||
use Firebase\JWT\Key;
|
||||
|
||||
/**
|
||||
* 小程序用户令牌
|
||||
*
|
||||
* 老 lgp-wx-api 把微信的 session_key 当 token 直接返回客户端:
|
||||
* 它既是解密用户数据的密钥,又永不过期,泄露等于长期冒用身份。
|
||||
* 这里改成服务端自签 JWT,session_key 只留库里。
|
||||
*
|
||||
* payload 带 scope=wx,与后台管理端的 token 互不通用——
|
||||
* 否则一个小程序 token 就能打后台接口。
|
||||
*/
|
||||
class WxTokenService
|
||||
{
|
||||
private static mixed $_instance;
|
||||
|
||||
private const SCOPE = 'wx';
|
||||
|
||||
private string $secretKey;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$secret = (string) config('nl.jwt.secret', '');
|
||||
// 与后台同一个密钥但 scope 不同,验证时强制校验 scope,不存在越权互通
|
||||
$this->secretKey = strlen($secret) >= 32 ? $secret : hash('sha256', $secret !== '' ? $secret : 'nl_wx_jwt_fallback');
|
||||
}
|
||||
|
||||
public static function getInstance(): null|static
|
||||
{
|
||||
$name = get_called_class();
|
||||
if (!isset(self::$_instance[$name])) {
|
||||
self::$_instance[$name] = new static();
|
||||
}
|
||||
return self::$_instance[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* 签发令牌
|
||||
*/
|
||||
public function issue(int $userId, string $appCode = ''): string
|
||||
{
|
||||
$now = time();
|
||||
return JWT::encode([
|
||||
'iat' => $now,
|
||||
'exp' => $now + (int) config('nl.wx.token_ttl', 30 * 24 * 3600),
|
||||
'scope' => self::SCOPE,
|
||||
'uid' => $userId,
|
||||
'app' => $appCode,
|
||||
], $this->secretKey, 'HS256');
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验令牌并返回用户;失败返回 null 由中间件统一响应
|
||||
*/
|
||||
public function resolveUser(?string $token): ?array
|
||||
{
|
||||
if (empty($token)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
$payload = JWT::decode($token, new Key($this->secretKey, 'HS256'));
|
||||
} catch (\Throwable) {
|
||||
return null;
|
||||
}
|
||||
if (($payload->scope ?? '') !== self::SCOPE) {
|
||||
return null;
|
||||
}
|
||||
$userId = (int) ($payload->uid ?? 0);
|
||||
if ($userId <= 0) {
|
||||
return null;
|
||||
}
|
||||
// 每次请求回查用户:倍率、价格可见性、是否被停用都可能刚被后台改过
|
||||
$user = WxUserModel::where('id', $userId)->where('deleted_at', 0)->first();
|
||||
if (empty($user)) {
|
||||
return null;
|
||||
}
|
||||
$user = $user->toArray();
|
||||
unset($user['session_key']);
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取当前请求的小程序用户,取不到直接中断
|
||||
*/
|
||||
public function requireUser(): array
|
||||
{
|
||||
$user = request()->attributes->get('wx_user');
|
||||
if (!empty($user)) {
|
||||
return $user;
|
||||
}
|
||||
$user = $this->resolveUser(request()->bearerToken());
|
||||
if (empty($user)) {
|
||||
UtilsService::getInstance()->notAuth('请先登录');
|
||||
}
|
||||
return $user;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user