['required', 'string', 'email', 'max:60'], 'mem_pw' => ['required', 'string', 'max:100'], 'return_url'=> ['nullable', 'string', 'max:2000'], ]; if (app()->environment('production')) { $rules['g-recaptcha-response'] = ['required', new RecaptchaV3Rule('login')]; } $v = Validator::make($request->all(), $rules, [ 'mem_email.required' => '아이디 혹은 비밀번호가 일치하지 않습니다.', 'mem_email.email' => '아이디 혹은 비밀번호가 일치하지 않습니다.', 'mem_pw.required' => '아이디 혹은 비밀번호가 일치하지 않습니다.', 'g-recaptcha-response.required' => '올바른 접근이 아닙니다.', ]); if ($v->fails()) { return back()->withErrors($v)->withInput(); } $email = strtolower(trim((string)$request->input('mem_email'))); $pw = (string)$request->input('mem_pw'); // return_url: 오픈리다이렉트 방지 (내부 path만 허용) $returnUrl = (string)($request->input('return_url') ?? '/'); if ($returnUrl === '' || str_starts_with($returnUrl, 'http://') || str_starts_with($returnUrl, 'https://') || str_starts_with($returnUrl, '//')) { $returnUrl = '/'; } if (!str_starts_with($returnUrl, '/')) { $returnUrl = '/'; } $res = $memInfoService->attemptLegacyLogin([ 'email' => $email, 'password' => $pw, 'ip' => $request->ip(), 'ua' => substr((string)$request->userAgent(), 0, 500), 'return_url' => $returnUrl, ]); if (!($res['ok'] ?? false)) { // 이메일 미인증이면 confirm 페이지로 이동 if (($res['reason'] ?? null) === 'email_unverified') { // 세션 고정 공격 방지 (중요) $request->session()->regenerate(); $request->session()->put('auth_user', [ 'mem_no' => (int)($res['mem_no'] ?? 0), 'email' => (string)($res['email'] ?? $email), 'issued_at' => now()->timestamp, 'expires_at' => now()->addMinutes(30)->timestamp, // auth_user 세션 유효기간 ]); return redirect()->route('web.auth.email.required'); } // 그 외 실패는 기존 방식 유지 return back() ->withErrors(['login' => $res['message'] ?? '로그인에 실패했습니다.']) ->withInput(['mem_email' => $email]); } // 세션 저장 AuthSession::putMember($res['session']); return redirect()->to($res['redirect'] ?? $returnUrl); } /** * (옵션) 휴면 해제 링크 처리 - 최소 골격 * 실제 로직은 다음 단계에서 dormancy 테이블 검증/만료/상태변경까지 붙이면 됨 */ public function dormancyPrc(Request $request) { // TODO: Crypt::decryptString(authnum) -> "auth_key|seq" // TODO: mem_dormancy 검증/만료/인증완료 처리 return redirect()->route('web.auth.login') ->withErrors(['login' => '휴면 해제 처리는 다음 단계에서 연결합니다.']); } public function logout(Request $request) { // $request->session()->forget('_sess'); // // // (선택) 회원가입/본인인증 진행 세션까지 같이 정리하고 싶으면 추가 // $request->session()->forget('signup'); // $request->session()->forget('register'); // // // (선택) 디버그 세션 정리 // $request->session()->forget('debug'); // // $request->session()->save(); $request->session()->invalidate(); $request->session()->regenerateToken(); return redirect()->route('web.home') ->with('ui_dialog', [ 'type' => 'alert', 'title' => '안내', 'message' => '로그아웃 되었습니다.', ]); } }