插件窝 干货文章 thinkPHP配置jwt(代码实例)

thinkPHP配置jwt(代码实例)

amp 39 token gt 764    来源:    2024-10-28

thinkphp5.1-jwt的安装与使用

安装jwt插件

在composer.json的require中加入如下配置

"firebase/php-jwt": "^5.0"

在项目根目录下执行composer update即可.

立即学习“PHP免费学习笔记(深入)”;

创建一个auth中间件

php think make:middleware Auth

打开applicationhttpmiddlewareAuth文件

<?php
namespace app\http\middleware;
use Firebase\JWT\JWT;
use Firebase\JWT\SignatureInvalid\Exception;
use think\exception\TokenException;
use think\exception\ValidateException;
use think
acade\Cache;
use think
acade\Config;

class Auth
{
 public function handle($request, Closure $next)
 {
 $bearer_token = [];
 $bearer = $request->header(&#39;authorization&#39;);//取header中的token
 if ($bearer !== null) {
 //不空尝试去匹配
 preg_match(&#39;/bearers*(S+)/i&#39;, $bearer, $bearer_token);
 }
 if (empty($bearer_token[1])) {
 //匹配不到结果尝试去url中获取
 if ($request->param(&#39;token&#39;) !== null) {
 $token = $request->param(&#39;token&#39;);
 }else{
 throw new TokenException(&#39;请登录&#39;, 401);
 }
 }else{
 $token=$bearer_token[1];
 }
 try {
 $de_token = JWT::decode($token, Config::get(&#39;JWT_KEY&#39;), Config::get(&#39;JWT_ENCRYPTION&#39;));
 } catch (SignatureInvalidException $exception) {
 //捕获JWT解析错误
 throw new TokenException(&#39;无效令牌&#39;, 401);
 } catch (Exception $exception) {
 throw new TokenException(&#39;请重新登录&#39;, 401);
 }
 if ($de_token->voe < time() && $de_token->exp > time()) {
 throw new TokenException(&#39;请换取新令牌&#39;, 402);
 } else if ($de_token->voe < time()) {
 throw new TokenException(&#39;请重新登录&#39;, 401);
 }
 if (Cache::tag(&#39;login&#39;)->get(&#39;token_&#39; . $de_token->data->uid) != $token) {
 throw new TokenException(&#39;用户信息错误,请重新登录&#39;, 401);
 }
 if ($de_token->data->is_ban == 1) {
 throw new ValidateException(&#39;该账户已被封禁&#39;);
 }
 $request->auth = $de_token->data->uid;
 return $next($request);
 }
}

创建一个控制器Login
php think make:controller login/Login --plain
代码如下

<?php
namespace app\login\controller;
use app\common\help;
use app\common\service\OperationToken;
use think\Controller;
use think\Db;
use think\Request;
class Login extends Controller
{
 public function login(Request $request)
 {
 $info = Db::name(&#39;user&#39;)->field(&#39;id,uuid,nick,gender,icon,im_accid,im_icon,is_ban&#39;)->where(&#39;del_time&#39;, &#39;=&#39;, &#39;0&#39;)->where([&#39;mobile&#39; => $request->param(&#39;phone&#39;), &#39;password&#39; => md5($request->param(&#39;password&#39;))])->findOrEmpty();
 if ($info == null || empty($info)) {
 return help::errJsonReturn(&#39;账号或密码错误&#39;);
 }
 $token = OperationToken::crearToken($info[&#39;id&#39;], $info[&#39;uuid&#39;], $info[&#39;is_ban&#39;]);
 return json([
 &#39;type&#39; => &#39;Bearer &#39;,
 &#39;access_token&#39;=>$token[&#39;token&#39;],
 &#39;exp_time&#39;=>$token[&#39;exp&#39;],
 &#39;voe_time&#39;=>$token[&#39;voe&#39;],
 &#39;iat_time&#39;=>time()
 ]);
 }
}

在application下新建common文件夹,在common下新建service文件夹,service文件夹下创建

OperationToken.php

<?php
namespace app\common\service;
use think\Db;
use think
acade\Cache;
use Firebase\JWT\JWT;
use think
acade\Config;
class OperationToken
{
 public static function crearToken(int $uid, string $uuid, int $is_ban): array
 {
 $time = time();
 $info_token = [
 &#39;iat&#39; => $time,//签发时间
 &#39;voe&#39; => Config::get(&#39;TOKEN_VOE&#39;,7200) + $time,//换取有效时间
 &#39;exp&#39; => Config::get(&#39;TOKEN_EXP&#39;,3600)+$time,//有效时间
 &#39;sign&#39; => base64_encode($uuid),//签名
 &#39;data&#39; => [
 &#39;uid&#39; => $uid,//用户id
 &#39;is_ban&#39; => $is_ban,//是否被禁用
 ]
 ];
 $token = JWT::encode($info_token, Config::get(&#39;JWT_KEY&#39;));
 Cache::tag(&#39;login&#39;)->set(&#39;token_&#39; . $uid, $token, Config::get(&#39;TOKEN_VOE&#39;,7200) + $time);
 Db::name(&#39;user_login_log&#39;)->insert(
 [
 &#39;uid&#39;=>$uid,
 &#39;token&#39;=>$token,
 &#39;iat_time&#39;=>$time,
 &#39;ip&#39;=>ip2long(request()->ip()),
 &#39;exp_time&#39;=>Config::get(&#39;TOKEN_EXP&#39;,3600)+$time,
 &#39;voe_time&#39;=> Config::get(&#39;TOKEN_VOE&#39;,7200) + $time
 ]
 );
 return [
 &#39;token&#39;=>$token, 
 &#39;voe&#39; =>Config::get(&#39;TOKEN_VOE&#39;,7200) + $time,
 &#39;exp&#39; => Config::get(&#39;TOKEN_EXP&#39;,3600)+$time];
 }
}

config/app.php文档末尾追加参数并接管错误控制

// 异常处理handle类 留空使用 think\exception\Handle
&#39;exception_handle&#39; => function ($e) {
 //参数验证错误
 if ($e instanceof think\exception\ValidateException) {
 return json([&#39;msg&#39; => $e->getError()], 422);
 }
 //route未定义
 if ($e instanceof think\exception\ValidateException) {
 return json([&#39;msg&#39; => $e->getMessage()], 404);
 } //token过期/无效 401-令牌/账号密码错误 402-令牌过期可旧换新 403-无权限访问
 if ($e instanceof hinkexceptionTokenException) {
 return json([&#39;msg&#39; => $e->getError()], $e->getCode());
 } // 请求异常
 if ($e instanceof HttpException && request()->isAjax()) {
 return response([&#39;msg&#39; => $e->getMessage()], $e->getStatusCode()); 
} 
},

thinkphp5.1-jwt的安装与使用与路由参数验证

在thinkphplibrary hinkexception下新建TokenException.php

代码如下

<?php
namespace think\exception;
class TokenException extends HttpException
{
 protected $error;
 public function __construct($error, $code = 0)
 {
 $this->error = $error;
 $this->message = $error;
 $this->code = $code;
 }
 /**
 * 获取验证错误信息
 * @access public
 * @return array|string
 */
 public function getError()
 {
 return $this->error;
 }
}

创建一个login的验证器

php think make:validate login/Login

代码如下

<?php
namespace app\login
validate;use think\Validate;class Login extends Validate
{ /**
 * 定义验证规则
 * 格式:&#39;字段名&#39; => [&#39;规则1&#39;,&#39;规则2&#39;...]
 *
 * @var array */
 protected $rule = [ &#39;phone&#39;=>&#39;require|mobile&#39;,
 &#39;password&#39;=>&#39;require|length:4,12&#39;
 ]; 
 /**
 * 定义错误信息
 * 格式:&#39;字段名.规则名&#39; => &#39;错误信息&#39;
 *
 * @var array */
 protected $message = [ &#39;phone.mobile&#39;=>&#39;phone格式错误&#39;,
 &#39;password.length&#39;=>&#39;密码长度错误&#39;
 ]; protected $scene=[ &#39;login&#39;=>[&#39;phone&#39;,&#39;password&#39;]
 ];
}

打开route/route.php

代码如下

<?php
use think
acade\Route;
Route::get(&#39;/&#39;,&#39;index/Index/index&#39;);
Route::group(&#39;account&#39;,function (){
 Route::post(&#39;/login&#39;,&#39;login/Login/login&#39;)->validate(&#39;applogin
alidateLogin&#39;,&#39;login&#39;);
});
//需要验证登录
Route::group(&#39;api&#39;,function (){
 Route::post(&#39;/user&#39;,&#39;index/Index/index&#39;);
})->middleware(apphttpmiddlewareAuth::class);

这里的middleware按照官方文档是可以注册到middleware.php中,但在测试中发现问题.路由不执行middleware方法在访问时会由上至下顺序执行middleware.php中注册的所有中间件,因此改写为middleware(apphttpmiddlewareAuth::class);去使用中间件在common下新建一个help.php

代码如下

<?php
namespace appcommon;
class help
{
 public static function susJsonReturn(array $data=[],string $msg=&#39;请求成功&#39;,int $code=1)
 {
 return json([
 &#39;msg&#39;=>$msg,
 &#39;data&#39;=>$data,
 &#39;code&#39;=>$code
 ]);
 }
 public static function errJsonReturn(string $msg = &#39;请求失败&#39;, int $code = 0, array $data = [])
 {
 return json([
 &#39;msg&#39;=>$msg,
 &#39;data&#39;=>$data,
 &#39;code&#39;=>$code
 ]);
 }
}

到数据库中新建一个数据库,新建两张表

CREATE TABLE `xn_user` (
 `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
 `uuid` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT &#39;&#39; COMMENT &#39;uuid&#39;,
 `password` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT &#39;&#39; COMMENT &#39;登录密码&#39;,
 `name` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT &#39;&#39; COMMENT &#39;真实姓名&#39;,
 `nick` varchar(8) COLLATE utf8mb4_unicode_ci DEFAULT &#39;&#39; COMMENT &#39;昵称&#39;,
 `gender` enum(&#39;1&#39;,&#39;2&#39;,&#39;0&#39;) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT &#39;0&#39; COMMENT &#39;用户性别,0 表示未知,1 表示男,2 女表示女&#39;,
 `regist_time` int(11) unsigned DEFAULT NULL,
 `icon` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT &#39;&#39; COMMENT &#39;头像&#39;,
 `mobile` char(11) COLLATE utf8mb4_unicode_ci DEFAULT &#39;&#39; COMMENT &#39;手机号&#39;,
 `im_accid` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT &#39;&#39; COMMENT &#39;im账号&#39;,
 `im_icon` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT &#39;&#39; COMMENT &#39;im头像&#39;,
 `im_email` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT &#39;&#39; COMMENT &#39;im邮箱&#39;,
 `im_birth` varchar(16) COLLATE utf8mb4_unicode_ci DEFAULT &#39;&#39; COMMENT &#39;im生日&#39;,
 `im_mobile` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT &#39;&#39; COMMENT &#39;im手机号码&#39;,
 `create_time` int(11) unsigned DEFAULT &#39;0&#39;,
 `del_time` int(11) unsigned DEFAULT &#39;0&#39;,
 `is_ban` enum(&#39;0&#39;,&#39;1&#39;) COLLATE utf8mb4_unicode_ci DEFAULT &#39;0&#39; COMMENT &#39;是否封号&#39;,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `xn_user_login_log` (
 `uid` int(11) NOT NULL DEFAULT &#39;0&#39; COMMENT &#39;用户id&#39;,
 `token` text COLLATE utf8mb4_unicode_ci NOT NULL COMMENT &#39;登录时的令牌&#39;,
 `iat_time` int(11) DEFAULT &#39;0&#39; COMMENT &#39;登录时间&#39;,
 `ip` bigint(20) unsigned DEFAULT &#39;0&#39; COMMENT &#39;登录ip-ip2long&#39;,
 `exp_time` int(11) DEFAULT &#39;0&#39; COMMENT &#39;失效时间&#39;,
 `voe_time` int(11) DEFAULT &#39;0&#39; COMMENT &#39;token旧换新有效时间&#39;,
 KEY `login_log_uid` (`uid`,`ip`,`token`(32)) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

推荐教程:《TP5》