get('logged_in')) { return redirect()->to('/'); } return view('auth/login'); } public function login() { $rules = [ 'login_id' => 'required|max_length[50]', 'password' => 'required|max_length[255]', ]; $messages = [ 'login_id' => [ 'required' => '아이디를 입력해 주세요.', 'max_length' => '아이디는 50자 이하여야 합니다.', ], 'password' => [ 'required' => '비밀번호를 입력해 주세요.', 'max_length' => '비밀번호는 255자 이하여야 합니다.', ], ]; if (! $this->validate($rules, $messages)) { return redirect()->back() ->withInput() ->with('errors', $this->validator->getErrors()); } $loginId = trim($this->request->getPost('login_id')); $password = $this->request->getPost('password'); $memberModel = model(MemberModel::class); $member = $memberModel->findByLoginId($loginId); $approvalModel = model(MemberApprovalRequestModel::class); $logData = $this->buildLogData($loginId, $member?->mb_idx); if ($member === null) { $this->insertMemberLog($logData, false, '회원 정보를 찾을 수 없습니다.'); return redirect()->back() ->withInput() ->with('error', '아이디 또는 비밀번호가 올바르지 않습니다.'); } if ($member->mb_state === self::MB_STATE_LEAVE) { $this->insertMemberLog($logData, false, '탈퇴한 회원입니다.'); return redirect()->back() ->withInput() ->with('error', '탈퇴한 회원입니다.'); } if ($member->mb_state === self::MB_STATE_BANNED) { $this->insertMemberLog($logData, false, '정지된 회원입니다.'); return redirect()->back() ->withInput() ->with('error', '정지된 회원입니다.'); } if (! password_verify($password, $member->mb_passwd)) { $this->insertMemberLog($logData, false, '비밀번호 불일치'); return redirect()->back() ->withInput() ->with('error', '아이디 또는 비밀번호가 올바르지 않습니다.'); } // 승인 요청 상태 확인(공개 회원가입 사용자) $latestApproval = $approvalModel->getLatestByMember((int) $member->mb_idx); if ($latestApproval !== null) { if ($latestApproval->mar_status === MemberApprovalRequestModel::STATUS_PENDING) { $this->insertMemberLog($logData, false, '승인 대기 상태'); return redirect()->back() ->withInput() ->with('error', '관리자 승인 후 로그인 가능합니다.'); } if ($latestApproval->mar_status === MemberApprovalRequestModel::STATUS_REJECTED) { $this->insertMemberLog($logData, false, '승인 반려 상태'); return redirect()->back() ->withInput() ->with('error', '승인이 반려되었습니다. 관리자에게 문의해 주세요.'); } } // 로그인 성공 $sessionData = [ 'mb_idx' => $member->mb_idx, 'mb_id' => $member->mb_id, 'mb_name' => $member->mb_name, 'mb_level' => $member->mb_level, 'mb_lg_idx' => $member->mb_lg_idx ?? null, 'logged_in' => true, ]; session()->set($sessionData); $memberModel->update($member->mb_idx, [ 'mb_latestdate' => date('Y-m-d H:i:s'), ]); $this->insertMemberLog($logData, true, '로그인 성공', $member->mb_idx); // 지자체 관리자 → 관리자 대시보드로 이동 if ((int) $member->mb_level === \Config\Roles::LEVEL_LOCAL_ADMIN) { return redirect()->to(site_url('admin'))->with('success', '로그인되었습니다.'); } // super admin → 지자체 선택 페이지로 이동 (선택 후 관리자 페이지 사용) if ((int) $member->mb_level === \Config\Roles::LEVEL_SUPER_ADMIN) { return redirect()->to(site_url('admin/select-local-government'))->with('success', '로그인되었습니다.'); } return redirect()->to(site_url('/'))->with('success', '로그인되었습니다.'); } public function logout() { if (session()->get('logged_in')) { $mbIdx = session()->get('mb_idx'); $mbId = session()->get('mb_id'); $log = model(MemberLogModel::class) ->where('mb_idx', $mbIdx) ->where('mll_success', 1) ->orderBy('mll_idx', 'DESC') ->first(); if ($log !== null) { model(MemberLogModel::class)->update($log->mll_idx, [ 'mll_logout_date' => date('Y-m-d H:i:s'), ]); } else { model(MemberLogModel::class)->insert([ 'mll_success' => 1, 'mb_idx' => $mbIdx, 'mb_id' => $mbId ?? '', 'mll_regdate' => date('Y-m-d H:i:s'), 'mll_ip' => $this->request->getIPAddress(), 'mll_msg' => '로그아웃', 'mll_useragent' => substr((string) $this->request->getUserAgent(), 0, 500), 'mll_logout_date' => date('Y-m-d H:i:s'), ]); } } session()->destroy(); return redirect()->to('login')->with('success', '로그아웃되었습니다.'); } public function showRegisterForm() { $localGovernments = model(LocalGovernmentModel::class) ->where('lg_state', 1) ->orderBy('lg_name', 'ASC') ->findAll(); return view('auth/register', [ 'localGovernments' => $localGovernments, ]); } public function register() { $rules = [ 'mb_id' => 'required|min_length[4]|max_length[50]|is_unique[member.mb_id]', 'mb_passwd' => 'required|min_length[4]|max_length[255]', 'mb_passwd_confirm' => 'required|matches[mb_passwd]', 'mb_name' => 'required|max_length[100]', 'mb_email' => 'permit_empty|valid_email|max_length[100]', 'mb_phone' => 'permit_empty|max_length[20]', 'mb_lg_idx' => 'permit_empty|is_natural_no_zero', 'mb_level' => 'required|in_list[1,2,3]', ]; $messages = [ 'mb_id' => [ 'required' => '아이디를 입력해 주세요.', 'min_length' => '아이디는 4자 이상이어야 합니다.', 'max_length' => '아이디는 50자 이하여야 합니다.', 'is_unique' => '이미 사용 중인 아이디입니다.', ], 'mb_passwd' => [ 'required' => '비밀번호를 입력해 주세요.', 'min_length' => '비밀번호는 4자 이상이어야 합니다.', 'max_length' => '비밀번호는 255자 이하여야 합니다.', ], 'mb_passwd_confirm' => [ 'required' => '비밀번호 확인을 입력해 주세요.', 'matches' => '비밀번호가 일치하지 않습니다.', ], 'mb_name' => [ 'required' => '이름을 입력해 주세요.', 'max_length' => '이름은 100자 이하여야 합니다.', ], 'mb_email' => [ 'valid_email' => '올바른 이메일 형식이 아닙니다.', 'max_length' => '이메일은 100자 이하여야 합니다.', ], 'mb_phone' => [ 'max_length' => '연락처는 20자 이하여야 합니다.', ], 'mb_level' => [ 'required' => '사용자 역할을 선택해 주세요.', 'in_list' => '유효하지 않은 역할입니다.', ], ]; if (! $this->validate($rules, $messages)) { return redirect()->back() ->withInput() ->with('errors', $this->validator->getErrors()); } $mbLevel = (int) $this->request->getPost('mb_level'); if (! config('Roles')->isValidLevel($mbLevel)) { $mbLevel = config('Roles')->defaultLevelForSelfRegister; } $lgIdx = $this->request->getPost('mb_lg_idx'); $mbLgIdx = ($lgIdx !== null && $lgIdx !== '' && (int) $lgIdx > 0) ? (int) $lgIdx : null; helper('pii_encryption'); $data = [ 'mb_id' => $this->request->getPost('mb_id'), 'mb_passwd' => password_hash($this->request->getPost('mb_passwd'), PASSWORD_DEFAULT), 'mb_name' => $this->request->getPost('mb_name'), 'mb_email' => pii_encrypt($this->request->getPost('mb_email') ?? ''), 'mb_phone' => pii_encrypt($this->request->getPost('mb_phone') ?? ''), 'mb_lang' => 'ko', // 공개 회원가입 시점에는 역할을 활성화하지 않고 기본 레벨로 생성(승인 후 requested_level 반영) 'mb_level' => \Config\Roles::LEVEL_CITIZEN, 'mb_group' => '', 'mb_lg_idx' => $mbLgIdx, 'mb_state' => 1, 'mb_regdate' => date('Y-m-d H:i:s'), ]; $memberModel = model(MemberModel::class); if (! $memberModel->insert($data)) { return redirect()->back() ->withInput() ->with('error', '회원가입 처리 중 오류가 발생했습니다. 다시 시도해 주세요.'); } $newMemberIdx = (int) $memberModel->getInsertID(); $approvalModel = model(MemberApprovalRequestModel::class); $approvalModel->insert([ 'mb_idx' => $newMemberIdx, 'mar_requested_level' => $mbLevel, 'mar_status' => MemberApprovalRequestModel::STATUS_PENDING, 'mar_request_note' => '', 'mar_reject_reason' => null, 'mar_requested_at' => date('Y-m-d H:i:s'), 'mar_requested_by' => $newMemberIdx, 'mar_processed_at' => null, 'mar_processed_by' => null, ]); return redirect()->to('login')->with('success', '회원가입이 완료되었습니다. 관리자 승인 후 로그인 가능합니다.'); } private function buildLogData(string $mbId, ?int $mbIdx): array { return [ 'mb_idx' => $mbIdx, 'mb_id' => $mbId, 'mll_regdate' => date('Y-m-d H:i:s'), 'mll_ip' => $this->request->getIPAddress(), 'mll_useragent' => substr((string) $this->request->getUserAgent(), 0, 500), 'mll_url' => current_url(), 'mll_referer' => $this->request->getServer('HTTP_REFERER'), ]; } private function insertMemberLog(array $data, bool $success, string $msg, ?int $mbIdx = null): void { $data['mll_success'] = $success ? 1 : 0; $data['mll_msg'] = $msg; if ($mbIdx !== null) { $data['mb_idx'] = $mbIdx; } model(MemberLogModel::class)->insert($data); } }