Roulette.php 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. <?php
  2. namespace App\Controllers;
  3. use CodeIgniter\RESTful\ResourceController;
  4. use App\Libraries\JwtLib\JWT;
  5. use App\Libraries\JwtLib\Key;
  6. use App\Models\LoginModel;
  7. class Roulette extends ResourceController
  8. {
  9. public function login()
  10. {
  11. // JSON 데이터 받기
  12. $data = $this->request->getJSON(true);
  13. $id = $data['id'] ?? null;
  14. $password = $data['password'] ?? null;
  15. $logintype = $data['logintype'] ?? null;
  16. if (!$id || !$password) {
  17. return $this->fail([
  18. 'errorCode' => 1000,
  19. 'message' => '아이디 또는 비밀번호가 누락되었습니다.'
  20. ], 400);
  21. }
  22. $loginModel = new LoginModel();
  23. $builder = $loginModel->getBuilderFor($logintype); // 모델을 통해 빌더를 가져옴
  24. $user = $builder->where('ID', $id)->get()->getRowArray();
  25. if (!$user) {
  26. return $this->fail([
  27. 'errorCode' => 1001,
  28. 'message' => '존재하지 않는 아이디입니다.'
  29. ], 404);
  30. }
  31. // 비밀번호 검증
  32. if (!password_verify($password, $user['PASSWORD'])) {
  33. return $this->fail([
  34. 'errorCode' => 1002,
  35. 'message' => '비밀번호가 틀렸습니다.'
  36. ], 401);
  37. }
  38. unset($user['PASSWORD']); // 비밀번호 노출 방지
  39. // JWT 토큰 생성에 필요한 값 로드
  40. $jwtSecret = env('JWT_SECRET');
  41. $kid = env('JWT_KID');
  42. if (empty($jwtSecret) || empty($kid)) {
  43. return $this->failServerError('환경변수가 누락되었습니다. 관리자에게 문의하세요.');
  44. }
  45. if (!class_exists('\App\Libraries\JwtLib\JWT')) {
  46. return $this->failServerError('JWT 라이브러리를 찾을 수 없습니다.');
  47. }
  48. $issuedAt = time();
  49. $accessExpire = $issuedAt + 60 * 15; // 15분
  50. //$accessExpire = $issuedAt + 5; // 15분
  51. $refreshExpire = $issuedAt + 60 * 60 * 24 * 14; // 14일
  52. //$refreshExpire = $issuedAt + 5; // 14일
  53. $accessPayload = [
  54. 'iat' => $issuedAt,
  55. 'exp' => $accessExpire,
  56. 'sub' => $user['ID'],
  57. 'name' => $user['NAME'] ?? '',
  58. ];
  59. // 리프레시 토큰 existing check
  60. $currentRefreshToken = $user['REFRESH_TOKEN'] ?? null;
  61. $validRefreshToken = null;
  62. // 토큰이 **없거나** (빈 값, null), 존재하지만 만료된 경우 모두 새로 발급
  63. $needIssueRefresh = !$currentRefreshToken; // null 또는 빈 문자열 등
  64. if ($currentRefreshToken) {
  65. // 기존 리프레시 토큰 유효성 검사
  66. try {
  67. $key = new Key($jwtSecret, 'HS256');
  68. $decoded = JWT::decode($currentRefreshToken, $key);
  69. if (isset($decoded->exp) && $decoded->exp > time()) {
  70. // 만료되지 않음, 재사용
  71. $validRefreshToken = $currentRefreshToken;
  72. $needIssueRefresh = false;
  73. } else {
  74. // 만료됨
  75. $needIssueRefresh = true;
  76. }
  77. } catch (\Throwable $e) {
  78. // 토큰이 변조됐거나 잘못된 경우에도 새로 발급
  79. $needIssueRefresh = true;
  80. }
  81. }
  82. if ($needIssueRefresh) {
  83. // 없거나 만료/무효화 시 새로 발급 및 DB 업데이트
  84. $refreshPayload = [
  85. 'iat' => $issuedAt,
  86. 'exp' => $refreshExpire,
  87. 'sub' => $user['ID'],
  88. ];
  89. try {
  90. $validRefreshToken = JWT::encode($refreshPayload, $jwtSecret, 'HS256', $kid);
  91. // ADM_LIST에 리프레시 토큰 값 업데이트 (신규 발급 포함)
  92. $builder->where('ID', $user['ID'])->update(['REFRESH_TOKEN' => $validRefreshToken]);
  93. } catch (\Throwable $e) {
  94. return $this->failServerError('JWT 생성 오류: ' . $e->getMessage());
  95. }
  96. }
  97. try {
  98. $accessToken = JWT::encode($accessPayload, $jwtSecret, 'HS256', $kid);
  99. } catch (\Throwable $e) {
  100. return $this->failServerError('JWT 생성 오류: ' . $e->getMessage());
  101. }
  102. return $this->respond([
  103. 'status' => 'active',
  104. 'accessToken' => $accessToken,
  105. 'refreshToken' => $validRefreshToken,
  106. 'user' => $user,
  107. ]);
  108. }
  109. public function refreshToken()
  110. {
  111. $data = $this->request->getJSON(true);
  112. $refreshToken = $data['refreshToken'] ?? null;
  113. if (!$refreshToken) {
  114. return $this->fail('리프레시 토큰이 필요합니다.', 400);
  115. }
  116. $jwtSecret = env('JWT_SECRET');
  117. $kid = env('JWT_KID');
  118. try {
  119. $key = new Key($jwtSecret, 'HS256');
  120. $headers = null;
  121. $decoded = JWT::decode($refreshToken, $key, $headers);
  122. if ($decoded->exp < time()) {
  123. return $this->fail('리프레시 토큰이 만료되었습니다.', 401);
  124. }
  125. $userId = $decoded->sub ?? null;
  126. if (!$userId) {
  127. return $this->fail('유효하지 않은 토큰입니다.', 401);
  128. }
  129. // ADM_LIST에서 해당 유저와 REFRESH_TOKEN 비교
  130. $db = \Config\Database::connect();
  131. $builder = $db->table('ADM_LIST');
  132. $user = $builder->where('ID', $userId)->get()->getRowArray();
  133. if (!$user) {
  134. return $this->fail('사용자를 찾을 수 없습니다.', 404);
  135. }
  136. unset($user['PASSWORD']);
  137. // DB에 저장된 리프레시 토큰과 요청 리프레시 토큰이 일치하지 않으면 거절
  138. if (!isset($user['REFRESH_TOKEN']) || $user['REFRESH_TOKEN'] !== $refreshToken) {
  139. return $this->fail('리프레시 토큰 불일치 또는 무효한 요청입니다.', 401);
  140. }
  141. // 일치하면 액세스 토큰 발급
  142. $issuedAt = time();
  143. $expire = $issuedAt + 60 * 15; // 15분
  144. //$expire = $issuedAt + 5; // 15분
  145. $accessPayload = [
  146. 'iat' => $issuedAt,
  147. 'exp' => $expire,
  148. 'sub' => $userId,
  149. 'name' => $user['NAME'] ?? '',
  150. ];
  151. $accessToken = JWT::encode($accessPayload, $jwtSecret, 'HS256', $kid);
  152. return $this->respond([
  153. 'accessToken' => $accessToken,
  154. 'user' => $user,
  155. ]);
  156. } catch (\Throwable $e) {
  157. return $this->fail('유효하지 않은 리프레시 토큰입니다.', 401);
  158. }
  159. }
  160. }