• JWT 实现基于API的用户认证


    基于 JWT-Auth 实现 API 验证

    如果想要了解其生成Token的算法原理,请自行查阅相关资料

    需要提及的几点:

    • 使用session存在的问题:

      • session和cookie是为了解决http无状态的方案。session是用户保存在服务器中的状态信息,cookie中则保存jsessionId,请求服务器时,服务器读取jsessionId从而确定用户的身份信息,而session+cookie用在restful接口上破坏了其“无状态”的特性,session运行时都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大。这也是restful最致力于通过“无状态”解决的问题。如果使用session,那么restful也就没有什么意义了

      • session降低了系统扩展性。用户认证之后,服务端做认证记录,如果认证的记录被保存在内存中的话,这意味着用户下次请求还必须要请求在这台服务器上,这样才能拿到授权的资源,这样在分布式的应用上,相应的限制了负载均衡器的能力。这也意味着限制了应用的扩展能力

      • cookie不安全,很容易导致跨站请求伪造攻击(CSRF)

    • token存在的问题:

      • 如,如何确定token的过期时间?如果token的有效期过短,很容易造成用户用着用着就掉线的情况,降低用户体验。但目前看来好像并没有太好的办法,只能尽量延长token的有效期,或者每隔一次前端自动向服务端请求一次token

    • 基于 JWT-Auth 的 token 验证体系

      (亲测,希望这篇文章让大家少入坑)

    1. 运行软件版本

      • laravel 5.5

    2. 安装 JWT-Auth 扩展包

      composer require tymon/jwt-auth "1.5.*"

    3. 安装完后在配置文件config/app.php 中添加注册服务提供者和别名:

      ...
      'providers' => [
          ...
          TymonJWTAuthProvidersJWTAuthServiceProvider::class,
      ]
      ...
      'aliases' => [
          ...
          'JWTAuth' => TymonJWTAuthFacadesJWTAuth::class,
      ]
      

        

    4. 发布资源配置

      php artisan vendor:publish --provider="TymonJWTAuthProvidersJWTAuthServiceProvider"
    5. 运行以下命令生成密钥key在生成的config/jwt.php 中

      // 如果运行后报错,提示ERROR:Method TymonJWTAuthCommandsJWTGenerateCommand::handle() does not exist,将vendor	ymonjwt-authsrcCommandsJWTGenerateCommand.php文件中的 fire() 方法修改为 handle()即可正常生成秘钥
      php artisan jwt:generate
    6. 编辑 app/Http/Kernel.php 添加 jwt.auth 和 jwt.refresh 到应用路由中间件数组:

      protected $routeMiddleware = [
          ...
          'jwt.auth' => TymonJWTAuthMiddlewareGetUserFromToken::class,
      //    'jwt.refresh' => TymonJWTAuthMiddlewareRefreshToken::class,
      ];
      

        

    7. JWTAuth 自身中间件TymonJWTAuthMiddlewareGetUserFromToken 中包含了对生成token的各类情况的验证,以及异常的抛出。下面是其底层验证类GetUserFromToken::class

      // file_path : vendor	ymonjwt-authsrcMiddlewareGetUserFromToken.php
      <?php
      ​
      /*
       * This file is part of jwt-auth.
       *
       * (c) Sean Tymon <tymon148@gmail.com>
       *
       * For the full copyright and license information, please view the LICENSE
       * file that was distributed with this source code.
       */
      ​
      namespace TymonJWTAuthMiddleware;
      ​
      use TymonJWTAuthExceptionsJWTException; //验证异常类
      use TymonJWTAuthExceptionsTokenExpiredException;//token过期异常验证类
      ​
      class GetUserFromToken extends BaseMiddleware
      {
          /**
           * Handle an incoming request.
           *
           * @param  IlluminateHttpRequest  $request
           * @param  Closure  $next
           * @return mixed
           */
          public function handle($request, Closure $next)
          {
              if (! $token = $this->auth->setRequest($request)->getToken()) {
                  return $this->respond('tymon.jwt.absent', 'token_not_provided', 400);
              }
      ​
              try {
                  $user = $this->auth->authenticate($token);
              } catch (TokenExpiredException $e) {
                  return $this->respond('tymon.jwt.expired', 'token_expired', $e->getStatusCode(), [$e]);
              } catch (JWTException $e) {
                  return $this->respond('tymon.jwt.invalid', 'token_invalid', $e->getStatusCode(), [$e]);
              }
      ​
              if (! $user) {
                  return $this->respond('tymon.jwt.user_not_found', 'user_not_found', 404);
              }
      ​
              $this->events->fire('tymon.jwt.valid', $user);
      ​
              return $next($request);
          }
      }
      

        

       

      其中,调用的respond 的方法在vendor ymonjwt-authsrcMiddlewareBaseMiddleware.php文件中

       /**
           * Fire event and return the response.
           *
           * @param  string   $event
           * @param  string   $error
           * @param  int  $status
           * @param  array    $payload
           * @return mixed
           */
          protected function respond($event, $error, $status, $payload = [])
          {
              $response = $this->events->fire($event, $payload, true);
      ​
              return $response ?: $this->response->json(['error' => $error], $status);
          }
      

        

      可看到,当出现异常需要返回错误信息时,会连带返回一个fire event 的警告事件对象,这里不做详述。

    8. 由底层代码中,可以了解到,我们如果想自定义自己所需要的验证方法,可以将这GetUserFromToken::class 内容复制到我们自己自定义的中间件中。比如:

      • 创建自定义的验证中间件AppHttpMiddlewareJwtAuth.php

        php artisan make:middleware JwtAuth
      • 全部复制到自定义的中间件中后,校验下中间件中需要的类是否应用完全,命名空间是否正确等等,检查无误后根据需要自行定义需要的验证功能。

        // demo 
        namespace AppHttpMiddleware;
        ​
        use TymonJWTAuthExceptionsJWTException;
        use TymonJWTAuthExceptionsTokenExpiredException;
        use Closure;
        use TymonJWTAuthMiddlewareBaseMiddleware;
        ​
        class JwtAuth extends BaseMiddleware
        {
            public function handle($request, Closure $next)
            {
                if (! $token = $this->auth->setRequest($request)->getToken()) {
                    return $this->respond('tymon.jwt.absent', 'token_not_provided', 400);
                }
        ​
                try {
                    $user = $this->auth->authenticate($token);
                } catch (TokenExpiredException $e) {
                    return $this->respond('tymon.jwt.expired', 'token_expired', $e->getStatusCode(), [$e]);
                } catch (JWTException $e) {
                    return $this->respond('tymon.jwt.invalid', 'token_invalid', $e->getStatusCode(), [$e]);
                }
        ​
                if (! $user) {
                    return $this->respond('tymon.jwt.user_not_found', 'user_not_found', 404);
                }
        ​
                $this->events->fire('tymon.jwt.valid', $user);
        ​
                return $next($request);
            }
        }
        

          

      • 定义完成后将自定义的中间件放入appHttpKernel.php的中间件数组中。

         protected $routeMiddleware = [
            ...
                //'jwt.auth' => TymonJWTAuthMiddlewareGetUserFromToken::class,
                'jwt.auth_self' => AppHttpMiddlewareJwtAuth::class
            ];
        

          

      • 添加好后,即可在routes/api.php 中对需要控制的api路由进行验证控制

        Route::group(['middleware'=>'jwt.auth_self'],function(){
            // 需要控制的api路由
            // ... code
        });
        

          

    9. 我们现在可以对请求来的路由进行token的验证,那么接下来我们就需要生成这个token,让后续访问中间件中的请求路由都携带这个token就能实现验证。这里要提一下在安装JWT-Auth过程中生成的配置文件config/jwt.php

      <?php
          return [
          ...
          /*
          |--------------------------------------------------------------------------
          | User Model namespace
          |--------------------------------------------------------------------------
          |
          | Specify the full namespace to your User model.
          | e.g. 'AcmeEntitiesUser'
          |
          */
          // 设置你的用户model,默认为laravel自带的 User model
          'user' => 'AppUser',
      ]
      

        

      如果需求需要,可在配置文件中修改用户mode ,但配置的model 中需要引用IlluminateFoundationAuthUser as Authenticatable,并继承,写法和User model一致

    10. 具体的请求登录以及获取token信息,登出等功能实现,可参考此文章

      Laravel 5 中使用 JWT(Json Web Token) 实现基于API的用户认证,这里简单提及下常用到的有关其token的方法

      <?php
          namespace AppHttpController;
          
          use TymonJWTAuthJWTAuth;
          
          class Auth{
              public function test (JWTAuth $JWTAuth){
                  
                  // 获取请求携带中的token
                  $token = $JWTAuth -> getToken();
                  // 获取token中的用户信息
                  $user = $JWTAuth -> parseToken() -> authenticate();
                  // 销毁此次生成的token
                  $JWTAuth->setToken( $JWTAuth->getToken() )->invalidate();
                  // 自定义生成token,如果需要
                  $JWTAuth->setToken('foo.bar.baz');
              }
          }
      

        

       
  • 相关阅读:
    【UVa 1592】Database
    【UVa 400】Unix ls
    【UVa 136】Ugly Numbers
    【UVa 540】Team Queue
    【Uva 12096】The SetStack Computer
    【POJ 1050】To the Max
    【UVa 156】Ananagrams
    【UVa 10815】Andy's First Dictionary
    [HNOI/AHOI2018]转盘
    CF46F Hercule Poirot Problem
  • 原文地址:https://www.cnblogs.com/rianley/p/11971233.html
Copyright © 2020-2023  润新知