安装
composer require tinywan/jwt
配置文件路径
/config/plugin/tinywan/jwt/app.php
接入中间件
namespace app\middleware;
use support\Redis;
use Tinywan\Jwt\JwtToken;
use Webman\Http\Request;
use Webman\Http\Response;
use Webman\MiddlewareInterface;
class AuthTokenMiddleware extends BaseController implements MiddlewareInterface
{
public function process(Request $request, callable $handler): Response
{
try {
JwtToken::verify();
} catch (\Throwable $e) {
return $this->error('登录状态有误,请重新登录',10003);
}
$tokenInfo = JwtToken::getExtend();
if(!$tokenInfo['id']){
return $this->error('登录状态有误,请重新登录',10000);
}
$key = "routineUser:" . $tokenInfo['id'];
Redis::del($key);
$redisInfo = Redis::get($key);
if($redisInfo){
$uInfo = json_decode($redisInfo,true);
}else{
$uInfo = (new WechatUsersLogic())->getInfo([['id','=',$tokenInfo['id']]]);
$uInfo['wechat_setting'] = $tokenInfo['wechat_setting'];
// $uInfo['code'] = $tokenInfo['code'];
$uInfo['pid'] = $tokenInfo['pid'];
Redis::set($key,json_encode($uInfo));
}
$request->routineUser = $uInfo;
if(empty($request->routineUser)){
return $this->error('登录状态有误,请重新登录',10000);
}
if($request->routineUser['is_del'] !== 0){
return $this->error('账号已被冻结 请联系管理员',10002);
}
/** @var Response $response */
$response = $handler($request);
// Add cross domain HTTP header
/*$response->withHeaders([
'Access-Control-Allow-Origin' => '*',
'Access-Control-Allow-Credentials' => 'true',
]);*/
return $response;
}
}
生成Token
#[RequestMapping(["/login"], methods: ['GET', 'POST', 'OPTIONS'])]
#[Apidoc\Title("登录注册")]
#[Apidoc\Url("/webapi/user/login")]
#[Apidoc\Method("POST,GET")]
#[Apidoc\ParamType("formdata")]
#[Apidoc\Param("account", type: "string", require: true, default: "0xFFabddf5d772662bBb5Fe358D6cDFEDE5B795e15", desc: "区块链地址")]
#[Apidoc\Param("invite", type: "string", require: true, default: "OzGGBB23Vc", desc: "邀请码")]
#[Apidoc\Returned("authorization", type: "string", default: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ3ZWJtYW4udGlueXdhbi5jbiIsImF1ZCI6IndlYm1hbi50aW55d2FuLmNuIiwiaWF0IjoxNzA4NTkzMzM4LCJuYmYiOjE3MDg1OTMzMzgsImV4cCI6MTcwOTE5ODEzOCwiZXh0ZW5kIjp7ImlkIjoyfX0._2Bed8AdUvSCe7-TEP0-1yC_n5_b-bSddXCx76GdES0", desc: "authorization")]
public function login(Request $request): Response
{
$key = "login" ;
if (Redis::setNx($key, 1)) {
Redis::expire($key, 1);
} else {
return error(lang($request, '操作太频繁,请稍后再试!'));
}
$tokenData = JwtToken::generateToken([
'id' => 1, // 这里必须是一个全局抽象唯一id
]);
return success(lang($request, '操作成功'), ['aToken' => $tokenData['access_token'],'rToken'=>$tokenData['refresh_token']]);
}
刷新Token
#[Apidoc\Title("刷新TOKEN")]
#[Apidoc\Url("/webapi/common/refreshToken")]
#[Apidoc\Method("POST,GET")]
#[Apidoc\ParamType("formdata")]
#[Apidoc\Returned("origin_name", type: "string", default: "u=3019768837,3832590544&fm=253&fmt=auto&app=138&f=JPEG.webp", desc: "文件原名")]
#[Apidoc\Returned("save_name", type: "string", default: "593f53411eb2e08c28dd0f15b555b7dc0bf83aae.webp", desc: "存储后文件名")]
#[Apidoc\Returned("save_path", type: "string", default: "E:\\phpstudy_pro\\WWW\\QianYi\\Dapp\\mining\\public/uploads/20240222\\593f53411eb2e08c28dd0f15b555b7dc0bf83aae.webp", desc: "存储路径")]
#[Apidoc\Returned("url", type: "string", default: "/uploads/20240222\\593f53411eb2e08c28dd0f15b555b7dc0bf83aae.webp", desc: "展示路径")]
#[Apidoc\Returned("extension", type: "string", default: "webp", desc: "图片后缀")]
#[RequestMapping(["/refreshToken"], methods: ['GET', 'POST', 'OPTIONS'])]
public function refreshToken($request): Response
{
try {
$tokenData = JwtToken::refreshToken();
}catch (\Throwable $e){
return error(lang($request,$e->getMessage()));
}
return success(lang($request, '操作成功'), ['aToken' => $tokenData['access_token'],'rToken'=>$tokenData['refresh_token']]);
}
JWT文档描述
**响应参数**
| 参数|类型|描述|示例值|
|:---|:---|:---|:---|
|token_type| string |Token 类型 | Bearer |
|expires_in| int |凭证有效时间,单位:秒 | 36000 |
|access_token| string |访问凭证 | XXXXXXXXXXXXXXXXXXXX|
|refresh_token| string | 刷新凭证(访问凭证过期使用 ) | XXXXXXXXXXXXXXXXXXXX|
## 支持函数列表
1、获取当前`id`
```php
$id = Tinywan\Jwt\JwtToken::getCurrentId();
```
2、获取所有字段
```php
$email = Tinywan\Jwt\JwtToken::getExtend();
```
3、获取自定义字段
```php
$email = Tinywan\Jwt\JwtToken::getExtendVal('email');
```
4、刷新令牌(通过刷新令牌获取访问令牌)
```php
$refreshToken = Tinywan\Jwt\JwtToken::refreshToken();
```
5、获令牌有效期剩余时长
```php
$exp = Tinywan\Jwt\JwtToken::getTokenExp();
```
6、单设备登录。默认是关闭,开启请修改配置文件`config/plugin/tinywan/jwt`
```php
'is_single_device' => true,
```
> 单设备登录支持定义客户端 `client` 字段,自定义客户端单点登录(默认为`WEB`,即网页端),如:`MOBILE`、`APP`、`WECHAT`、`WEB`、`ADMIN`、`API`、`OTHER`等等
```php
$user = [
'id' => 2022,
'name' => 'Tinywan',
'client' => 'MOBILE',
];
$token = Tinywan\Jwt\JwtToken::generateToken($user);
var_dump(json_encode($token));
```
7、获取当前用户信息(模型)
```php
$user = Tinywan\Jwt\JwtToken::getUser();
```
该配置项目`'user_model'`为一个匿名函数,默认返回空数组,可以根据自己项目ORM定制化自己的返回模型
**ThinkORM** 配置
```php
'user_model' => function($uid) {
// 返回一个数组
return \think\facade\Db::table('resty_user')
->field('id,username,create_time')
->where('id',$uid)
->find();
}
```
**LaravelORM** 配置
```php
'user_model' => function($uid) {
// 返回一个对象
return \support\Db::table('resty_user')
->where('id', $uid)
->select('id','email','mobile','create_time')
->first();
}
```
8、令牌清理
```php
$res = Tinywan\Jwt\JwtToken::clear();
```
> 只有配置项 `is_single_device`为`true` 才会生效。可选参数:`MOBILE`、`APP`、`WECHAT`、`WEB`、`ADMIN`、`API`、`OTHER`等等
9、自定义终端`client`
```php
// 生成WEB令牌
$user = [
'id' => 2022,
'name' => 'Tinywan',
'client' => JwtToken::TOKEN_CLIENT_WEB
];
$token = JwtToken::generateToken($user);
// 生成移动端令牌
$user = [
'id' => 2022,
'name' => 'Tinywan',
'client' => JwtToken::TOKEN_CLIENT_MOBILE
];
$token = JwtToken::generateToken($user);
```
默认是`WEB`端
10、自定义访问令牌和刷新令牌过期时间
```php
$extend = [
'id' => 2024,
'access_exp' => 7200, // 2 小时
];
$token = Tinywan\Jwt\JwtToken::generateToken($extend);
```
11、令牌过期错误码
* 访问令牌
* 身份验证令牌无效:`401011`
* 身份验证令牌尚未生效:`401012`
* 身份验证会话已过期,请重新登录!:`401013`
* 获取的扩展字段不存在:`401014`
* 访问令牌未知错误:`401015`
* 刷新令牌
* 刷新令牌无效:`401021`
* 刷新令牌尚未生效:`401022`
* 刷新令牌会话已过期,请再次登录!:`401023`
* 刷新令牌获取的扩展字段不存在:`401024`
* 刷新令牌未知错误:`401025`