Laravel + JWT 实现 API 跨域授权

简介配置 Laravel 5.6 + JWT + AngularJS 配置 Api 跨域授权访问,以及实现 Token 刷新。
因内容较多,涉及较广,这里只能概括的讲一下了。

安装配置

JWT

详细配置过程,请查看官方文档。

  • 安装 tymondesigns/jwt-auth
    composer require tymon/jwt-auth "1.0.0-rc.2"
  • 生成key php artisan jwt:secret
  • User 实现 JWTSubject 接口
  • 修改 auth.php 配置
    'api' => [
            'driver' => 'jwt',
            'provider' => 'users',
        ],
  • 一个【授权 + 刷新】 的中间件
    添加到 app/Http/Kernel.php

    protected $routeMiddleware = [
    ...
    'refresh.token' => \App\Http\Middleware\RefreshToken::class
    ...
    ];

    RefreshToken 中间件

    class RefreshToken extends BaseMiddleware
    {
    public function handle($request, Closure $next)
    {
        // 检查此次请求中是否带有 token,如果没有则抛出异常。
        $this->checkForToken($request);
        Log::debug($request->headers->all());
        // 使用 try 包裹,以捕捉 token 过期所抛出的 TokenExpiredException  异常
        try {
            // 检测用户的登录状态,如果正常则通过
            if ($this->auth->parseToken()->authenticate()) {
                return $next($request);
            }
            $token = $this->auth->refresh();
            // 使用一次性登录以保证此次请求的成功
    
        } catch (TokenExpiredException $exception) {
            // 此处捕获到了 token 过期所抛出的 TokenExpiredException 异常,我们在这里需要做的是刷新该用户的 token 并将它添加到响应头中
            try {
                // 刷新用户的 token
                $token = $this->auth->refresh();
                // 使用一次性登录以保证此次请求的成功
                Auth::guard('api')->onceUsingId($this->auth->manager()->getPayloadFactory()->buildClaimsCollection()->toPlainArray()['sub']);
            } catch (JWTException $exception) {
                // 如果捕获到此异常,即代表 refresh 也过期了,用户无法刷新令牌,需要重新登录。
                throw new UnauthorizedHttpException('jwt-auth', $exception->getMessage());
            }
        }
        // 在响应头中返回新的 token
        return $this->setAuthenticationHeader($next($request), $token);
    }
    }
    
  • 几个关键的配置
    • JWT_TTL Token的有效时间,单位:分,建议设置 20 min ~ 1 h。
    • JWT_REFRESH_TTL=1440 刷新后的 Token 有效时间,单位:分,建议设置在 7 ~ 30 day。
    • JWT_BLACKLIST_GRACE_PERIOD 防止并发请求导致某些请求失效,单位:秒,建议设置 10 ~ 60 s。

跨域

详细配置过程,请查看官方文档。

  • 安装 barryvdh/laravel-cors
  • 配置 cors
    在配置文件 cors.php 中,设置 exposedHeaders,不然无法跨域获取到 header 中的 Authorization 值来刷新前端令牌。
    'exposedHeaders' => ['Authorization'],

后端 Laravel

登录

  • 登录 Api 接口
    验证账号密码,返回 Token,我这里使用的 username 字段。
    Api返回,自己找一个返回success、error、message 的 Trait 工具就可以了,没必要用 dingo。

    class LoginController extends ApiController
    {
    use AuthenticatesUsers;
    
    public function login(LoginRequest $request)
    {
        $credentials = $this->credentials($request);
        if ($token = auth('api')->attempt($credentials)) {
            $user = auth('api')->user();
            return $this->success(
                [
                    'user' => $user,
                    'token' => $token,
                ]);
        }
        return $this->failed('账号或密码错误!');
    }
    
    public function username()
    {
        return 'username';
    }
    }
  • 路由
    Api 接口都要使用 api 中间件,控制器最好放在 \Api 命名空间下。
    Route::middleware('api')->prefix('api/user')->namespace('Modules\Account\Http\Controllers\Api')->group(function () {
            Route::post('login', 'LoginController@login')->name('user.login');
    });
  • 给需要授权的接口添加中间件,比如获取用户列表 getList
    public function __construct(UserRepository $repository)
    {
        $this->middleware('refresh.token')->only([
            'getList', 'get', 'update'
        ]);
        $this->repository = $repository;
    }

前端 Angular

Token

在登录授权时,将返回的 token 存储在 TokenService 中进行管理。

this.tokenService.set({
          token: res.data.token
 });

Interceptor

拦截器将每一个请求的头中放入 Authorization 信息。

setReq(req: HttpRequest<any>, options: DelonAuthConfig): HttpRequest<any> {
    return req.clone({
      setHeaders: {
        Authorization: `Bearer ${this.model.token}`,
      },
    });
  }

Refresh Token

如果返回的 Response 的 Header 中有 Authorization 信息,说明请求中带的 Token 已经过期,服务器将 刷新的 Token 放在了 Response 头中。前端获取后更新 Token 。

 let newAuth = event.headers.get('Authorization');
      if (newAuth) {
          let token = newAuth.slice(7);
          this.tokenService.set(Object.assign(this.tokenService.get(), {
              token: token,
          }));
      }
本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由系统于 6年前 自动加精
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 4

// 使用一次性登录以保证此次请求的成功 这条注释是什么意思?

6年前 评论

没成功啊,RefreshToken 中间件 这里不行最新版的

5年前 评论

最近开发小程序后整理的
tymon/jwt-auth+overtrue/laravel-wechat
有需要的拿去!lyne007/laravel-advanced-demo

3年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!