Xác thực mã thông báo web JSON cho Lumen 5 sử dụng tymon / jwt-auth
White

Tân Nguyễn viết ngày 23/01/2018

Việc triển khai JWT (JSON Web Token) thành Lumen 5.3 có vẻ như là một khu vực khó hiểu đối với hầu hết các nhà phát triển hiện nay do sự thay đổi cả Lumen và tymon / jwt-auth .

Bạn có thể muốn JWT sử dụng nó cùng với Socialite (Facebook, GitHub, ... integration), AngularJs, ... Nó đã là một tiêu chuẩn IETF , vì vậy sử dụng nó là một ý tưởng tốt (congrats!).

Các bước tôi mô tả ở đây sẽ cho bạn một ví dụ làm việc với:

  • Lumen 5.3
  • jwt-auth 1.0.0-beta.3@dev

- Repo Github

Nếu bạn không muốn đọc toàn bộ điều, bạn chỉ có thể kiểm tra repo tôi tạo ra cho bạn trong github.

Nó có chứa mã rất giống tôi đặt ở đây

https://github.com/tannguyenit/lumen

- Chuẩn bị

Trong phần này, chúng ta sẽ:

  • Tạo một dự án Lumen (5.5.2) (Laravel Components 5.5.*)
  • Tạo một cơ sở dữ liệu
  • Tạo một số người dùng

Nếu bạn đã biết Lumen, bạn chỉ cần nhảy về phía trước phần tiếp theo .

Tài liệu chính thức của Lumen / Laravel đã cung cấp thông tin rất tốt về cách tạo và triển khai Lumen, vì vậy tôi sẽ không giải thích nhiều.

Tôi thích tạo dự án:

composer create-project --prefer-dist laravel/lumen:"^5.3" lumen-jwt

Sau đó, tôi sao chép cấu hình:

cd lumen-jwt
cp .env.example .env

Bạn hãy thay đổi .env :

    APP_ENV=local
    APP_DEBUG=true
    APP_KEY=XRyRe3QFqXh91emMHfjCVywTwXojPvjr

    DB_CONNECTION=mysql
    DB_HOST=localhost
    DB_USERNAME=homestead
    DB_PASSWORD=secret

    CACHE_DRIVER=file

Hãy cẩn thận: nhớ thay đổi APP_KEY của riêng bạn. Lumen không cung cấp công cụ để tạo ra nó.

Tạo migration cho bảng người dùng :

php artisan make:migration create_users_table

Sửa đổi tệp được tạo ra (đối với tôi đã được database/migrations/2018_01_23_024751_create_users_table.php) :

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('email')->unique();
            $table->string('password');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('users');
    }
}

Sửa đổi database/factories/ModelFactory.php :

<?php

/*
|--------------------------------------------------------------------------
| Model Factories
|--------------------------------------------------------------------------
|
| Here you may define all of your model factories. Model factories give
| you a convenient way to create models for testing and seeding your
| database. Just tell the factory how a default model should look.
|
*/

$factory->define(App\User::class, function (Faker\Generator $faker) {
    return [
        'name' => $faker->name,
        'email' => $faker->unique()->email,
    ];
});

Để tạo seeder (nó sẽ thêm vào cơ sở dữ liệu của bạn với một số người dùng), sửa đổi database/seeds/UsersTableSeeder.php để trông như:

<?php

use Illuminate\Database\Seeder;

class UsersTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        factory(App\User::class)->create([
            'email' => 'user1@example.com',
            'password' => app('hash')->make('1234')
        ]);

        factory(App\User::class)->create([
            'email' => 'user2@example.com',
            'password' => app('hash')->make('1234')
        ]);

        factory(App\User::class)->create([
            'email' => 'user3@example.com',
            'password' => app('hash')->make('1234')
        ]);
    }
}

Quan trọng: Bây giờ bạn phải tạo lại trình nạp tự động (Lumen vẫn không biết về lớp UsersTableSeeder):

composer dump-autoload

Sửa đổi database/seeds/DatabaseSeeder.php để giống như thế này:

<?php

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        Model::unguard();

        $this->call(UsersTableSeeder::class);

        Model::reguard();
    }
}

Tạo cơ sở dữ liệu và thêm data vào:

    php artisan migrate
    php artisan db:seed

Bạn đã sẵn sàng để tích hợp JWT vào dự án.

- Tích hợp JWT

Thêm thư viện JWT vào dự án của bạn bằng composer:

composer require tymon/jwt-auth:"^1.0.0-beta.3@dev"

Tạo một key sẽ được sử dụng để tạo các mã thông báo:

php artisan jwt:secret

Để cho phép Middleware và Dịch vụ liên quan đến Xác thực / Cấp phép, hãy bỏ comment hoặc thêm các dòng này vào
bootstrap/app.php:

$app->withFacades();    // cho phép gọi các Class mà không cần phải đúng các namespace

$app->withEloquent(); // cho phép sử dụng eloquent của Laravel 

$app->routeMiddleware([
    'auth' => App\Http\Middleware\Authenticate::class,
]);

$app->register(App\Providers\AuthServiceProvider::class);
$app->register(Tymon\JWTAuth\Providers\LumenServiceProvider::class);

Tạo tệp để cấu hình dịch vụ Xác thực config/auth.php:

<?php

return [
    /*
    |--------------------------------------------------------------------------
    | Authentication Defaults
    |--------------------------------------------------------------------------
    |
    | This option controls the default authentication "guard" and password
    | reset options for your application. You may change these defaults
    | as required, but they're a perfect start for most applications.
    |
    */
    'defaults' => [
        'guard' => env('AUTH_GUARD', 'api'),
        'passwords' => 'users',
    ],
    /*
    |--------------------------------------------------------------------------
    | Authentication Guards
    |--------------------------------------------------------------------------
    |
    | Next, you may define every authentication guard for your application.
    | Of course, a great default configuration has been defined for you
    | here which uses session storage and the Eloquent user provider.
    |
    | All authentication drivers have a user provider. This defines how the
    | users are actually retrieved out of your database or other storage
    | mechanisms used by this application to persist your user's data.
    |
    | Supported: "session", "token"
    |
    */
    'guards' => [
        'api' => [
            'driver' => 'jwt',
            'provider' => 'users',
        ],
    ],
    /*
    |--------------------------------------------------------------------------
    | User Providers
    |--------------------------------------------------------------------------
    |
    | All authentication drivers have a user provider. This defines how the
    | users are actually retrieved out of your database or other storage
    | mechanisms used by this application to persist your user's data.
    |
    | If you have multiple user tables or models you may configure multiple
    | sources which represent each model / table. These sources may then
    | be assigned to any extra authentication guards you have defined.
    |
    | Supported: "database", "eloquent"
    |
    */
    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\User::class,
        ],
    ],
    /*
    |--------------------------------------------------------------------------
    | Resetting Passwords
    |--------------------------------------------------------------------------
    |
    | You may specify multiple password reset configurations if you have more
    | than one user table or model in the application and you want to have
    | separate password reset settings based on the specific user types.
    |
    | The expire time is the number of minutes that the reset token should be
    | considered valid. This security feature keeps tokens short-lived so
    | they have less time to be guessed. You may change this as needed.
    |
    */
    'passwords' => [
        'users' => [
            'provider' => 'users',
            'table' => 'password_resets',
            'expire' => 60,
        ],
    ],
];

Tạo tệp để định cấu hình dịch vụ JWT config/jwt.php:

<?php

return [
    /*
    |--------------------------------------------------------------------------
    | JWT Authentication Secret
    |--------------------------------------------------------------------------
    |
    | Don't forget to set this in your .env file, as it will be used to sign
    | your tokens. A helper command is provided for this:
    | `php artisan jwt:secret`
    |
    | Note: This will be used for Symmetric algorithms only (HMAC),
    | since RSA and ECDSA use a private/public key combo (See below).
    |
    */
    'secret' => env('JWT_SECRET'),

    /*
    |--------------------------------------------------------------------------
    | JWT Authentication Keys
    |--------------------------------------------------------------------------
    |
    | What algorithm you are using, will determine whether your tokens are
    | signed with a random string (defined in `JWT_SECRET`) or using the
    | following public & private keys.
    |
    | Symmetric Algorithms:
    | HS256, HS384 & HS512 will use `JWT_SECRET`.
    |
    | Asymmetric Algorithms:
    | RS256, RS384 & RS512 / ES256, ES384 & ES512 will use the keys below.
    |
    */
    'keys' => [
        /*
        |--------------------------------------------------------------------------
        | Public Key
        |--------------------------------------------------------------------------
        |
        | A path or resource to your public key.
        |
        | E.g. 'file://path/to/public/key'
        |
        */
        'public' => env('JWT_PUBLIC_KEY'),
        /*
        |--------------------------------------------------------------------------
        | Private Key
        |--------------------------------------------------------------------------
        |
        | A path or resource to your private key.
        |
        | E.g. 'file://path/to/private/key'
        |
        */
        'private' => env('JWT_PRIVATE_KEY'),
        /*
        |--------------------------------------------------------------------------
        | Passphrase
        |--------------------------------------------------------------------------
        |
        | The passphrase for your private key. Can be null if none set.
        |
        */
        'passphrase' => env('JWT_PASSPHRASE'),
    ],

    /*
    |--------------------------------------------------------------------------
    | JWT time to live
    |--------------------------------------------------------------------------
    |
    | Specify the length of time (in minutes) that the token will be valid for.
    | Defaults to 1 hour.
    |
    | You can also set this to null, to yield a never expiring token.
    | Some people may want this behaviour for e.g. a mobile app.
    | This is not particularly recommended, so make sure you have appropriate
    | systems in place to revoke the token if necessary.
    |
    */
    'ttl' => env('JWT_TTL', 60),

    /*
    |--------------------------------------------------------------------------
    | Refresh time to live
    |--------------------------------------------------------------------------
    |
    | Specify the length of time (in minutes) that the token can be refreshed
    | within. I.E. The user can refresh their token within a 2 week window of
    | the original token being created until they must re-authenticate.
    | Defaults to 2 weeks.
    |
    | You can also set this to null, to yield an infinite refresh time.
    | Some may want this instead of never expiring tokens for e.g. a mobile app.
    | This is not particularly recommended, so make sure you have appropriate
    | systems in place to revoke the token if necessary.
    |
    */
    'refresh_ttl' => env('JWT_REFRESH_TTL', 20160),

    /*
    |--------------------------------------------------------------------------
    | JWT hashing algorithm
    |--------------------------------------------------------------------------
    |
    | Specify the hashing algorithm that will be used to sign the token.
    |
    | See here: https://github.com/namshi/jose/tree/master/src/Namshi/JOSE/Signer/OpenSSL
    | for possible values.
    |
    */
    'algo' => env('JWT_ALGO', 'HS256'),

    /*
    |--------------------------------------------------------------------------
    | Required Claims
    |--------------------------------------------------------------------------
    |
    | Specify the required claims that must exist in any token.
    | A TokenInvalidException will be thrown if any of these claims are not
    | present in the payload.
    |
    */
    'required_claims' => ['iss', 'iat', 'exp', 'nbf', 'sub', 'jti'],

    /*
    |--------------------------------------------------------------------------
    | Blacklist Enabled
    |--------------------------------------------------------------------------
    |
    | In order to invalidate tokens, you must have the blacklist enabled.
    | If you do not want or need this functionality, then set this to false.
    |
    */
    'blacklist_enabled' => env('JWT_BLACKLIST_ENABLED', true),

    /*
    | -------------------------------------------------------------------------
    | Blacklist Grace Period
    | -------------------------------------------------------------------------
    |
    | When multiple concurrent requests are made with the same JWT,
    | it is possible that some of them fail, due to token regeneration
    | on every request.
    |
    | Set grace period in seconds to prevent parallel request failure.
    |
    */
    'blacklist_grace_period' => env('JWT_BLACKLIST_GRACE_PERIOD', 0),

    /*
    |--------------------------------------------------------------------------
    | Providers
    |--------------------------------------------------------------------------
    |
    | Specify the various providers used throughout the package.
    |
    */
    'providers' => [
        /*
        |--------------------------------------------------------------------------
        | JWT Provider
        |--------------------------------------------------------------------------
        |
        | Specify the provider that is used to create and decode the tokens.
        |
        */
        'jwt' => Tymon\JWTAuth\Providers\JWT\Namshi::class,
        /*
        |--------------------------------------------------------------------------
        | Authentication Provider
        |--------------------------------------------------------------------------
        |
        | Specify the provider that is used to authenticate users.
        |
        */
        'auth' => Tymon\JWTAuth\Providers\Auth\Illuminate::class,
        /*
        |--------------------------------------------------------------------------
        | Storage Provider
        |--------------------------------------------------------------------------
        |
        | Specify the provider that is used to store tokens in the blacklist.
        |
        */
        'storage' => Tymon\JWTAuth\Providers\Storage\Illuminate::class,
    ],
];

Chỉnh sửa method boot() trong app/Providers/AuthServiceProvider.php :

    public function boot()
    {
        // Here you may define how you wish users to be authenticated for your Lumen
        // application. The callback which receives the incoming request instance
        // should return either a User instance or null. You're free to obtain
        // the User instance via an API token or any other method necessary.

        $this->app['auth']->viaRequest('api', function ($request) {
            if ($request->input('email')) {
                return User::where('email', $request->input('email'))->first();
            }
        });
    }

Sửa app/User.php implement JWTSubject ;

<?php

namespace App;

use Illuminate\Auth\Authenticatable;
use Laravel\Lumen\Auth\Authorizable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
use Tymon\JWTAuth\Contracts\JWTSubject;

class User extends Model implements AuthenticatableContract, AuthorizableContract, JWTSubject
{
    use Authenticatable, Authorizable;

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

    /**
     * The attributes excluded from the model's JSON form.
     *
     * @var array
     */
    protected $hidden = [
        'password',
    ];

    /**
     * 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 [];
    }
}

Xong việc setup :)
Chúng ta chỉ cần sử dụng nó ngay bây giờ. Chúng ta sẽ cần:

Tạo router để lấy mã thông báo JWT

- Route

Trước hết, tạo một router ở file app/Http/routes.php bằng cách thêm:

$app->POST('/auth/login', 'AuthController@loginPost');

Bây giờ, tạo bộ điều khiển bằng cách tạo và chỉnh sửa app/Http/Controllers/AuthController.php :

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
use Tymon\JWTAuth\Exceptions\TokenInvalidException;
use Tymon\JWTAuth\JWTAuth;

class AuthController extends Controller
{
    /**
     * @var \Tymon\JWTAuth\JWTAuth
     */
    protected $jwt;

    public function __construct(JWTAuth $jwt)
    {
        $this->jwt = $jwt;
    }

    public function loginPost(Request $request)
    {
        $this->validate($request, [
            'email'    => 'required|email|max:255',
            'password' => 'required',
        ]);

        try {
            if (! $token = $this->jwt->attempt($request->only('email', 'password'))) {
                return response()->json(['user_not_found'], 404);
            }
        } catch (TokenExpiredException $e) {
            return response()->json(['token_expired'], $e->getStatusCode());
        } catch (TokenInvalidException $e) {
            return response()->json(['token_invalid'], $e->getStatusCode());
        } catch (JWTException $e) {
            return response()->json(['token_absent' => $e->getMessage()], $e->getStatusCode());
        }

        return response()->json(compact('token'));
    }
}

Bây giờ bạn đã thực sự hoàn toàn sẵn sàng.
Nào, giờ chúng ta hãy check API response bằng Postman.

alt text

Sau khi đăng nhập thành công, ta sẽ nhận được JWT token. Ví dụ của mình sẽ có dạng như này:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vZGVtb2x1bWVuLnRlc3QvYXBpL2F1dGgvbG9naW4iLCJpYXQiOjE1MTY2NzY5OTMsImV4cCI6MTUxNjY4MDU5MywibmJmIjoxNTE2Njc2OTkzLCJqdGkiOiJIdjIxbFNpVERFYmlsSWpSIiwic3ViIjoyLCJwcnYiOiI4N2UwYWYxZWY5ZmQxNTgxMmZkZWM5NzE1M2ExNGUwYjA0NzU0NmFhIn0.FfR8qKCx4SUPYvURPGE9PTiZvNA_-3GUFOBvDoHF42c

Nếu bạn để ý kỹ thì JWT token mà chúng ta có được bao gồm 3 thành phần chính, phân cách nhau bởi dấu dot (.), đó là:

  • Header
  • Payload
  • Signature
Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

Tân Nguyễn

4 bài viết.
2 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
14 0
Q: Chào anh, Anh có thể chia sẻ về việc sử dụng esb trong esb và các cách liên lạc giữa các service cũng như API Gateway được không a. A: ESB là k...
Tân Nguyễn viết 23 ngày trước
14 0
White
4 0
Microservices hiện được quan tâm trong giới phần mềm, công nghệ với nhiều bài viết, blog, thảo luận, truyền thông, hội thảo. Kỳ vọng về khả năng củ...
Tân Nguyễn viết 22 ngày trước
4 0
White
1 0
Bài viết đầu tiên trong loạt 7 phần về thiết kế, xây dựng và triển khai các dịch vụ nhỏ (microservices) đã giới thiệu về mô hình kiến trúc Microser...
Tân Nguyễn viết 18 ngày trước
1 0
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
4 bài viết.
2 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!