615 lines
14 KiB
Markdown
615 lines
14 KiB
Markdown
|
|
# 세션 기반 인증 전환 가이드 - 백엔드 (PHP/Laravel)
|
||
|
|
|
||
|
|
## 📋 개요
|
||
|
|
|
||
|
|
**목적**: JWT 토큰 기반 → 세션 기반 인증으로 전환하여 보안 강화
|
||
|
|
|
||
|
|
**주요 보안 개선 사항**:
|
||
|
|
- ✅ 로그아웃 시 즉시 세션 무효화 (토큰 만료 대기 불필요)
|
||
|
|
- ✅ 세션 하이재킹 실시간 감지 (IP/User-Agent 추적)
|
||
|
|
- ✅ 관리자의 강제 로그아웃 기능
|
||
|
|
- ✅ 1계정 1세션 강제 (동시 로그인 제한)
|
||
|
|
- ✅ 의심스러운 활동 자동 차단
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🔧 1단계: 환경 설정
|
||
|
|
|
||
|
|
### 1.1 세션 드라이버 설정
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# .env
|
||
|
|
SESSION_DRIVER=redis
|
||
|
|
SESSION_LIFETIME=120 # 2시간 (분 단위)
|
||
|
|
SESSION_SECURE_COOKIE=true
|
||
|
|
SESSION_DOMAIN=.yourdomain.com # 서브도메인 공유 시
|
||
|
|
REDIS_HOST=127.0.0.1
|
||
|
|
REDIS_PASSWORD=null
|
||
|
|
REDIS_PORT=6379
|
||
|
|
```
|
||
|
|
|
||
|
|
### 1.2 세션 설정 파일
|
||
|
|
|
||
|
|
```php
|
||
|
|
// config/session.php
|
||
|
|
return [
|
||
|
|
'driver' => env('SESSION_DRIVER', 'redis'),
|
||
|
|
'lifetime' => env('SESSION_LIFETIME', 120),
|
||
|
|
'expire_on_close' => false,
|
||
|
|
'encrypt' => true, // 🔒 세션 데이터 암호화
|
||
|
|
'http_only' => true, // 🔒 XSS 방지
|
||
|
|
'same_site' => 'strict', // 🔒 CSRF 방지
|
||
|
|
'secure' => env('SESSION_SECURE_COOKIE', true), // 🔒 HTTPS only
|
||
|
|
|
||
|
|
// 세션 가비지 컬렉션
|
||
|
|
'lottery' => [2, 100],
|
||
|
|
|
||
|
|
// 세션 쿠키 이름
|
||
|
|
'cookie' => env(
|
||
|
|
'SESSION_COOKIE',
|
||
|
|
Str::slug(env('APP_NAME', 'laravel'), '_').'_session'
|
||
|
|
),
|
||
|
|
];
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🔐 2단계: 인증 가드 변경
|
||
|
|
|
||
|
|
### 2.1 Auth 설정
|
||
|
|
|
||
|
|
```php
|
||
|
|
// config/auth.php
|
||
|
|
'guards' => [
|
||
|
|
'web' => [
|
||
|
|
'driver' => 'session',
|
||
|
|
'provider' => 'users',
|
||
|
|
],
|
||
|
|
|
||
|
|
'api' => [
|
||
|
|
'driver' => 'session', // Sanctum → Session 변경
|
||
|
|
'provider' => 'users',
|
||
|
|
],
|
||
|
|
],
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🚪 3단계: 로그인 컨트롤러 수정
|
||
|
|
|
||
|
|
### 3.1 기존 코드 (토큰 기반)
|
||
|
|
|
||
|
|
```php
|
||
|
|
// ❌ 제거할 코드
|
||
|
|
public function login(Request $request)
|
||
|
|
{
|
||
|
|
// JWT 토큰 발급
|
||
|
|
$token = auth()->attempt($credentials);
|
||
|
|
|
||
|
|
return response()->json([
|
||
|
|
'access_token' => $token,
|
||
|
|
'refresh_token' => $refreshToken,
|
||
|
|
'token_type' => 'bearer',
|
||
|
|
'expires_in' => 7200,
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3.2 새로운 코드 (세션 기반)
|
||
|
|
|
||
|
|
```php
|
||
|
|
// ✅ 새로운 로그인 로직
|
||
|
|
namespace App\Http\Controllers\Auth;
|
||
|
|
|
||
|
|
use Illuminate\Http\Request;
|
||
|
|
use Illuminate\Support\Facades\Auth;
|
||
|
|
use Illuminate\Support\Facades\DB;
|
||
|
|
|
||
|
|
class LoginController extends Controller
|
||
|
|
{
|
||
|
|
public function login(Request $request)
|
||
|
|
{
|
||
|
|
// 입력 검증
|
||
|
|
$credentials = $request->validate([
|
||
|
|
'user_id' => 'required|string',
|
||
|
|
'user_pwd' => 'required|string',
|
||
|
|
]);
|
||
|
|
|
||
|
|
// 🔒 세션 기반 인증
|
||
|
|
if (Auth::attempt([
|
||
|
|
'user_id' => $credentials['user_id'],
|
||
|
|
'password' => $credentials['user_pwd']
|
||
|
|
], $request->filled('remember'))) {
|
||
|
|
|
||
|
|
// 🔒 세션 재생성 (세션 고정 공격 방지)
|
||
|
|
$request->session()->regenerate();
|
||
|
|
|
||
|
|
// 🔒 보안 정보 저장 (하이재킹 감지용)
|
||
|
|
session([
|
||
|
|
'ip_address' => $request->ip(),
|
||
|
|
'user_agent' => $request->userAgent(),
|
||
|
|
'login_at' => now()->toDateTimeString(),
|
||
|
|
]);
|
||
|
|
|
||
|
|
// 🔒 동시 로그인 제한 (옵션)
|
||
|
|
$this->limitConcurrentSessions(Auth::user());
|
||
|
|
|
||
|
|
// 사용자 정보 반환 (토큰 없음!)
|
||
|
|
return response()->json([
|
||
|
|
'message' => 'Login successful',
|
||
|
|
'user' => [
|
||
|
|
'id' => Auth::user()->id,
|
||
|
|
'user_id' => Auth::user()->user_id,
|
||
|
|
'name' => Auth::user()->name,
|
||
|
|
'email' => Auth::user()->email,
|
||
|
|
'phone' => Auth::user()->phone,
|
||
|
|
],
|
||
|
|
'tenant' => Auth::user()->tenant,
|
||
|
|
'menus' => Auth::user()->menus,
|
||
|
|
'roles' => Auth::user()->roles,
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 인증 실패
|
||
|
|
return response()->json([
|
||
|
|
'error' => 'Invalid credentials'
|
||
|
|
], 401);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 🔒 동시 로그인 제한 (1계정 1세션)
|
||
|
|
*/
|
||
|
|
protected function limitConcurrentSessions($user)
|
||
|
|
{
|
||
|
|
// 현재 세션 ID 제외하고 모든 세션 삭제
|
||
|
|
DB::table('sessions')
|
||
|
|
->where('user_id', $user->id)
|
||
|
|
->where('id', '!=', session()->getId())
|
||
|
|
->delete();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🚪 4단계: 로그아웃 컨트롤러 수정
|
||
|
|
|
||
|
|
```php
|
||
|
|
// app/Http/Controllers/Auth/LogoutController.php
|
||
|
|
namespace App\Http\Controllers\Auth;
|
||
|
|
|
||
|
|
use Illuminate\Http\Request;
|
||
|
|
use Illuminate\Support\Facades\Auth;
|
||
|
|
|
||
|
|
class LogoutController extends Controller
|
||
|
|
{
|
||
|
|
public function logout(Request $request)
|
||
|
|
{
|
||
|
|
// 🔒 세션 무효화
|
||
|
|
Auth::logout();
|
||
|
|
|
||
|
|
// 🔒 세션 데이터 삭제
|
||
|
|
$request->session()->invalidate();
|
||
|
|
|
||
|
|
// 🔒 CSRF 토큰 재생성
|
||
|
|
$request->session()->regenerateToken();
|
||
|
|
|
||
|
|
return response()->json([
|
||
|
|
'message' => 'Logged out successfully'
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🛡️ 5단계: 세션 하이재킹 감지 미들웨어
|
||
|
|
|
||
|
|
### 5.1 미들웨어 생성
|
||
|
|
|
||
|
|
```bash
|
||
|
|
php artisan make:middleware DetectSessionHijacking
|
||
|
|
```
|
||
|
|
|
||
|
|
### 5.2 미들웨어 코드
|
||
|
|
|
||
|
|
```php
|
||
|
|
// app/Http/Middleware/DetectSessionHijacking.php
|
||
|
|
namespace App\Http\Middleware;
|
||
|
|
|
||
|
|
use Closure;
|
||
|
|
use Illuminate\Http\Request;
|
||
|
|
use Illuminate\Support\Facades\Auth;
|
||
|
|
use Illuminate\Support\Facades\Log;
|
||
|
|
|
||
|
|
class DetectSessionHijacking
|
||
|
|
{
|
||
|
|
/**
|
||
|
|
* 세션 하이재킹 감지 및 차단
|
||
|
|
*/
|
||
|
|
public function handle(Request $request, Closure $next)
|
||
|
|
{
|
||
|
|
if (Auth::check()) {
|
||
|
|
$user = Auth::user();
|
||
|
|
|
||
|
|
// 🔒 IP 주소 변경 감지
|
||
|
|
if (session('ip_address') && session('ip_address') !== $request->ip()) {
|
||
|
|
Log::warning('Session hijacking detected: IP changed', [
|
||
|
|
'user_id' => $user->id,
|
||
|
|
'old_ip' => session('ip_address'),
|
||
|
|
'new_ip' => $request->ip(),
|
||
|
|
]);
|
||
|
|
|
||
|
|
// 세션 파괴 및 로그아웃
|
||
|
|
Auth::logout();
|
||
|
|
$request->session()->invalidate();
|
||
|
|
$request->session()->regenerateToken();
|
||
|
|
|
||
|
|
return response()->json([
|
||
|
|
'error' => 'Session security violation detected',
|
||
|
|
'code' => 'SESSION_HIJACKED',
|
||
|
|
'message' => 'Your session has been terminated for security reasons.'
|
||
|
|
], 401);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 🔒 User-Agent 변경 감지
|
||
|
|
if (session('user_agent') && session('user_agent') !== $request->userAgent()) {
|
||
|
|
Log::warning('Session hijacking detected: User-Agent changed', [
|
||
|
|
'user_id' => $user->id,
|
||
|
|
'old_ua' => session('user_agent'),
|
||
|
|
'new_ua' => $request->userAgent(),
|
||
|
|
]);
|
||
|
|
|
||
|
|
Auth::logout();
|
||
|
|
$request->session()->invalidate();
|
||
|
|
$request->session()->regenerateToken();
|
||
|
|
|
||
|
|
return response()->json([
|
||
|
|
'error' => 'Session security violation detected',
|
||
|
|
'code' => 'SESSION_HIJACKED'
|
||
|
|
], 401);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return $next($request);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 5.3 미들웨어 등록
|
||
|
|
|
||
|
|
```php
|
||
|
|
// app/Http/Kernel.php
|
||
|
|
protected $middlewareGroups = [
|
||
|
|
'api' => [
|
||
|
|
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
|
||
|
|
'throttle:api',
|
||
|
|
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||
|
|
\App\Http\Middleware\DetectSessionHijacking::class, // ✅ 추가
|
||
|
|
],
|
||
|
|
];
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🌐 6단계: CORS 설정 (중요!)
|
||
|
|
|
||
|
|
### 6.1 CORS 설정 파일
|
||
|
|
|
||
|
|
```php
|
||
|
|
// config/cors.php
|
||
|
|
return [
|
||
|
|
'paths' => ['api/*', 'sanctum/csrf-cookie'],
|
||
|
|
|
||
|
|
'allowed_methods' => ['*'],
|
||
|
|
|
||
|
|
'allowed_origins' => [
|
||
|
|
'http://localhost:3000', // 개발 환경
|
||
|
|
'https://yourdomain.com', // 프로덕션
|
||
|
|
'https://app.yourdomain.com', // 프로덕션 앱
|
||
|
|
],
|
||
|
|
|
||
|
|
'allowed_origins_patterns' => [],
|
||
|
|
|
||
|
|
'allowed_headers' => ['*'],
|
||
|
|
|
||
|
|
'exposed_headers' => [],
|
||
|
|
|
||
|
|
'max_age' => 0,
|
||
|
|
|
||
|
|
'supports_credentials' => true, // ✅ 세션 쿠키 전송 허용 (필수!)
|
||
|
|
];
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🗑️ 7단계: 토큰 관련 코드 제거
|
||
|
|
|
||
|
|
### 7.1 삭제할 엔드포인트
|
||
|
|
|
||
|
|
```php
|
||
|
|
// routes/api.php
|
||
|
|
|
||
|
|
// ❌ 삭제: 토큰 갱신 엔드포인트 (세션은 자동 갱신)
|
||
|
|
// Route::post('/refresh', [TokenController::class, 'refresh']);
|
||
|
|
```
|
||
|
|
|
||
|
|
### 7.2 삭제할 컨트롤러
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# ❌ 삭제 또는 주석 처리
|
||
|
|
# app/Http/Controllers/Auth/TokenRefreshController.php
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## ✅ 8단계: 세션 확인 엔드포인트 추가
|
||
|
|
|
||
|
|
```php
|
||
|
|
// routes/api.php
|
||
|
|
Route::get('/auth/check', [AuthController::class, 'check']);
|
||
|
|
```
|
||
|
|
|
||
|
|
```php
|
||
|
|
// app/Http/Controllers/Auth/AuthController.php
|
||
|
|
public function check(Request $request)
|
||
|
|
{
|
||
|
|
if (Auth::check()) {
|
||
|
|
return response()->json([
|
||
|
|
'authenticated' => true,
|
||
|
|
'user' => [
|
||
|
|
'id' => Auth::user()->id,
|
||
|
|
'name' => Auth::user()->name,
|
||
|
|
'email' => Auth::user()->email,
|
||
|
|
]
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
|
||
|
|
return response()->json([
|
||
|
|
'authenticated' => false
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🧪 9단계: 테스트
|
||
|
|
|
||
|
|
### 9.1 로그인 테스트
|
||
|
|
|
||
|
|
```bash
|
||
|
|
curl -X POST http://localhost:8000/api/v1/login \
|
||
|
|
-H "Content-Type: application/json" \
|
||
|
|
-H "X-API-KEY: your-api-key" \
|
||
|
|
-d '{"user_id": "test", "user_pwd": "password"}' \
|
||
|
|
-c cookies.txt # 쿠키 저장
|
||
|
|
|
||
|
|
# 응답:
|
||
|
|
# {
|
||
|
|
# "message": "Login successful",
|
||
|
|
# "user": {...},
|
||
|
|
# "tenant": {...}
|
||
|
|
# }
|
||
|
|
#
|
||
|
|
# Set-Cookie: laravel_session=abc123...
|
||
|
|
```
|
||
|
|
|
||
|
|
### 9.2 세션 확인 테스트
|
||
|
|
|
||
|
|
```bash
|
||
|
|
curl -X GET http://localhost:8000/api/v1/auth/check \
|
||
|
|
-H "X-API-KEY: your-api-key" \
|
||
|
|
-b cookies.txt # 저장된 쿠키 사용
|
||
|
|
|
||
|
|
# 응답:
|
||
|
|
# {
|
||
|
|
# "authenticated": true,
|
||
|
|
# "user": {...}
|
||
|
|
# }
|
||
|
|
```
|
||
|
|
|
||
|
|
### 9.3 로그아웃 테스트
|
||
|
|
|
||
|
|
```bash
|
||
|
|
curl -X POST http://localhost:8000/api/v1/logout \
|
||
|
|
-H "X-API-KEY: your-api-key" \
|
||
|
|
-b cookies.txt
|
||
|
|
|
||
|
|
# 응답:
|
||
|
|
# {
|
||
|
|
# "message": "Logged out successfully"
|
||
|
|
# }
|
||
|
|
```
|
||
|
|
|
||
|
|
### 9.4 세션 하이재킹 감지 테스트
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# 1. 로그인 (IP: A)
|
||
|
|
curl -X POST http://localhost:8000/api/v1/login \
|
||
|
|
-H "X-API-KEY: your-api-key" \
|
||
|
|
-d '{"user_id": "test", "user_pwd": "password"}' \
|
||
|
|
-c cookies.txt
|
||
|
|
|
||
|
|
# 2. 다른 IP에서 같은 세션 ID 사용 시도 (IP: B)
|
||
|
|
# → 자동 차단되어야 함
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🔒 10단계: 추가 보안 강화 (옵션)
|
||
|
|
|
||
|
|
### 10.1 Rate Limiting (무차별 대입 공격 방지)
|
||
|
|
|
||
|
|
```php
|
||
|
|
// routes/api.php
|
||
|
|
Route::middleware(['throttle:5,1'])->group(function () {
|
||
|
|
Route::post('/login', [LoginController::class, 'login']);
|
||
|
|
});
|
||
|
|
|
||
|
|
// 5번 시도 후 1분 대기
|
||
|
|
```
|
||
|
|
|
||
|
|
### 10.2 세션 활동 로그
|
||
|
|
|
||
|
|
```php
|
||
|
|
// app/Models/SessionLog.php 생성
|
||
|
|
Schema::create('session_logs', function (Blueprint $table) {
|
||
|
|
$table->id();
|
||
|
|
$table->unsignedBigInteger('user_id');
|
||
|
|
$table->string('ip_address');
|
||
|
|
$table->text('user_agent');
|
||
|
|
$table->timestamp('login_at');
|
||
|
|
$table->timestamp('logout_at')->nullable();
|
||
|
|
$table->timestamps();
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
```php
|
||
|
|
// 로그인 시 기록
|
||
|
|
SessionLog::create([
|
||
|
|
'user_id' => Auth::id(),
|
||
|
|
'ip_address' => $request->ip(),
|
||
|
|
'user_agent' => $request->userAgent(),
|
||
|
|
'login_at' => now(),
|
||
|
|
]);
|
||
|
|
```
|
||
|
|
|
||
|
|
### 10.3 관리자 강제 로그아웃 기능
|
||
|
|
|
||
|
|
```php
|
||
|
|
// app/Http/Controllers/Admin/SessionController.php
|
||
|
|
public function forceLogout(Request $request, $userId)
|
||
|
|
{
|
||
|
|
// 특정 사용자의 모든 세션 삭제
|
||
|
|
DB::table('sessions')
|
||
|
|
->where('user_id', $userId)
|
||
|
|
->delete();
|
||
|
|
|
||
|
|
return response()->json([
|
||
|
|
'message' => 'User sessions terminated'
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 📊 마이그레이션 체크리스트
|
||
|
|
|
||
|
|
### 필수 작업
|
||
|
|
|
||
|
|
- [ ] `.env` 파일 세션 드라이버 설정
|
||
|
|
- [ ] `config/session.php` 보안 설정 적용
|
||
|
|
- [ ] `config/auth.php` 가드를 세션으로 변경
|
||
|
|
- [ ] 로그인 컨트롤러 수정 (토큰 제거, 세션 사용)
|
||
|
|
- [ ] 로그아웃 컨트롤러 수정 (세션 무효화)
|
||
|
|
- [ ] `config/cors.php`에서 `supports_credentials: true` 설정
|
||
|
|
- [ ] 세션 하이재킹 감지 미들웨어 추가
|
||
|
|
- [ ] `/api/v1/refresh` 엔드포인트 삭제
|
||
|
|
- [ ] `/api/v1/auth/check` 엔드포인트 추가
|
||
|
|
|
||
|
|
### 권장 작업
|
||
|
|
|
||
|
|
- [ ] Rate Limiting 적용
|
||
|
|
- [ ] 세션 활동 로그 테이블 생성
|
||
|
|
- [ ] 관리자 강제 로그아웃 기능 구현
|
||
|
|
- [ ] 동시 로그인 제한 적용
|
||
|
|
|
||
|
|
### 테스트
|
||
|
|
|
||
|
|
- [ ] 로그인 → 세션 생성 확인
|
||
|
|
- [ ] 로그아웃 → 세션 파괴 확인
|
||
|
|
- [ ] 세션 하이재킹 감지 테스트
|
||
|
|
- [ ] CORS 크로스 도메인 테스트
|
||
|
|
- [ ] 동시 로그인 제한 테스트
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🚨 주의사항
|
||
|
|
|
||
|
|
### 1. 세션 저장소 (Redis) 필수
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Redis 설치 확인
|
||
|
|
redis-cli ping
|
||
|
|
# 응답: PONG
|
||
|
|
|
||
|
|
# Redis 접속 테스트
|
||
|
|
redis-cli
|
||
|
|
> KEYS *session*
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. CORS 설정 필수
|
||
|
|
|
||
|
|
- `supports_credentials: true` 반드시 설정
|
||
|
|
- 프론트엔드 도메인을 `allowed_origins`에 추가
|
||
|
|
- `*` (와일드카드) 사용 불가 (credentials와 충돌)
|
||
|
|
|
||
|
|
### 3. HTTPS 필수 (프로덕션)
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# .env
|
||
|
|
SESSION_SECURE_COOKIE=true # HTTPS만 쿠키 전송
|
||
|
|
```
|
||
|
|
|
||
|
|
### 4. 세션 쿠키 이름 확인
|
||
|
|
|
||
|
|
```php
|
||
|
|
// config/session.php
|
||
|
|
'cookie' => 'laravel_session', // 프론트엔드에서 이 이름 사용
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 📞 프론트엔드 팀 공유 사항
|
||
|
|
|
||
|
|
### API 변경 사항
|
||
|
|
|
||
|
|
**로그인 응답 변경**:
|
||
|
|
```json
|
||
|
|
// ❌ 이전 (토큰 반환)
|
||
|
|
{
|
||
|
|
"access_token": "eyJhbG...",
|
||
|
|
"refresh_token": "eyJhbG...",
|
||
|
|
"token_type": "bearer",
|
||
|
|
"expires_in": 7200
|
||
|
|
}
|
||
|
|
|
||
|
|
// ✅ 이후 (토큰 없음, 세션 쿠키만)
|
||
|
|
{
|
||
|
|
"message": "Login successful",
|
||
|
|
"user": {...},
|
||
|
|
"tenant": {...}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Set-Cookie: laravel_session=abc123...
|
||
|
|
```
|
||
|
|
|
||
|
|
**필수 요구사항**:
|
||
|
|
- 모든 API 호출에 `credentials: 'include'` 추가
|
||
|
|
- 세션 쿠키를 자동으로 포함하여 전송
|
||
|
|
- `/api/auth/refresh` 엔드포인트 사용 중단
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🎯 완료 후 확인사항
|
||
|
|
|
||
|
|
1. ✅ 로그인 시 세션 쿠키 생성
|
||
|
|
2. ✅ 로그아웃 시 즉시 접근 차단
|
||
|
|
3. ✅ IP 변경 시 자동 차단
|
||
|
|
4. ✅ User-Agent 변경 시 자동 차단
|
||
|
|
5. ✅ 관리자 강제 로그아웃 작동
|
||
|
|
6. ✅ Redis에 세션 데이터 저장 확인
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 📚 참고 자료
|
||
|
|
|
||
|
|
- [Laravel Session 공식 문서](https://laravel.com/docs/session)
|
||
|
|
- [Laravel Authentication 공식 문서](https://laravel.com/docs/authentication)
|
||
|
|
- [Redis Session Driver](https://laravel.com/docs/redis)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
**작성일**: 2025-11-12
|
||
|
|
**작성자**: Claude Code
|
||
|
|
**버전**: 1.0
|