Tạo server JSON Web Tokens với laravel 10
Hôm nay mình sẽ hướng dẫn các bạn cách thiết lập backend JWT trên laravel để bảo mật dữ liệu khi gọi api
Bước 1
Cài đặt laravel với dữ liệu câu lệnh như sau :
composer create-project laravel/laravel laraveljwt
Ở bản mới đã dùng sqlite nên các bạn không cần cấu hình gì thêm nếu các bạn muốn dùng mysql thì config theo mysql của mình
Bước 2 Cài đặt và cấu hình package JWT Authentication
Ở đây mình dùng gói tymon/jwt-auth các bạn có thể tham khảo chi tiết ở đây : https://jwt-auth.readthedocs.io/en/develop/laravel-installation/
2.1 Cài đặt gói jwt với lệnh sau
composer require tymon/jwt-auth
2.2 Public các file cấu hình của gói jwt-auth
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
2.3 Tạo key bí mật
php artisan jwt:secret
Ta sẽ thấy file .env có thêm dòng
JWT_SECRET=TcDjnk1t3IPQV7YJt4BSZSk2SLkNmmWRuhj8IKcHV5ppRW58CElVyxDeC******
Ở cuối .Đây chính là key bí mật
Lưu ý nhỏ nên config thêm JWT_REFRESH_TTL để
Bước 3 : Cấu hình lại file user model
Ở đây do mình chỉ dùng lưu người dùng trên web mà không lưu thông tin người dùng admin hay nhà quảng cáo ,Mình cũng khuyên các bạn nên tách biệt ra như này để tránh lộ thông tin khi bị tấn công chiếm quyền
<?php
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
//use Illuminate\Foundation\Auth\User as Authenticatable;
use MongoDB\Laravel\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
use Tymon\JWTAuth\Contracts\JWTSubject;
class User extends Authenticatable implements JWTSubject
{
use HasApiTokens, HasFactory, Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
public function getJWTIdentifier()
{
// TODO: Implement getJWTIdentifier() method.
return $this->getKey();
}
public function getJWTCustomClaims()
{
// TODO: Implement getJWTCustomClaims() method.
return ['hoten'=>$this->name];
}
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}
Ở đây mình cần implement JWTSubject để dùng 2 phương thức
/**
* 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 [];
}
Bước 4 Config lại auth guard
<?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' => '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"
|
*/
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'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\Models\User::class,
],
// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
],
/*
|--------------------------------------------------------------------------
| 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 expiry time is the number of minutes that each reset token will be
| considered valid. This security feature keeps tokens short-lived so
| they have less time to be guessed. You may change this as needed.
|
| The throttle setting is the number of seconds a user must wait before
| generating more password reset tokens. This prevents the user from
| quickly generating a very large amount of password reset tokens.
|
*/
'passwords' => [
'users' => [
'provider' => 'users',
'table' => 'password_reset_tokens',
'expire' => 60,
'throttle' => 60,
],
],
/*
|--------------------------------------------------------------------------
| Password Confirmation Timeout
|--------------------------------------------------------------------------
|
| Here you may define the amount of seconds before a password confirmation
| times out and the user is prompted to re-enter their password via the
| confirmation screen. By default, the timeout lasts for three hours.
|
*/
'password_timeout' => 10800,
];
Bây giờ mọi phương thức gọi đến api đều xử dụng phương thức xác thực jwt
Bước 5 Config router api và tạo UserController
Tạo router api với lệnh php artisan install:api
Tại file routes/api.php
Route::get('login',[\App\Http\Controllers\AuthController::class,'login']);
Route::post('logout',[\App\Http\Controllers\AuthController::class,'logout']);
Route::post('register',[\App\Http\Controllers\AuthController::class,'register']);
Route::post('changerpassword',[\App\Http\Controllers\AuthController::class,'changerpassword']);
//Route::get('refresh',[\App\Http\Controllers\AuthController::class,'refresh']);
Route::post('refresh_token',[\App\Http\Controllers\AuthController::class,'refresh']);
Tại file app/Http/Controllers/UserController.php
<?php
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Facades\JWTAuth;
use Tymon\JWTAuth\Facades\JWTProvider;
class AuthController extends Controller
{
public function __construct()
{
$this->middleware('auth:api', ['except' => ['login', 'register', 'refresh_token']]);
}
public function login(Request $request)
{
// kiểm tra thông tin đăng nhập
$validator = Validator::make($request->all(), [
'email' => 'required|email',
'password' => 'required',
]);
if ($validator->fails()) {
return response(['status' => 0, 'message' => $validator->errors()], 422);
}
if (!$token = auth()->attempt($validator->validated())) {
return response()->json(['error' => 'Unauthorized'], 401);
}
$refresh_token = $this->createrefreshToken();
return $this->respondWithToken($token);
}
//đăng xuất
public function logout()
{
\auth()->logout();
return response()->json(['message' => 'Successfully logged out']);
}
// đăng kí
public function register(Request $request)
{
$validator = Validator::make($request->all(), [
'name' => 'required|string|between:2,100',
'email' => 'required|string|email|max:100|unique:users',
'password' => 'required|string|confirmed|min:6',
]);
if ($validator->fails()) {
return response()->json(['status' => 1, 'message' => $validator->errors()], 400);
}
$user = User::create(array_merge(
$validator->validated(),
['password' => bcrypt($request->password)]
));
return response()->json([
'message' => 'User successfully registered',
'user' => $user
], 201);
}
public function changerpassword(Request $request)
{
$validator = Validator::make($request->all(), [
'old_password' => 'required|string|between:2,100',
'new_password' => 'required|string|confirmed|min:6',
]);
if ($validator->fails()) {
return response()->json(['status' => 0, 'message' => $validator->errors()], 400);
}
$userId = auth()->user()->email;
if (! Hash::check($request->old_password ,Auth::user()->password))
{
return response()->json(['status' => 0, 'message' => 'Không đúng password'], 400);
}
$user = User::where('email','=', $userId)->update(
['password' => bcrypt($request->new_password)]
);
\auth()->logout();
return response()->json([
'message' => 'User successfully changed password',
'user' => $user,
], 201);
}
public function refresh(Request $request)
{
$refresh_token = $request->refresh_token;
// decode
try {
$data = JWTAuth::getJWTProvider()->decode($refresh_token);
$user = User::find($data['user_id']);
// xoá token cũ
auth()->invalidate(true);
// tạo token mới
$token = auth()->login($user);
return $this->respondWithToken($token);
} catch (JWTException $exception) {
return response()->json(['status' => 0, 'message' => $exception->getMessage()]);
}
//return $this->respondWithToken(auth()->refresh());
}
protected function createrefreshToken()
{
$data = [
'user_id' => Auth::id(),
'email' => Auth::user()->email,
];
$refresh_token = JWTAuth::getJWTProvider()->encode($data);
return $refresh_token;
}
protected function respondWithToken($token)
{
$refreshtoken = $this->createrefreshToken();
return response()->json([
'access_token' => $token,
'token_type' => 'bearer',
'expires_in' => env('jwt_ttl'),
'refresh_token' => $refreshtoken,
'exprires_refresh_token' => env('jwt_refresh_ttl'),
]);
}
}
Ở phần sau mình sẽ hướng dẫn các bạn cách bảo mật refresh token mà mình làm
Vậy là chúng ta đã cấu hình cơ bản được jwt ,hẹn gặp lại các bạn