Hyperf 完整项目-1-jwt 权限认证
1. 安装依赖
composer require phper666/jwt-auth
2.新建中间件
<?php
declare(strict_types=1);
namespace App\Middleware;
use App\Model\User;
use Phper666\JWTAuth\JWT;
use Hyperf\Utils\Context;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Phper666\JwtAuth\Exception\TokenValidException;
use Hyperf\HttpServer\Contract\ResponseInterface as HttpResponse;
class JwtAuthMiddleware implements MiddlewareInterface
{
/**
* @var HttpResponse
*/
protected $response;
protected $prefix = 'Bearer';
protected $jwt;
public function __construct(HttpResponse $response, JWT $jwt)
{
$this->response = $response;
$this->jwt = $jwt;
}
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$isValidToken = false;
$token = $request->getHeader('Authorization')[0] ?? '';
if (strlen($token) > 0) {
$token = ucfirst($token);
$arr = explode($this->prefix . ' ', $token);
$token = $arr[1] ?? '';
try {
if (strlen($token) > 0 && $this->jwt->checkToken()) {
$isValidToken = true;
}
} catch(\Exception $e) {
$data = [
'code' => 401,
'msg' => '对不起,token验证没有通过',
'data' => [],
];
return $this->response->json($data);
}
}
if ($isValidToken) {
$jwtData = $this->jwt->getParserData();
//更改上下文,写入用户信息
//User模型自行创建
$user = User::query()
->where('account', $jwtData['account'])
->first();
$request = Context::get(ServerRequestInterface::class);
$request = $request->withAttribute('user', $user);
Context::set(ServerRequestInterface::class, $request);
return $handler->handle($request);
}
$data = [
'code' => 401,
'msg' => '对不起,token验证没有通过',
'data' => [],
];
return $this->response->json($data);
}
}
3.新建登录
<?php
declare(strict_types = 1);
namespace App\Controller\Auth;
use App\Model\User;
use Phper666\JWTAuth\JWT;
use App\Controller\Controller;
use Hyperf\Di\Annotation\Inject;
class LoginController extends Controller
{
/**
* @Inject
*
* @var JWT
*/
protected $jwt;
/**
* 用户登录.
*
* @return array
*/
public function login()
{
// $hash = password_hash($this->request->input('password'), PASSWORD_DEFAULT);
// return $this->failed($hash);
$user = User::query()->where('account', $this->request->input('account'))->first();
//验证用户账户密码
if (!empty($user->password) && password_verify($this->request->input('password'), $user->password)) {
$userData = [
'uid' => $user->id,
'account' => $user->account,
];
$token = $this->jwt->getToken($userData);
$data = [
'token' => (string) $token,
'exp' => $this->jwt->getTTL(),
];
return $this->success($data);
}
return $this->failed('登录失败');
}
}
4.用户信息控制器
<?php
declare(strict_types=1);
namespace App\Controller;
use Phper666\JWTAuth\JWT;
use Hyperf\Di\Annotation\Inject;
class UserController extends Controller
{
/**
* @Inject()
* @var JWT
*/
protected $jwt;
/**
* 获取用户信息
* @return [type] [description]
*/
public function info()
{
//获取用户数据
$user = $this->request->getAttribute('user');
return $this->success($user);
}
/**
* 用户退出
* @return [type] [description]
*/
public function logout()
{
if ($this->jwt->logout()) {
return $this->success('','退出登录成功');
};
return $this->failed('退出登录失败');
}
}
5.继承的控制器
<?php
declare(strict_types = 1);
namespace App\Controller;
use Hyperf\Di\Annotation\Inject;
use Psr\Container\ContainerInterface;
use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\HttpServer\Contract\ResponseInterface;
class Controller
{
/**
* @Inject
*
* @var ContainerInterface
*/
protected $container;
/**
* @Inject
*
* @var RequestInterface
*/
protected $request;
/**
* @Inject
*
* @var ResponseInterface
*/
protected $response;
/**
* 请求成功
*
* @param $data
* @param string $message
*
* @return array
*/
public function success($data, $message = 'success')
{
$code = $this->response->getStatusCode();
return ['msg' => $message, 'code' => $code, 'data' => $data];
}
/**
* 请求失败.
*
* @param string $message
*
* @return array
*/
public function failed($message = 'Request format error!')
{
return ['msg' => $message, 'code' => 500, 'data' => ''];
}
}
6.配置文件 config/autoload/jwt.php
<?php
declare(strict_types=1);
return [
'login_type' => env('JWT_LOGIN_TYPE', 'sso'), // 登录方式,sso为单点登录,mpop为多点登录
/**
* 单点登录自定义数据中必须存在uid的键值,这个key你可以自行定义,只要自定义数据中存在该键即可
*/
'sso_key' => 'uid',
'secret' => env('JWT_SECRET', 'phper666'), // 非对称加密使用字符串,请使用自己加密的字符串
/**
* JWT 权限keys
* 对称算法: HS256, HS384 & HS512 使用 `JWT_SECRET`.
* 非对称算法: RS256, RS384 & RS512 / ES256, ES384 & ES512 使用下面的公钥私钥.
*/
'keys' => [
'public' => env('JWT_PUBLIC_KEY'), // 公钥,例如:'file:///path/to/public/key'
'private' => env('JWT_PRIVATE_KEY'), // 私钥,例如:'file:///path/to/private/key'
],
'ttl' => env('JWT_TTL', 7200), // token过期时间,单位为秒
'alg' => env('JWT_ALG', 'HS256'), // jwt的hearder加密算法
/**
* 支持的算法
*/
'supported_algs' => [
'HS256' => 'Lcobucci\JWT\Signer\Hmac\Sha256',
'HS384' => 'Lcobucci\JWT\Signer\Hmac\Sha384',
'HS512' => 'Lcobucci\JWT\Signer\Hmac\Sha512',
'ES256' => 'Lcobucci\JWT\Signer\Ecdsa\Sha256',
'ES384' => 'Lcobucci\JWT\Signer\Ecdsa\Sha384',
'ES512' => 'Lcobucci\JWT\Signer\Ecdsa\Sha512',
'RS256' => 'Lcobucci\JWT\Signer\Rsa\Sha256',
'RS384' => 'Lcobucci\JWT\Signer\Rsa\Sha384',
'RS512' => 'Lcobucci\JWT\Signer\Rsa\Sha512',
],
/**
* 对称算法名称
*/
'symmetry_algs' => [
'HS256',
'HS384',
'HS512'
],
/**
* 非对称算法名称
*/
'asymmetric_algs' => [
'RS256',
'RS384',
'RS512',
'ES256',
'ES384',
'ES512',
],
/**
* 是否开启黑名单,单点登录和多点登录的注销、刷新使原token失效,必须要开启黑名单,目前黑名单缓存只支持hyperf缓存驱动
*/
'blacklist_enabled' => env('JWT_BLACKLIST_ENABLED', true),
/**
* 黑名单的宽限时间 单位为:秒,注意:如果使用单点登录,该宽限时间无效
*/
'blacklist_grace_period' => env('JWT_BLACKLIST_GRACE_PERIOD', 0),
/**
* 黑名单缓存token时间,注意:该时间一定要设置比token过期时间要大一点,默认为1天,最好设置跟过期时间一样
*/
'blacklist_cache_ttl' => env('JWT_TTL', 86400),
'blacklist_prefix' => 'phper666_jwt', // 黑名单缓存的前缀
/**
* 区分不同场景的token,比如你一个项目可能会有多种类型的应用接口鉴权,下面自行定义,我只是举例子
* 下面的配置会自动覆盖根配置,比如application1会里面的数据会覆盖掉根数据
* 下面的scene会和根数据合并
* scene必须存在一个default
* 什么叫根数据,这个配置的一维数组,除了scene都叫根配置
*/
'scene' => [
'default' => [],
'application1' => [
'secret' => 'application1', // 非对称加密使用字符串,请使用自己加密的字符串
'login_type' => 'sso', // 登录方式,sso为单点登录,mpop为多点登录
'sso_key' => 'uid',
'ttl' => 7200, // token过期时间,单位为秒
'blacklist_cache_ttl' => env('JWT_TTL', 7200), // 黑名单缓存token时间,注意:该时间一定要设置比token过期时间要大一点,默认为100秒,最好设置跟过期时间一样
],
'application2' => [
'secret' => 'application2', // 非对称加密使用字符串,请使用自己加密的字符串
'login_type' => 'sso', // 登录方式,sso为单点登录,mpop为多点登录
'sso_key' => 'uid',
'ttl' => 7200, // token过期时间,单位为秒
'blacklist_cache_ttl' => env('JWT_TTL', 7200), // 黑名单缓存token时间,注意:该时间一定要设置比token过期时间要大一点,默认为100秒,最好设置跟过期时间一样
],
'application3' => [
'secret' => 'application3', // 非对称加密使用字符串,请使用自己加密的字符串
'login_type' => 'mppo', // 登录方式,sso为单点登录,mpop为多点登录
'ttl' => 7200, // token过期时间,单位为秒
'blacklist_cache_ttl' => env('JWT_TTL', 7200), // 黑名单缓存token时间,注意:该时间一定要设置比token过期时间要大一点,默认为100秒,最好设置跟过期时间一样
]
],
'model' => [ // TODO 支持直接获取某模型的数据
'class' => '',
'pk' => 'uid'
]
];
7. 创建路由
Router::post('/user/login', 'App\Controller\Auth\LoginController@login');
Router::post('/user/register', 'App\Controller\Auth\RegisterController@register');
//个人资料
Router::addGroup('/user/', function () {
Router::get('info','App\Controller\UserController@info');
Router::post('logout', 'App\Controller\UserController@logout');
Router::get('elasticsearch', 'App\Controller\UserController@elasticsearch');
}, [
'middleware' => [App\Middleware\JwtAuthMiddleware::class]
]);
接下来可以模拟API请求,看效果
本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由系统于 5年前 自动加精
我的logout不生效 :cry:
这个中间件其实不太需要,本身
phper666/jwt-auth
已经实现了棒,之前有个博客只加了权限却取不到信息,我就自己改了,思路和实现一样。感觉这样才是完整的。点赞
[ERROR] Call to undefined method Lcobucci\JWT\Builder::identifiedBy()[127] in /www/wwwroot/project/assistant/vendor/phper666/jwt-auth/src/Jwt.php
这是什么问题·?刚开始用3.x版本的,后来换成2.x的都不行