kaabar-jwt / yii2-jwt
Yii2 JWT 扩展是用于在 Yii2 应用程序中实现 JWT (JSON Web Token) 认证的工具。它允许开发者创建需要认证和授权的 API,确保只有授权用户可以访问某些资源。该扩展提供了一种简单灵活的方式在 Yii2 中实现 JWT 认证,使用 JWT 库并遵循 JWT 规范。它包括创建和验证 JWT 令牌,以及处理令牌过期和刷新。Yii2 JWT 扩展可以轻松集成到任何 Yii2 应用程序中,是 API 认证和授权的强大工具。
dev-master
2023-06-22 08:48 UTC
Requires
- lcobucci/jwt: ^4.0
- yiisoft/yii2: ~2.0.0
This package is auto-updated.
Last update: 2024-09-22 11:38:53 UTC
README
Yii2 JWT 扩展是用于在 Yii2 应用程序中实现 JWT (JSON Web Token) 认证的工具。它允许开发者创建需要认证和授权的 API,确保只有授权用户可以访问某些资源。该扩展提供了一种简单灵活的方式在 Yii2 中实现 JWT 认证,使用 JWT 库并遵循 JWT 规范。它包括创建和验证 JWT 令牌,以及处理令牌过期和刷新。Yii2 JWT 扩展可以轻松集成到任何 Yii2 应用程序中,是 API 认证和授权的强大工具。
安装此扩展的首选方式是通过 composer。
运行以下命令之一:
php composer.phar require --prefer-dist kaabar-jwt/yii2-jwt:dev-master
或
"kaabar-jwt/yii2-jwt": "dev-master"
将其添加到您的 composer.json
文件的 require 部分。
实施步骤
- 已安装 Yii2
- 需要一个启用 https 的站点,以便 HttpOnly cookie 可以跨站点工作
- 一个用于存储 RefreshTokens 的数据库表
<?php CREATE TABLE `user_refresh_tokens` ( `user_refresh_tokenID` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, `urf_userID` INT(10) UNSIGNED NOT NULL, `urf_token` VARCHAR(1000) NOT NULL, `urf_ip` VARCHAR(50) NOT NULL, `urf_user_agent` VARCHAR(1000) NOT NULL, `urf_created` DATETIME NOT NULL COMMENT 'UTC', PRIMARY KEY (`user_refresh_tokenID`) ) COMMENT='For JWT authentication process'; ?>
- 在 /config/params.php 中添加 JWT 参数
<?php return [ ... 'jwt' => [ 'issuer' => 'https://api.example.com', //name of your project (for information only) 'audience' => 'https://example.com', //description of the audience, eg. the website using the authentication (for info only) 'id' => 'AMqey0yAVrqmhR82RMlWB3zqMpvRP0zaaOheEeq2tmmcEtRYNj', //a unique identifier for the JWT, typically a random string 'expire' => '+1 hour', //the short-lived JWT token is here set to expire after 1 Hours. 'request_time' => '+5 seconds', //the time between the two requests. (optional) ], ... ]; ?>
- 在 /config/web.php 中添加组件以初始化 JWT 认证
<?php $config = [ 'components' => [ ... 'jwt' => [ 'class' => \kaabar\jwt\Jwt::class, 'key' => 'SECRET-KEY', //typically a long random string ], ... ], ]; ?>
-
将认证器行为添加到您的控制器中
-
对于 AuthController.php,我们必须排除不需要认证的操作,例如登录、选项(当浏览器发送跨站点的 OPTIONS 请求时)。
<?php public function behaviors() { $behaviors = parent::behaviors(); $behaviors['authenticator'] = [ 'class' => \kaabar\jwt\JwtHttpBearerAuth::class, 'except' => [ 'login', 'options', ], ]; return $behaviors; } ?>
- 将 generateJwt() 和 generateRefreshToken() 方法添加到 AuthController.php 中。我们将在登录/刷新令牌操作中使用它们。如果您的用户模型名称不同,请调整类名。
<?php private function generateJwt(\app\models\User $user) { $jwt = Yii::$app->jwt; $signer = $jwt->getSigner('HS256'); $key = $jwt->getKey(); //use DateTimeImmutable; $now = new DateTimeImmutable(); $jwtParams = Yii::$app->params['jwt']; $token = $jwt->getBuilder() // Configures the issuer (iss claim) ->issuedBy($jwtParams['issuer']) // Configures the audience (aud claim) ->permittedFor($jwtParams['audience']) // Configures the id (jti claim) ->identifiedBy($jwtParams['id'], true) // Configures the time that the token was issue (iat claim) ->issuedAt($now) // Configures the time that the token can be used (nbf claim) ->canOnlyBeUsedAfter($now->modify($jwtParams['request_time'])) // Configures the expiration time of the token (exp claim) ->expiresAt($now->modify($jwtParams['expire'])) // Configures a new claim, called "uid" ->withClaim('uid', $user->id) // Builds a new token ->getToken($signer, $key); return $token->toString(); } /** * @throws yii\base\Exception */ private function generateRefreshToken(\app\models\User $user, \app\models\User $impersonator = null): \app\models\UserRefreshToken { $refreshToken = Yii::$app->security->generateRandomString(200); // TODO: Don't always regenerate - you could reuse existing one if user already has one with same IP and user agent $userRefreshToken = new \app\models\UserRefreshToken([ 'urf_userID' => $user->id, 'urf_token' => $refreshToken, 'urf_ip' => Yii::$app->request->userIP, 'urf_user_agent' => Yii::$app->request->userAgent, 'urf_created' => gmdate('Y-m-d H:i:s'), ]); if (!$userRefreshToken->save()) { throw new \yii\web\ServerErrorHttpException('Failed to save the refresh token: '. $userRefreshToken->getErrorSummary(true)); } // Send the refresh-token to the user in a HttpOnly cookie that Javascript can never read and that's limited by path Yii::$app->response->cookies->add(new \yii\web\Cookie([ 'name' => 'refresh-token', 'value' => $refreshToken, 'httpOnly' => true, 'sameSite' => 'none', 'secure' => true, 'path' => '/v1/auth/refresh-token', //endpoint URI for renewing the JWT token using this refresh-token, or deleting refresh-token ])); return $userRefreshToken; } ?>
- 将登录操作添加到 AuthController.php 中
<?php public function actionLogin() { $model = new \app\models\LoginForm(); if ($model->load(Yii::$app->request->getBodyParams()) && $model->login()) { $user = Yii::$app->user->identity; $token = $this->generateJwt($user); return [ 'user' => $user, 'token' => (string) $token, ]; } else { $model->validate(); return $model; } } ?>
- 将刷新令牌操作添加到 AuthController.php 中。当 JWT 过期时调用 POST /auth/refresh-token,当用户请求注销时调用 DELETE /auth/refresh-token(然后从 localStorage 中删除 JWT 令牌)。
<?php public function actionRefreshToken() { $refreshToken = Yii::$app->request->cookies->getValue('refresh-token', false); if (!$refreshToken) { return new \yii\web\UnauthorizedHttpException('No refresh token found.'); } $userRefreshToken = \app\models\UserRefreshToken::findOne(['urf_token' => $refreshToken]); if (Yii::$app->request->getMethod() == 'POST') { // Getting new JWT after it has expired if (!$userRefreshToken) { return new \yii\web\UnauthorizedHttpException('The refresh token no longer exists.'); } $user = \app\models\User::find() //adapt this to your needs ->where(['userID' => $userRefreshToken->urf_userID]) ->andWhere(['not', ['usr_status' => 'inactive']]) ->one(); if (!$user) { $userRefreshToken->delete(); return new \yii\web\UnauthorizedHttpException('The user is inactive.'); } $token = $this->generateJwt($user); return [ 'status' => 'ok', 'token' => (string) $token, ]; } elseif (Yii::$app->request->getMethod() == 'DELETE') { // Logging out if ($userRefreshToken && !$userRefreshToken->delete()) { return new \yii\web\ServerErrorHttpException('Failed to delete the refresh token.'); } return ['status' => 'ok']; } else { return new \yii\web\UnauthorizedHttpException('The user is inactive.'); } } ?>
- 调整您的用户模型中的 findIdentityByAccessToken() 以通过 JWT 的 uid 断言找到认证用户
<?php public static function findIdentityByAccessToken($token, $type = null) { return static::find() ->where(['userID' => (string) $token->getClaim('uid') ]) ->andWhere(['<>', 'usr_status', 'inactive']) //adapt this to your needs ->one(); } ?>
- 此外,请记住在更改密码时清除用户的所有 RefreshTokens,例如在用户模型的 afterSave() 中。
<?php public function afterSave($isInsert, $changedOldAttributes) { // Purge the user tokens when the password is changed if (array_key_exists('usr_password', $changedOldAttributes)) { \app\models\UserRefreshToken::deleteAll(['urf_userID' => $this->userID]); } return parent::afterSave($isInsert, $changedOldAttributes); } ?>
- 创建一个页面,用户可以删除他的 RefreshTokens。列出属于给定用户的 user_refresh_tokens 记录,并允许他删除他选择的记录。