菜单 学习猿地 - LMONKEY

VIP

开通学习猿地VIP

尊享10项VIP特权 持续新增

知识通关挑战

打卡带练!告别无效练习

接私单赚外块

VIP优先接,累计金额超百万

学习猿地私房课免费学

大厂实战课仅对VIP开放

你的一对一导师

每月可免费咨询大牛30次

领取更多软件工程师实用特权

入驻
1921
2

一步步搭建基础 API 开发环境 与 Jwt 的使用

原创
05/13 14:22
阅读数 1459

欢迎阅读该文章!

  1. 本教程使用laravel提供的auth组件来做的认证登录
  2. 本文教程只需要你安装了基本的laravel运行环境即可运行

一. 安装

安装tymon/jwt-auth,代码如下:

composer require tymon/jwt-auth ^1.0 // 安装

php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider" // 生成配置文件

php artisan jwt:secret // 初步密钥生成

二. 配置

认证配置修改

修改你的config/auth.php文件,如下:

'defaults' => [
    'guard' => 'api',
    'passwords' => 'users',
],
'guards' => [
    'api' => [
        'driver' => 'jwt',
        'provider' => 'users',
        'hash' => false,
    ],
],

修改你的App/User.php文件,如下:

<?php

namespace App;

use Tymon\JWTAuth\Contracts\JWTSubject;
use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable implements JWTSubject
{
    use Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];

    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];

    /**
     * Get the identifier that will be stored in the subject claim of the JWT.
     *
     * @return mixed
     */
    public function getJWTIdentifier()
    {
        return $this->getKey();
    }

    /**
     * Return a key value array, containing any custom claims to be added to the JWT.
     *
     * @return array
     */
    public function getJWTCustomClaims()
    {
        return [];
    }
}

这里解析下:
laravel中的默认使用web来进行认证的守卫,这里 jwt 主要是弄接口的嘛,所以自然而然的改成 api。守卫中的api驱动也改成咱们下载下来即将要用到的jwt。这样laravel的认证配置就大功告成了。

数据的初步生成

使用laravel的提供的auth,如下:

php artisan make:auth
  1. 好了,这是在database/migrations文件中生成了即将要迁移的用户数据表。
  2. 有了数据表还不够,咱们还需要往里面填充数据。执行php artisan make:seeder UsersTableSeeder命令在database/seeds文件夹中会生成UsersTableSeeder.php文件用来填充数据。该文件代码如下:
    use Illuminate\Database\Seeder;
    use App\User;
    use Illuminate\Contracts\Hashing\Hasher as HasherContract;
    class UsersTableSeeder extends Seeder
    {
        /**
         * Run the database seeds.
         *
         * @return void
         */
        public function run(HasherContract $hasher)
        {
            $arr = [
                [
                    'name' => 'jack',
                    'email' => '775893055@qq.com',
                    'password' => $hasher->make('123456'),
                ],
                [
                    'name' => 'hurry',
                    'email' => '1041224389@qq.com',
                    'password' => $hasher->make('123456'),
                ],
            ];
            User::insert($arr);
        }
    }
  3. 然后记得把seeds\DatabaseSeeder.php中的注释去掉
    <?php
    use Illuminate\Database\Seeder;
    class DatabaseSeeder extends Seeder
    {
    /**
     * Seed the application's database.
     *
     * @return void
     */
    public function run()
    {
        $this->call(UsersTableSeeder::class);
    }
    }
  4. .env文件中配置好你的数据库,执行:
    php artisan migrate --seed
  5. 路由配置,文件routes\api.php,如下:
    <?php
    use Illuminate\Http\Request;
    Route::middleware('auth:api')->get('/user', function (Request $request) {
    return $request->user();
    });
    Auth::routes();

三. 代码编写

Auth\LoginController.php 重写登录方法

<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
class LoginController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Login Controller
    |--------------------------------------------------------------------------
    |
    | This controller handles authenticating users for the application and
    | redirecting them to your home screen. The controller uses a trait
    | to conveniently provide its functionality to your applications.
    |
    */

    use AuthenticatesUsers;

    /**
     * Where to redirect users after login.
     *
     * @var string
     */
    protected $redirectTo = '/home';

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest')->except('logout');
    }

    /**
     * 重写登录方法(jwt)
     *
     * @param Request $request
     * @return mixed
     * @throws \Illuminate\Validation\ValidationException
     */
    public function login(Request $request)
    {
        $this->validateLogin($request);

        $credentials = $this->credentials($request);

        if (!$token = auth()->attempt($credentials)) {
            return $this->failed('登录失败,账号或者密码错误', '401');
        }

        return $this->success([
            'access_token' => $token,
            'token_type' => 'bearer',
            'user' => auth()->user(),
            'expires_in' => auth()->factory()->getTTL() * 60
        ]);
    }
}

Exceptions\Handler.php 确保异常返回的是 json 格式

<?php

namespace App\Exceptions;

use Exception;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Validation\ValidationException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
use App\Http\Controllers\Controller;

class Handler extends ExceptionHandler
{
    /**
     * A list of the exception types that are not reported.
     *
     * @var array
     */
    protected $dontReport = [
        //
    ];

    /**
     * A list of the inputs that are never flashed for validation exceptions.
     *
     * @var array
     */
    protected $dontFlash = [
        'password',
        'password_confirmation',
    ];

    /**
     * Report or log an exception.
     *
     * @param  \Exception  $exception
     * @return void
     */
    public function report(Exception $exception)
    {
        parent::report($exception);
    }

    /**
     * Render an exception into an HTTP response.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Exception  $exception
     * @return \Illuminate\Http\Response
     */
    public function render($request, Exception $exception)
    {
        if ($request->is("api/*")) {
            if ($exception instanceof NotFoundHttpException) {
                return (new Controller())->failed('路由没找到.');
            }

            if ($exception instanceof ValidationException) {
                return (new Controller())->failed($exception->validator->errors()->first());
            }

            if ($exception->getPrevious() instanceof TokenExpiredException) {
                return (new Controller())->failed('登录超时,请重新登录', 401);
            }

            return (new Controller())->failed($exception->getMessage());
        }
        return parent::render($request, $exception);
    }
}

Controllers\Controllers.php 封装返回的统一消息

<?php

namespace App\Http\Controllers;

use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Response;
use Illuminate\Support\Facades\DB;
use Symfony\Component\HttpFoundation\Response as FoundationResponse;

class Controller extends BaseController
{
    use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
    /**
     * @var int
     */
    protected $statusCode = FoundationResponse::HTTP_OK;

    /**
     * 获取响应http状态码
     *
     * @return mixed
     */
    public function getStatusCode()
    {
        return $this->statusCode;
    }

    /**
     * 设置响应http状态码
     *
     * @param $statusCode
     * @return $this
     */
    public function setStatusCode($statusCode)
    {
        $this->statusCode = $statusCode;
        return $this;
    }

    /**
     * 封装的底层http响应
     *
     * @param       $data
     * @param array $header
     * @return mixed
     */
    public function respond($data, $header = [])
    {
        return Response::json($data, $this->getStatusCode(), $header);
    }

    /**
     * 封装status
     *
     * @param       $status
     * @param array $data
     * @param null $code
     * @return mixed
     */
    public function status($status, array $data, $code = null)
    {
        if ($code) {
            $this->setStatusCode($code);
        }

        $status = [
            'status' => $status,
            'code' => $this->statusCode
        ];

        if (config('app.debug')) {
            $status['logs'] = DB::getQueryLog();
        }

        $data = array_merge($status, $data);

        return $this->respond($data);
    }

    /**
     * 消息响应
     *
     * @param        $message
     * @param string $status
     * @return mixed
     */
    public function message($message, $status = "success")
    {
        return $this->status($status, [
            'message' => $message
        ]);
    }

    /**
     * 创建成功响应
     *
     * @param string $message
     * @return mixed
     */
    public function created($message = "created")
    {
        return $this->setStatusCode(FoundationResponse::HTTP_CREATED)
            ->message($message);
    }

    /**
     * 通用返回
     *
     * @param $data
     * @return mixed
     */
    public function returnMsg($data)
    {
        if ($data['success'] == true) {
            if ($data['data']) {
                return $this->success($data['data']);
            }
            return $this->message($data['msg']);
        } else {
            return $this->failed($data['msg']);
        }
    }

    /**
     * 成功响应
     *
     * @param        $data
     * @param string $status
     * @return mixed
     */
    public function success($data, $status = "success")
    {
        if (isset($data['success']) && $data['success'] === false) {
            return $this->returnMsg($data);
        }
        return $this->status($status, compact('data'));
    }

    /**
     * 失败响应
     *
     * @param        $message
     * @param int $code
     * @param string $status
     * @return mixed
     */
    public function failed($message = '操作失败', $code = FoundationResponse::HTTP_BAD_REQUEST, $status = 'error')
    {
        return $this->setStatusCode($code)->message($message, $status);
    }

    /**
     * HTTP内部服务器错误
     *
     * @param string $message
     * @return mixed
     */
    public function internalError($message = "Internal Error!")
    {
        return $this->failed($message, FoundationResponse::HTTP_INTERNAL_SERVER_ERROR);
    }

    /**
     * 不存在的api
     *
     * @param string $message
     * @return mixed
     */
    public function notFond($message = 'Not Fond!')
    {
        return $this->failed($message, FoundationResponse::HTTP_NOT_FOUND);
    }

    /**
     * 接口未授权
     *
     * @param string $message
     * @return mixed
     */
    public function unAuthorized($message = "Unauthorized!")
    {
        return $this->failed($message, FoundationResponse::HTTP_UNAUTHORIZED);
    }

    /**
     * 没有权限操作指定资源
     *
     * @param string $message
     * @return mixed
     */
    public function forbidden($message = "Forbidden!")
    {
        return $this->failed($message, FoundationResponse::HTTP_FORBIDDEN);
    }
}

好了,完成上面代码的编写之后,咱们就可以登录了。访问接口/api/login,进行登录:

在 Laravel 中 Jwt 的使用 与 基础 API 开发的搭建
emmmm,可以看到有返回到的token。接下来就把这个token带上头部就可以进行用户的认证了。
具体的方法是 Authorization: bearer +你的token,结合下面的中间键使用。基本满足用户认证,单点登录的需求了啦。

四. 中间键

Http\Kernel.php文件中$routeMiddleware数组:加上一行:

'jwt.auth' => \Tymon\JWTAuth\Http\Middleware\AuthenticateAndRenew::class,

这样,你就可以在需要认证的路由上面加上这个中间键就可以了。

五. 获取当前认证用户的方法

auth()->user() 或 $request->user()

好了。差不多就这样了。这里涵盖了laravel中api的基本搭建。

为了方便大家的理解:我弄了个小小的demo放上了github上面:

  1. 前端github地址:前端
  2. 后端github地址:后端

最后,如果发现有什么问题,错误的,私信我。尽快修正。

如果想搭建一个更具细节的Api开发环境,安利一篇博文:博客:手摸手教你让 Laravel 开发 API 更得心应手

努力,加油。

发表评论

0/200
1921 点赞
2 评论
收藏
为你推荐 换一批