sebk / small-swoft-auth
基于 small-orm 的 Swoft jwt 身份验证
Requires
- sebk/small-orm-forms: 1.*
- sebk/small-orm-swoft: 1.*
- sebk/swoft-voter: 1.*
- swoft/auth: ~2.0.0
- swoft/framework: ~2.0.0
This package is auto-updated.
Last update: 2024-09-29 06:03:09 UTC
README
基于 small-orm 的 Swoft jwt 身份验证
包含一个控制器基类,以简化在控制器中实现令牌支持,并与投票者关联来管理用户权限
安装
创建您的 Swoft 项目:http://swoft.io/docs/2.x/en/quick-start/install.html
安装依赖项
composer require sebk/small-orm-core
composer require sebk/small-orm-forms
composer require sebk/swoft-voter
需要 Swoft Voter 包(https://github.com/sebk69/small-swoft-auth)
composer require sebk/small-swoft-auth
文档
参数
在 base.php 中,注册 AuthManagerService
return [
...
\Swoft\Auth\Contract\AuthManagerInterface::class => [
'class' => \Sebk\SmallSwoftAuth\Service\AuthManagerService::class
],
...
];
在 app.php 中,根据您的应用程序注册 app.user,以便 AuthManager 请求您的应用程序用户
return [
...
'user' => [
'dao' => ['GenericBundle', 'User'],
'accountField' => 'login',
'identityField' => 'id',
],
...
];
实现 UserModelInterface
您的应用程序用户模型必须实现 UserModelInterface。 (在此示例中,为了简单起见,密码通过 md5 哈希存储。出于安全原因,请勿使用 md5 哈希,而应首选 SHA-256 哈希或更高版本)
use Sebk\SmallSwoftAuth\Interfaces\UserModelInterface;
class User extends Model implements UserModelInterface
{
/**
* Check user password
* @param string $password
* @return bool
*/
public function checkPassword(string $password)
{
return $this->getPassword() == md5($password);
}
}
实现您的投票者
有关配置和实现的详细信息,请参阅 https://github.com/sebk69/small-voter
实现登录
实现您的控制器
为了保护您的控制器
- 使用 swoft/auth 的 AuthMiddleware
- 您的控制器必须扩展 TokenSecuredController 抽象类
此外,您可以通过在控制器对象上使用 denyAccessUnlessGranted 方法并使用投票者来通过应用程序的特定规则保护路由
$this->denyAccessUnlessGranted(VoterInterface::ATTRIBUTE_READ, $this);
以下是一个控制器示例
<?php declare(strict_types=1);
namespace App\Http\Controller;
use Sebk\SmallOrmSwoft\Traits\Injection\DaoFactory;
use Sebk\SmallSwoftAuth\Controller\TokenSecuredController;
use Sebk\SwoftVoter\VoterManager\VoterInterface;
use App\Model\OrderBundle\Dao\Customer;
use Swoft\Http\Message\Response;
use Swoft\Http\Server\Annotation\Mapping\Controller;
use Swoft\Http\Server\Annotation\Mapping\RequestMapping;
use Swoft\Http\Server\Annotation\Mapping\RequestMethod;
use Swoft\Http\Server\Annotation\Mapping\Middleware;
use Swoft\Http\Server\Annotation\Mapping\Middlewares;
use Swoft\Http\Message\Request;
use Swoft\Auth\Middleware\AuthMiddleware;
/**
* Class CustomerController
*
* @since 2.0
*
* @Controller("api/customers")
*
* @Middlewares ({AuthMiddleware::class})
* @Middleware (AuthMiddleware::class)
*/
class CustomerController extends TokenSecuredController
{
use DaoFactory;
/**
* @RequestMapping("page/{page}/pageSize/{pageSize}", method={RequestMethod::GET})
* @param int $page
* @param int $pageSize
* @param Request $request
* @return Response
* @throws \Sebk\SmallOrmCore\QueryBuilder\BracketException
* @throws \Sebk\SmallOrmCore\QueryBuilder\QueryBuilderException
*/
public function getCustomerList(int $page, int $pageSize, Request $request)
{
// Check user rigths
$this->denyAccessUnlessGranted(VoterInterface::ATTRIBUTE_READ, $this);
/** @var Customer $daoCustomer */
$daoCustomer = $this->daoFactory->get("CommandeBundle", "Customer");
$customers = $daoCustomer->list($request->getQueryParams(), $page, $pageSize);
return JsonResponse($customers);
}
}
用法:登录请求
以下是一个实现登录操作的 AuthController
<?php
namespace App\Http\Controller;
use Sebk\SmallSwoftAuth\Traits\Injection\AuthManagerService;
use Swoft\Auth\Exception\AuthException;
use Swoft\Http\Message\ContentType;
use Swoft\Http\Message\Request;
use Swoft\Http\Message\Response;
use Swoft\Http\Server\Annotation\Mapping\Controller;
use Swoft\Http\Server\Annotation\Mapping\RequestMapping;
use Swoft\Http\Server\Annotation\Mapping\RequestMethod;
use Swoole\Http\Status;
/**
*
* @since 2.0
*
* @Controller ("api")
*/
class AuthController
{
use AuthManagerService;
const LOGIN_FAILED_MESSAGE = 'Login failed !';
/**
* @RequestMapping (route="login_check", method={RequestMethod::POST})
* @param Request $request
* @return Response
*/
public function login(Request $request): Response {
// Check request format
if (!in_array(ContentType::JSON, $request->getHeader(ContentType::KEY))) {
return JsonResponse(static::LOGIN_FAILED_MESSAGE)
->withStatus(Status::BAD_REQUEST)
;
}
// Login & sign token
try {
$session = $this->authManager->auth($request->getParsedBody());
} catch (AuthException $e) {
return JsonResponse(static::LOGIN_FAILED_MESSAGE)
->withStatus(Status::UNAUTHORIZED)
;
}
$tokenData = [
'token' => $session->getToken(),
'expired_at' => $session->getExpirationTime()
];
return JsonResponse($tokenData);
}
}
要测试它,请使用您喜欢的 rest 应用程序在
POST https:///api/login_check
headers :
Content-Type : application/json
body :
{"account":"myLogin","password":"myPassword"}
它将返回类似的内容
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJTZWJrXFxTbWFsbFN3b2Z0QXV0aFxcU2VydmljZVxcQXV0aExvZ2ljIiwic3ViIjoiODciLCJpYXQiOjE2MzQ3MzY2MzIsImV4cCI6MTYzNDgyMzAzMiwiZGF0YSI6eyJ1c2VyIjp7ImlkVXRpbGlzYXRldXIiOjg3LCJpZEdyb3VwIjoxMiwibm9tVXRpbGlzYXRldXIiOiJLVVMiLCJwcmVub21VdGlsaXNhdGV1ciI6IlNcdTAwZTliYXN0aWVuIiwibG9naW5VdGlsaXNhdGV1ciI6IktTIiwicGFzc3dvcmRVdGlsaXNhdGV1ciI6IjVhZWRmN2M4OWNjMzFlZjRmMzQ1ZWViY2U0YTNjZjkxIiwiaWRUeXBlQ29tbWFuZGVVdGlsaXNhdGV1ciI6MSwiZGF0ZUNvbm5leGlvblV0aWxpc2F0ZXVyIjoiMjAyMS0wOC0yMyAxNjoxMToxOCIsImlkRW50cmVwb3RVdGlsaXNhdGV1ciI6MSwiZW1haWxVdGlsaXNhdGV1ciI6Imsuc2ViYXN0aWVuQGxhLWJlY2FuZXJpZS5jb20iLCJ1dGlsaXNhdGV1ckdyb3VwIjpudWxsLCJ1dGlsaXNhdGV1cnNGb25jdGlvbm5hbGl0ZXMiOltdLCJ1dGlsaXNhdGV1cnNSb2xlcyI6W10sImNvdWxldXIiOiIjMzBmY2NjIiwiY291bGV1clN0eWxlIjoiYmFja2dyb3VuZC1jb2xvcjogIzMwZmNjYzsgY29sb3I6d2hpdGU7IiwiZnJvbURiIjp0cnVlfX19.uHcBGGQcc4hhSwcqdBFyci31DI0yeGMq4teD1zURmIE",
"expired_at": 1634823032
}
或者,如果登录或密码错误,将返回 401 响应
现在,为了访问受保护的路由,请使用带有登录响应令牌的 Authorization 标头。
对于我们的客户列表路由,请使用
GET https:///api/customers/page/1/pageSize/10
headers :
Authorization : Bearer eyJpc3MiOiJTZWJrXFxTbWFsbFN3b2Z0QXV0aFxcU2VydmljZVxcQXV0aExvZ2ljIiwic3ViIjoiODciLCJpYXQiOjE2MzQ3MzA2NDgsImV4cCI6MTYzNDgxNzA0OCwiZGF0YSI6eyJ1c2VyIjp7ImlkVXRpbGlzYXRldXIiOjg3LCJpZEdyb3VwIjoxMiwibm9tVXRpbGlzYXRldXIiOiJLVVMiLCJwcmVub21VdGlsaXNhdGV1ciI6IlNcdTAwZTliYXN0aWVuIiwibG9naW5VdGlsaXNhdGV1ciI6IktTIiwicGFzc3dvcmRVdGlsaXNhdGV1ciI6IjVhZWRmN2M4OWNjMzFlZjRmMzQ1ZWViY2U0YTNjZjkxIiwiaWRUeXBlQ29tbWFuZGVVdGlsaXNhdGV1ciI6MSwiZGF0ZUNvbm5leGlvblV0aWxpc2F0ZXVyIjoiMjAyMS0wOC0yMyAxNjoxMToxOCIsImlkRW50cmVwb3RVdGlsaXNhdGV1ciI6MSwiZW1haWxVdGlsaXNhdGV1ciI6Imsuc2ViYXN0aWVuQGxhLWJlY2FuZXJpZS5jb20iLCJ1dGlsaXNhdGV1ckdyb3VwIjpudWxsLCJ1dGlsaXNhdGV1cnNGb25jdGlvbm5hbGl0ZXMiOltdLCJ1dGlsaXNhdGV1cnNSb2xlcyI6W10sImNvdWxldXIiOiIjMzBmY2NjIiwiY291bGV1clN0eWxlIjoiYmFja2dyb3VuZC1jb2xvcjogIzMwZmNjYzsgY29sb3I6d2hpdGU7IiwiZnJvbURiIjp0cnVlfX19.dTW8L2RyNv9OnhXHu96UCEKL3UpHJj3mXLJdf_LD-yI
如果成功,服务器将返回 200 状态码,如果令牌错误或过期,则返回 401 状态码。