[PHP & Laravel] JWT Token 방식 구현

320x100

개요

웹 사이트에서 인증을 다룰 때는 대표적으로 세션 방식과 JWT Token 방식이 존재합니다.

 

둘 다 매커니즘은 쿠키로 세션ID 또는 암호화된 JWT Token를 발급받아 프론트와 백엔드간 쿠키를 주고받고 인증을 허가하는 방식이나, 세션 방식의 문제점 중 하나는 URL을 이동할 경우 세션이 만료되어 로그인을 다시 해야 한다는 단점이 존재하며, JWT Token에서는 서버에 세션 정보를 저장하지 않아 서버의 부하를 줄일 수 있고, 유효 기간과 사용 범위 등을 정해 보안적인 측면에서 확장된 제어가 가능하며, 필요에 따라 토큰에 정보와 권한을 포함시켜 인증과 인가를 분리해 관리가 가능합니다.

 

오늘은 그 JWT Token 방식을 PHP & Laravel에서 구현하는 방법을 알아보고자 하며, 직접 처음부터 끝까지 구현할 수도 있겠지만, 라라벨에서는 이미 JWT Token 방식을 손쉽게 사용할 수 있는 패키지를 제공하고 있습니다.

(백엔드에서 사용할 로그인 API 기준으로 작성했습니다. 프론트엔드측 코드는 별도로 넣지 않았습니다.)

제한 사항 (반드시 확인)

Laravel Version : 5.2 버전 이상

PHP : 5.5.9 버전 이상

JWT-Auth 패키지를 사용하는 장점

  • 일반적으로 직접 구현하는 것보다 성능이 뛰어나다.
  • 토큰을 라라벨 프로젝트 내부에서 발급, 서명, 보관, 유효성 검증, 만료 처리, 캐싱 작업을 진행해 관리할 내용이 적다.
  • 사용 또한 손쉽게 가능하다.

구현 방법

1. 패키지 설치 (프로젝트 루트 폴더에서 진행)이후 “{프로젝트경로}/config/jwt.php” 파일 생성 확인

composer require tymon/jwt-auth

2. JWT-Auth 구성 파일 생성 (프로젝트 루트 폴더에서 진행)

php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"

3. 서명에 사용할 시크릿 키 생성 (프로젝트 루트 폴더에서 진행))

php artisan jwt:secret

이후 .env 파일 내부에 JWT_SECRET 변수에 값이 추가되었을 것임(이 시크릿 키는 config/jwt.php에 'secret' => env('JWT_SECRET'),로 지정되어 있음).

 

4. config/auth.php에서 driver 지정

'guards' => [
    ...
    'api' => [
        'driver' => 'jwt',
        'provider' => 'users'
    ]
],

...

'providers' => [
    'users' => [
        'driver' => 'eloquent',
        'model' => App\User::class,
    ],
],

5. User 모델에서 JWTSubject를 상속 (User Model은 현재 사용할 프로젝트에서 사용자들의 정보를 저장할 모델을 미리 생성해놓아야 함)

...
use Tymon\JWTAuth\Contracts\JWTSubject;

class User extends Authenticatable implements JWTSubject
{
    ...

	public function signup(Request $request): JsonResponse
    {
        $user = AdminUser::create([
            'email' => $request->input('email'),
            'password' => bcrypt($request->input('password')),
        ]);

        return response()->json($user);
    }

	public function login(Request $request)
	{
	    $credentails = $request->only('email', 'password');
   
	    // attempt의 응답값으로 액세스 토큰이 반환됨.
	    $token = auth('api')->attempt($credentails);
    
	    // 응답값에 토큰 정보 추가
	    return response()->json([
	        'result' => true,
	        'token' => $token
		    ]);
		}

	public function logout(): JsonResponse
    {
        auth('api')->logout();

        return response()->json([
            'code' => 200,
            'message' => '로그아웃 되었습니다.'
        ]);
    }
}

6. AuthController에서 로그인, 회원가입, 로그아웃 기능 구현

<?php

namespace App\Http\Controllers;

use App\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;

class JWTAuthController extends Controller
{
    public function __construct()
    {
    }

    /**
     * 회원가입
     *
     * @param Request $request
     * @return JsonResponse
     */
    public function signup(Request $request): JsonResponse
    {
        $user = AdminUser::create([
            'email' => $request->input('email'),
            'password' => bcrypt($request->input('password')),
        ]);

        return response()->json($user);
    }

    /**
     * JWT 로그인
     *
     * @param Request $request
     * @return JsonResponse
     */
    public function login(Request $request): JsonResponse
    {
        $credentials = $request->only('email', 'password');

        if (!$token = auth('api')->attempt($credentials)) {
            return response()->json([
                'result' => false,
                'code' => 401,
                'message' => '이메일 또는 비밀번호가 올바르지 않습니다.'
            ]);
        }

        $user = auth('api')->user();
        return response()->json([
            'result' => true,
            'user' => $user,
            'token' => $token
        ]);
    }

    /**
     * 로그아웃
     *
     * @return JsonResponse
     */
    public function logout(): JsonResponse
    {
        auth('api')->logout();

        return response()->json([
            'code' => 200,
            'message' => '로그아웃 되었습니다.'
        ]);
    }
}

7. 로그인한 사용자만 조회 가능한 정보 얻는 컨트롤러 생성

<?php

declare(strict_types=1);

namespace App\Http\Controllers;

class ApiController extends Controller
{
    public function __construct(
    {
    }

    public function getValue(): string
    {
        return '로그인을 해야 해당 정보를 볼 수 있을걸요?';
    }
}

8. API 정의 (config/api.php)

Route::post('/register', [App\Http\Controllers\JWTAuthController::class, 'signup']);
Route::post('/login', [App\Http\Controllers\JWTAuthController::class, 'login']);

Route::group(['middleware' => ['api']], function () {
	Route::get('/need-login-values', [App\Http\Controllers\ApiController::class, 'getValue']);
}

9. API를 사용해 정상 작동 확인

저는 API 사용 시 postman이라는 프로그램을 사용했습니다.

- 로그인 API 호출 결과

- /need-login-values api에 bearer-token 지정 (위에서 발급받은 token의 내용이 들어가야 함)

- /need-login-values 결과 확인

- 로그인하지 않고 /need-login-values 호출 시 

320x100

'PHP' 카테고리의 다른 글

PHP 표준 권고(PSR)  (0) 2023.07.04
PHP 기본 문법 요약  (0) 2023.06.30
PHP 전체 개발 환경 정리  (0) 2023.03.28
Java 프로그래머가 PHP를 시작하는 방법  (0) 2023.03.01