전체 사용자 인증 및 액세스 제어 – A Laravel Passport Tutorial, Pt. 1
게시 됨: 2022-03-11웹 애플리케이션을 개발할 때 일반적으로 두 계층으로 분할하는 것이 좋습니다. 중간 계층 API는 데이터베이스와 상호 작용하며 웹 계층은 일반적으로 프런트 엔드 SPA 또는 MPA로 구성됩니다. 이렇게 하면 웹 응용 프로그램이 더 느슨하게 연결되어 장기적으로 관리 및 디버그가 더 쉬워집니다.
API가 생성되면 상태 비저장 API 컨텍스트에서 인증 및 상태를 설정하는 것이 다소 문제가 될 수 있습니다.
이 기사에서는 Laravel과 Passport를 사용하여 API에서 전체 사용자 인증과 간단한 형식의 액세스 제어를 구현하는 방법을 살펴보겠습니다. 이것은 입문 튜토리얼이 아니므로 Laravel을 사용한 경험이 있어야 합니다.
설치 전제 조건:
- PHP 7+, MySQL, Apache(3개를 한번에 설치하려는 개발자는 XAMPP를 사용할 수 있습니다.)
- 작곡가
- 라라벨 7
- 라라벨 여권. API는 일반적으로 상태 비저장이고 세션을 사용하지 않기 때문에 일반적으로 토큰을 사용하여 요청 간에 상태를 유지합니다. Laravel은 Passport 라이브러리를 사용하여 API에서 인증에 사용할 수 있는 전체 OAuth2 서버를 구현합니다.
- API 테스트를 위한 Postman, cURL 또는 Insomnia - 이것은 개인의 취향에 달려 있습니다.
- 원하는 텍스트 편집기
- Laravel 도우미(Laravel 6.0 이상용) - Laravel 및 Passport를 설치한 후 다음을 실행합니다.
composer require laravel/helpers
위의 내용이 설치되었으면 시작할 준비가 되었습니다. .env
파일을 편집하여 데이터베이스 연결을 설정해야 합니다.
Laravel Passport 튜토리얼, 1단계: 더미 요청을 위한 컨트롤러 및 모델 추가
먼저 더미 요청을 위한 컨트롤러와 모델을 만들 것입니다. 이 모델은 이 튜토리얼에서 많이 사용되지 않을 것이며 컨트롤러가 조작할 데이터에 대한 아이디어를 제공하기 위한 것입니다.
모델과 컨트롤러를 생성하기 전에 마이그레이션을 생성해야 합니다. 터미널(Windows를 사용하는 경우 cmd.exe
창)에서 다음을 실행합니다.
php artisan make:migration create_articles_table --create=articles
이제 database/migrations
폴더로 이동하여 xxxx_xx_xx_xxxxxx_create_articles_table.php
와 유사한 이름의 파일을 엽니다.
클래스의 up
함수에서 다음과 같이 작성합니다.
Schema::create('articles', function (Blueprint $table) { $table->increments('id'); $table->string('title'); $table->string('body'); $table->integer('user_id'); $table->timestamps(); });
다음으로 Article
모델을 생성하겠습니다. 그렇게 하려면 다음을 실행하십시오.
php artisan make:model Article
그런 다음 다음을 실행하여 ArticleController
컨트롤러를 만듭니다.
php artisan make:controller ArticleController --resource
다음으로 app/Providers/AppServiceProvider.php
파일을 편집하고 다음을 추가하여 Illuminate\Support\Facades\Schema
클래스를 가져옵니다.
use Illuminate\Support\Facades\Schema
... 파일의 맨 위에서 가져오기의 맨 아래로.
그런 다음 boot
기능에서 다음을 작성합니다.
Schema::defaultStringLength(191);
이 모든 작업이 끝나면 다음을 실행할 수 있습니다.
php artisan migrate
… 위에서 만든 마이그레이션을 적용합니다.
Laravel Passport 튜토리얼, 2단계: 필요한 미들웨어 조각 만들기
여기에 API가 작동하는 데 필요한 미들웨어 조각을 추가합니다.
JSON 응답
필요한 첫 번째 부분은 모든 응답을 JSON으로 자동 변환하는 ForceJsonResponse
미들웨어입니다.
이렇게 하려면 다음을 실행합니다.
php artisan make:middleware ForceJsonResponse
그리고 이것은 App/Http/Middleware/ForceJsonReponse.php
에 있는 해당 미들웨어의 핸들 기능입니다.
public function handle($request, Closure $next) { $request->headers->set('Accept', 'application/json'); return $next($request); }
다음으로 $routeMiddleware
배열의 app/Http/Kernel.php
파일에 미들웨어를 추가합니다.
'json.response' => \App\Http\Middleware\ForceJsonResponse::class,
그런 다음 동일한 파일의 $middleware
배열에도 추가합니다.
\App\Http\Middleware\ForceJsonResponse::class,
그러면 ForceJsonResponse
미들웨어가 모든 요청에서 실행됩니다.
CORS(교차 출처 리소스 공유)
Laravel REST API의 소비자가 다른 출처에서 액세스할 수 있도록 하려면 CORS를 설정해야 합니다. 그렇게 하기 위해 Cors
라는 미들웨어를 만들 것입니다.
터미널 또는 명령 프롬프트에서 프로젝트 루트 디렉터리로 cd
하고 다음을 실행합니다.
php artisan make:middleware Cors
그런 다음 app/Http/Middleware/Cors.php
에 다음 코드를 추가합니다.
public function handle($request, Closure $next) { return $next($request) ->header('Access-Control-Allow-Origin', '*') ->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS') ->header('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, X-Token-Auth, Authorization'); }
이 미들웨어 조각을 로드하려면 app/Http/Kernel.php
의 $routeMiddleware
배열에 한 줄을 추가해야 합니다.
'cors' => \App\Http\Middleware\Cors::class,
또한 이전 미들웨어에서 했던 것처럼 $middleware
배열에 추가해야 합니다.
\App\Http\Middleware\Cors::class,
그런 다음 이 경로 그룹을 routes/api.php
에 추가합니다.
Route::group(['middleware' => ['cors', 'json.response']], function () { // ... });
아래에서 볼 수 있듯이 모든 API 경로는 해당 함수로 이동합니다.
Laravel Passport 튜토리얼, 3단계: API용 사용자 인증 컨트롤러 생성
이제 login
및 register
기능이 있는 인증 컨트롤러를 만들고 싶습니다.
먼저 다음을 실행합니다.
php artisan make:controller Auth/ApiAuthController
이제 일부 클래스를 app/Http/Controllers/Auth/ApiAuthController.php
파일로 가져옵니다. 이 클래스는 login
및 register
기능을 만드는 데 사용됩니다. 다음을 추가하여 클래스를 가져올 것입니다.
use App\User; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Validator; use Illuminate\Support\Str;
...컨트롤러 상단으로.
이제 사용자를 위한 Laravel API 인증을 추가하기 위해 동일한 파일에 login
, logout
및 register
(가입) 기능을 만들 것입니다.
register
기능은 다음과 같습니다.
public function register (Request $request) { $validator = Validator::make($request->all(), [ 'name' => 'required|string|max:255', 'email' => 'required|string|email|max:255|unique:users', 'password' => 'required|string|min:6|confirmed', ]); if ($validator->fails()) { return response(['errors'=>$validator->errors()->all()], 422); } $request['password']=Hash::make($request['password']); $request['remember_token'] = Str::random(10); $user = User::create($request->toArray()); $token = $user->createToken('Laravel Password Grant Client')->accessToken; $response = ['token' => $token]; return response($response, 200); }
login
기능은 다음과 같습니다.
public function login (Request $request) { $validator = Validator::make($request->all(), [ 'email' => 'required|string|email|max:255', 'password' => 'required|string|min:6|confirmed', ]); if ($validator->fails()) { return response(['errors'=>$validator->errors()->all()], 422); } $user = User::where('email', $request->email)->first(); if ($user) { if (Hash::check($request->password, $user->password)) { $token = $user->createToken('Laravel Password Grant Client')->accessToken; $response = ['token' => $token]; return response($response, 200); } else { $response = ["message" => "Password mismatch"]; return response($response, 422); } } else { $response = ["message" =>'User does not exist']; return response($response, 422); } }
마지막으로 logout
기능은 다음과 같습니다.
public function logout (Request $request) { $token = $request->user()->token(); $token->revoke(); $response = ['message' => 'You have been successfully logged out!']; return response($response, 200); }
그런 다음, 우리의 경로에 login
, register
및 logout
기능을 추가해야 합니다. 즉, API에 이미 있는 경로 그룹 내입니다.
Route::group(['middleware' => ['cors', 'json.response']], function () { // ... // public routes Route::post('/login', 'Auth\ApiAuthController@login')->name('login.api'); Route::post('/register','Auth\ApiAuthController@register')->name('register.api'); Route::post('/logout', 'Auth\ApiAuthController@logout')->name('logout.api'); // ... });
마지막으로 User
모델에 HasApiToken
특성을 추가해야 합니다. app/User
로 이동하여 다음 사항이 있는지 확인합니다.
use HasApiTokens, Notifiable;
... 클래스의 맨 위에.
지금까지 우리가 가진 것…
애플리케이션 서버를 시작한 다음(예: php artisan serve
실행) GET
요청을 /api/user
라우트에 보내려고 하면 다음 메시지를 받아야 합니다.
{ "message": "Unauthenticated." }
이는 해당 경로에 액세스하도록 인증되지 않았기 때문입니다. 선택한 경로를 보호하기 위해 Route::post
행 바로 뒤에 routes/api.php
에 추가할 수 있습니다.
Route::middleware('auth:api')->group(function () { // our routes to be protected will go in here });
계속 진행하기 전에 Laravel이 토큰을 사용하여 사용자를 로그아웃하기 때문에 auth:api
미들웨어에 로그아웃 경로를 추가합니다. 토큰은 auth:api
미들웨어 외부에서 액세스할 수 없는 토큰입니다. 공개 경로는 다음과 같습니다.
Route::group(['middleware' => ['cors', 'json.response']], function () { // ... // public routes Route::post('/login', 'Auth\ApiAuthController@login')->name('login.api'); Route::post('/register', 'Auth\ApiAuthController@register')->name('register.api'); // ... });
반면에 보호 경로는 다음과 같습니다.
Route::middleware('auth:api')->group(function () { // our routes to be protected will go in here Route::post('/logout', 'Auth\ApiAuthController@logout')->name('logout.api'); });
이제 app/Http/Controllers/ArticleController.php
에서 생성한 ArticleController
로 이동하고 해당 클래스에서 create
및 edit
메서드를 삭제합니다. 그 후, 나머지 각 함수에 약간 편집된 다음 코드를 추가합니다.
$response = ['message' => '<function name> function']; return response($response, 200);
<function name>
을 적절하게 채울 것입니다. 예를 들어, update
함수의 본문은 다음과 같습니다.
$response = ['message' => 'update function']; return response($response, 200);
수동 Laravel 인증 테스트: 사용자 생성
사용자를 등록하기 위해 name
, email
(고유해야 함), password
및 password_confirmation
매개변수를 사용하여 /api/register
에 POST
요청을 보냅니다.
사용자가 생성되면 API는 토큰을 반환하고 추가 요청에서 인증 수단으로 사용할 것입니다.
로그인하기 위해 POST
요청을 /api/login
으로 보냅니다. 자격 증명이 정확하면 이러한 방식으로 Laravel 로그인 API에서 토큰을 얻을 수도 있습니다.
이 요청에서 반환된 인증 토큰은 보호된 경로에 액세스하려고 할 때 사용할 수 있습니다. Postman에서 "Authorization" 탭에는 유형을 "Bearer Token"으로 설정할 수 있는 드롭다운이 있으며 그 후에 토큰은 토큰 필드에 들어갈 수 있습니다.
이 과정은 불면증에서도 매우 유사합니다.
cURL 사용자는 -H "Authorization: Bearer <token>"
매개변수를 전달하여 동등한 작업을 수행할 수 있습니다. 여기서 <token>
은 로그인 또는 등록 응답에서 제공되는 인증 토큰입니다.
cURL과 마찬가지로 개발자가 axios 또는 그러한 종류의 라이브러리를 사용하여 API를 사용하려는 경우 Bearer <token>
값이 있는 Authorization
헤더를 추가할 수 있습니다.
Laravel Passport 튜토리얼, 4단계: 비밀번호 재설정 기능 생성
기본 인증이 완료되었으니 비밀번호 재설정 기능을 설정할 차례입니다.
이를 위해 api_auth
컨트롤러 디렉토리를 생성하고, 새로운 사용자 정의 컨트롤러를 생성하고, 기능을 구현하도록 선택할 수 있습니다. 또는 Laravel로 생성할 수 있는 인증 컨트롤러를 편집할 수 있습니다. 이 경우 전체 애플리케이션이 API이므로 인증 컨트롤러를 편집합니다.

먼저 다음을 실행하여 인증 컨트롤러를 생성합니다.
composer require laravel/ui php artisan ui vue --auth
app/Http/Controllers/Auth/ForgotPasswordController.php
에서 클래스를 편집하고 다음 두 가지 방법을 추가합니다.
protected function sendResetLinkResponse(Request $request, $response) { $response = ['message' => "Password reset email sent"]; return response($response, 200); } protected function sendResetLinkFailedResponse(Request $request, $response) { $response = "Email could not be sent to this email address"; return response($response, 500); }
다음으로, 실제로 비밀번호를 재설정하는 컨트롤러를 설정해야 하므로 app/Http/Controllers/Auth/ResetPasswordController.php
로 이동하여 다음과 같은 기본 기능을 재정의합니다.
protected function resetPassword($user, $password) { $user->password = Hash::make($password); $user->save(); event(new PasswordReset($user)); } protected function sendResetResponse(Request $request, $response) { $response = ['message' => "Password reset successful"]; return response($response, 200); } protected function sendResetFailedResponse(Request $request, $response) { $response = "Token Invalid"; return response($response, 401); }
또한 다음을 추가하여 컨트롤러에서 일부 클래스를 가져와야 합니다.
use Illuminate\Auth\Events\PasswordReset; use Illuminate\Http\Request; use Illuminate\Support\Facades\Hash;
...컨트롤러 상단으로.
Laravel과 함께 제공되는 메일 알림은 승인을 위해 API 토큰을 사용하지 않기 때문에 사용되는 이메일 알림도 수정하고 싶습니다. 다음 명령을 실행하여 app/Notifications
아래에 새 항목을 만들 수 있습니다.
php artisan make:notification MailResetPasswordNotification
app/Notifications/MailResetPasswordNotification.php
파일을 다음과 같이 편집해야 합니다.
<?php namespace App\Notifications; use Illuminate\Bus\Queueable; use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Auth\Notifications\ResetPassword; use Illuminate\Support\Facades\Lang; class MailResetPasswordNotification extends ResetPassword { use Queueable; protected $pageUrl; public $token; /** * Create a new notification instance. * * @param $token */ public function __construct($token) { parent::__construct($token); $this->pageUrl = 'localhost:8080'; // we can set whatever we want here, or use .env to set environmental variables } /** * Get the notification's delivery channels. * * @param mixed $notifiable * @return array */ public function via($notifiable) { return ['mail']; } /** * Get the mail representation of the notification. * * @param mixed $notifiable * @return \Illuminate\Notifications\Messages\MailMessage */ public function toMail($notifiable) { if (static::$toMailCallback) { return call_user_func(static::$toMailCallback, $notifiable, $this->token); } return (new MailMessage) ->subject(Lang::getFromJson('Reset application Password')) ->line(Lang::getFromJson('You are receiving this email because we received a password reset request for your account.')) ->action(Lang::getFromJson('Reset Password'), $this->pageUrl."?token=".$this->token) ->line(Lang::getFromJson('This password reset link will expire in :count minutes.', ['count' => config('auth.passwords.users.expire')])) ->line(Lang::getFromJson('If you did not request a password reset, no further action is required.')); } /** * Get the array representation of the notification. * * @param mixed $notifiable * @return array */ public function toArray($notifiable) { return [ // ]; } }
이 새로운 알림을 사용하려면 User
가 Authenticatable
클래스에서 상속한 sendPasswordResetNotification
메서드를 재정의해야 합니다. 이것을 app/User.php
에 추가하기만 하면 됩니다.
public function sendPasswordResetNotification($token) { $this->notify(new \App\Notifications\MailResetPasswordNotification($token)); }
메일 설정이 제대로 작동하면 이 시점에서 알림이 작동해야 합니다.
이제 사용자 액세스 제어만 남았습니다.
Laravel Passport 튜토리얼, 5단계: 액세스 제어 미들웨어 생성
액세스 제어 미들웨어를 만들기 전에 user
테이블을 업데이트하여 사용자 수준을 결정하는 데 사용할 type
이라는 열을 가져야 합니다. 유형 0은 일반 사용자, 유형 1은 관리자, 유형 2는 a입니다. 최고 관리자.
user
테이블을 업데이트하려면 다음을 실행하여 마이그레이션을 생성해야 합니다.
php artisan make:migration update_users_table_to_include_type --table=users
database/migrations/[timestamp]_update_users_table.php
형식의 새로 생성된 파일에서 type
열을 각각 추가 및 제거하기 위해 up
및 down
함수를 업데이트해야 합니다.
public function up() { Schema::table('users', function (Blueprint $table) { $table->integer('type'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('users', function (Blueprint $table) { $table->dropIfExists('type'); }); }
다음으로 php artisan migrate
를 실행합니다. 이 작업이 완료되면 ApiAuthController.php
파일에서 register
기능을 편집하여 $user = User::create($request->toArray());
:
$request['type'] = $request['type'] ? $request['type'] : 0;
또한 다음 행을 $validator
배열에 추가해야 합니다.
'type' => 'integer',
이 두 편집 중 첫 번째는 등록된 모든 사용자를 기본적으로 "일반 사용자"로 만듭니다. 즉, 사용자 유형이 입력되지 않은 경우입니다.
액세스 제어 미들웨어 자체
이제 우리는 액세스 제어에 사용할 두 가지 미들웨어를 만들 수 있습니다. 하나는 관리자용이고 다른 하나는 최고 관리자용입니다.
그래서 우리는 다음을 실행할 것입니다:
php artisan make:middleware AdminAuth php artisan make:middleware SuperAdminAuth
먼저 app/Http/Middleware/AdminAuth.php
로 이동하여 Illuminate\Support\Facades\Auth
를 가져온 다음 handle
기능을 다음과 같이 편집합니다.
public function handle($request, Closure $next) { if (Auth::guard('api')->check() && $request->user()->type >= 1) { return $next($request); } else { $message = ["message" => "Permission Denied"]; return response($message, 401); } }
또한 app/Http/Middleware/SuperAdminAuth.php
에서 handle
기능을 편집해야 합니다.
public function handle($request, Closure $next) { if (Auth::guard('api')->check() && $request->user()->type >= 2) { return $next($request); } else { $message = ["message" => "Permission Denied"]; return response($message, 401); } }
또한 다음을 추가하여 두 파일의 맨 위에 있는 Auth
클래스를 가져와야 합니다.
use Illuminate\Support\Facades\Auth;
... 거기에서 찾은 수입품의 맨 아래.
새로운 미들웨어를 사용하기 위해 $routeMiddleware
배열에 다음 줄을 추가하여 커널(예: app/Http/Kernel.php
)의 두 클래스를 모두 참조합니다.
'api.admin' => \App\Http\Middleware\AdminAuth::class, 'api.superAdmin' => \App\Http\Middleware\SuperAdminAuth::class,
개발자가 주어진 경로에서 미들웨어를 사용하려면 다음과 같이 경로 기능에 추가하기만 하면 됩니다.
Route::post('route','Controller@method')->middleware('<middleware-name-here>');
이 경우 <middleware-name-here>
는 적절하게 api.admin
, api.superAdmin
등이 될 수 있습니다.
이것이 미들웨어를 만드는 데 필요한 전부입니다.
함께 모아서
인증 및 액세스 제어가 작동하는지 테스트하기 위해 거쳐야 할 몇 가지 추가 단계가 있습니다.
Laravel 인증 및 액세스 제어 테스트: 1단계
ArticleController
의 index
기능을 수정하고 경로를 등록해야 합니다. (실제 프로젝트에서는 PHPUnit을 사용하고 자동화된 테스트의 일부로 이를 수행합니다. 여기에서는 테스트 목적으로 경로를 수동으로 추가합니다. 나중에 제거할 수 있습니다.)
app/Http/Controllers/ArticleController
에 있는 ArticleController
컨트롤러로 이동하고 index
기능을 다음과 같이 수정합니다.
public function index() { $response = ['message' => 'article index']; return response($response, 200); }
다음으로, routes/api.php
파일로 이동하여 다음을 추가하여 경로에 함수를 등록합니다.
Route::middleware('auth:api')->group(function () { Route::get('/articles', 'ArticleController@index')->name('articles'); });
Laravel 인증 및 액세스 제어 테스트: 2단계
이제 인증 토큰 없이 경로에 액세스할 수 있습니다. 인증 오류가 발생해야 합니다.
Laravel 인증 및 액세스 제어 테스트: 3단계
인증 토큰(이 기사의 앞부분에서 등록하거나 로그인하여 얻은 토큰)을 사용하여 동일한 경로에 액세스할 수도 있습니다.
경우에 따라 다음과 유사한 오류가 발생할 수 있습니다.
Unknown column 'api_token' in 'where clause' (SQL: select * from `users` where `api_token` = ...
이 경우 개발자는 Passport 마이그레이션을 실행하고 config/auth.php
에서 ['guards']['api']['driver']
를 passport
으로 설정해야 합니다.
'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], 'api' => [ 'driver' => 'passport', 'provider' => 'users', ], ],
그런 다음 구성 캐시도 업데이트해야 합니다.
이 문제가 해결되면 경로에 액세스할 수 있어야 합니다.
Laravel 인증 및 액세스 제어 테스트: 4단계
액세스 제어를 테스트할 시간입니다. 기사 경로에 ->middleware('api.admin')
를 추가하여 다음과 같이 보이도록 합시다.
Route::get('/articles', 'ArticleController@index')->middleware('api.admin')->name('articles');
api/user
경로를 통해 볼 수 있듯이 새로 생성된 사용자에게 자동으로 유형 0이 할당되도록 만들었습니다.
그 때문에 이러한 사용자로 articles
엔드포인트에 액세스하려고 하면 오류가 발생해야 합니다.
테스트를 위해 데이터베이스의 사용자를 type
1로 수정해 보겠습니다. api/user
경로를 통해 변경 사항을 다시 확인한 후 GET
/articles/
경로를 다시 시도할 준비가 되었습니다.
그것은 완벽하게 작동합니다.
더 복잡한 애플리케이션을 만드는 개발자는 적절한 액세스 제어가 이렇게 간단하지 않다는 점에 유의해야 합니다. 이 경우 다른 타사 애플리케이션이나 Laravel의 게이트 및 정책을 사용하여 사용자 지정 사용자 액세스 제어를 구현할 수 있습니다. 이 시리즈의 두 번째 파트에서는 보다 강력하고 유연한 액세스 제어 솔루션을 살펴보겠습니다.
Laravel API 인증: 우리가 배운 것
이 Laravel Passport 튜토리얼에서 우리는 다음을 논의했습니다.
- Laravel Passport 예제를 테스트하는 동안 사용할 더미 컨트롤러와 모델을 만듭니다.
- API를 원활하게 실행하는 데 필요한 미들웨어를 만들고 CORS를 처리하고 API가 항상 JSON 응답을 반환하도록 합니다.
- 기본 Laravel API 인증 설정: 등록, 로그인 및 로그아웃.
- Laravel의 기본값을 기반으로 "비밀번호 재설정" 기능을 설정합니다.
- 다른 경로에 사용자 인증 권한 수준을 추가하는 액세스 제어 미들웨어 생성.
이는 라라벨 개발 서비스 분야에서 일하는 모든 사람에게 필수적인 기술입니다. 독자는 이 GitHub 리포지토리에서 최종 결과를 찾을 수 있으며 이제 Laravel을 사용하여 인증을 구현하기에 좋은 위치에 있어야 합니다. 아래의 의견을 기다리겠습니다.