아이디/비밀번호 찾기 추가개발 건

This commit is contained in:
iyak 2025-10-01 07:59:10 +00:00
parent d38117f898
commit 41780ba327
20 changed files with 918 additions and 297 deletions

View File

@ -1,13 +1,13 @@
using System;
using NP.Base.Auth;
using NP.Base.ENUM;
using NP.Model;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Mvc;
using NP.Model;
using System.Collections;
using NP.Base.Auth;
using NP.Base.ENUM;
namespace NP.Base.Controllers
{
/// <summary>
@ -140,7 +140,6 @@ namespace NP.Base.Controllers
return JsonOK(Dao.Save("lect.edub2b", e));
}
//[HttpPost]
//public JsonResult SaveUserFile(String type, Int64? fgno)
//{

View File

@ -1,16 +1,12 @@
using IBatisNet.Common.Transaction;
using Newtonsoft.Json;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NP.Base.Auth;
using NP.Base.ENUM;
using NP.Frame;
using NP.Model;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.ModelBinding;
using System.Web.Mvc;
namespace NP.Base.Controllers
@ -47,55 +43,24 @@ namespace NP.Base.Controllers
{
Dao.Save("sys.file.down", fno);
var file = Dao.Get<File>("sys.file.get" + (getdel > 0 ? "all" : ""), fno).FirstOrDefault();
//if (!string.IsNullOrEmpty(loginfo))
//{
// LogSet(GetLong(loginfo.Split('|')[0]), null, fno, GetInt(loginfo.Split('|')[1]), 5, null);
//}
//if (((file.tablename ?? "").ToUpper() +"."+ (file.columnname??"").ToUpper()).Equals("삭제체크테이블.삭제체크컬럼"))
//{
// //프로젝트파일 권한 확인
// if (Dao.Get<int>("sys.file.authcheck.orderfgno", new Hashtable() { {"FGNo", file.fgno }, {"UserNo", SUserInfo.UserNo } }).First() < 1)
// {
// return;
// }
//}
//else if ((file.UsingTable ?? "").ToUpper().Equals("users.fgno"))
//{
// //이력서파일 권한 확인
// if (Dao.Get<int>("sys.file.authcheck.usersfgno", new Hashtable() { { "FGNo", file.FGNo }, { "UserNo", SUserInfo.UserNo } }).First() < 1)
// {
// return;
// }
//}
if (Request.UrlReferrer == null || (!SUserInfo.IsAdmin && file.tablename == "lectsd" && file.columnname == "fgno" && SUserInfo.UserNo != file.cno))
{
//Response.AppendHeader("Content-Disposition", cds.ToString());
//return File(string.Format("{0}\\{1}", uploadDir, entity.SaveFileName), "application/file");
{
Response.Clear();
Response.ClearHeaders();
Response.ClearContent();
Response.ContentType = "Application/octet-stream";
Response.AppendHeader("Content-Disposition", "attachment;filename=" + Server.UrlEncode("권한없음.png"));
//Response.AppendHeader("Content-Length", file.filesize.ToString());
Response.AppendHeader("Content-Disposition", "attachment;filename=" + Server.UrlEncode("권한없음.png"));
Response.TransmitFile(string.Format("{0}", Server.MapPath("/img/repute_tail.png")));
Response.Flush();
Response.End();
}
else
{
string fileurl = Server.MapPath((issubject == 1 ? file.fullurl2 : file.fullurl));
//var cds = new System.Net.Mime.ContentDisposition
//{
// FileName = Server.UrlEncode(file.orgname), // 파일의 원래이름(등록할때의 이름)
// Inline = false,
//};
//Response.AppendHeader("Content-Disposition", cds.ToString());
//return File(string.Format("{0}\\{1}", uploadDir, entity.SaveFileName), "application/file");
string fileurl = Server.MapPath((issubject == 1 ? file.fullurl2 : file.fullurl));
Response.Clear();
Response.ClearHeaders();
Response.ClearContent();
//Response.ContentType = "Application/octet-stream";
Response.ClearContent();
Response.ContentType = "text/plain";
Response.AppendHeader("Content-Disposition", "attachment;filename=" + Server.UrlEncode(file.orgname));
Response.AppendHeader("Content-Length", file.filesize.ToString());
@ -361,10 +326,9 @@ namespace NP.Base.Controllers
if (IsEmail && users.Count == 1)
{
var MHtml = "<img src=\"" + fronturl + "/img/common/gnb_logo.gif\" alt=\"\" /><br /><br />안녕하세요, " + user.username + "님,<br />당신의 영남건설기술교육원 비밀번호를 재설정하기 위한 링크를 전달합니다.<br />" +
"아래 링크를 클릭하셔서 비밀번호를 재설정해주세요.<br /><br />" +
"<a href=\"" + fronturl + "/Account/FindMe?pwcallno=" + pwcallno + "&userno=" + user.userno + "\" target=\"_blank\" style=\"color: #0094ff;\">비밀번호 재설정</a><br /><br />이 링크는 24시간 뒤 만료됩니다.";
var MHtml = $"안녕하세요, {user.username}님,<br />당신의 영남건설기술교육원 비밀번호를 재설정하기 위한 링크를 전달합니다.<br />" +
$"아래 링크를 클릭하셔서 비밀번호를 재설정해주세요.<br /><br />" +
$"<a href=\"{fronturl}/Account/FindMe?pwcallno={pwcallno}&userno={user.userno}\" target=\"_blank\" style=\"color: #0094ff;\">비밀번호 재설정</a><br /><br />이 링크는 24시간 뒤 만료됩니다.";
var mailResult = SendEmail(masteremail.Split(';')[1], GetInt(masteremail.Split(';')[2]), masteremail.Split(';')[3], masteremail.Split(';')[4], 999, user.email, masteremail.Split(';')[0], "영남건설기술교육원 운영자", "[영남건설기술교육원] 비밀번호 찾기 안내", MHtml, "", "0:0");
return JsonOK(mailResult);
@ -1603,5 +1567,77 @@ namespace NP.Base.Controllers
return JsonOKObj(result);
}
/// <summary>
/// 비밀번호 변경
/// </summary>
/// <returns></returns>
[HttpPost]
[ValidateAntiForgeryToken]
public JsonResult UserChangePassword(string userid, string password)
{
try
{
#region + ( )
// 1. 빈 문자열 확인
if (string.IsNullOrEmpty(userid) || string.IsNullOrEmpty(password))
{
return Json(new { success = false, message = "아이디 또는 비밀번호를 입력해주세요.", data = -1 });
}
// 2. 비밀번호 복잡도 검사 (8자 이상, 영문, 숫자, 특수문자 포함)
var passwordRegex = new Regex("^(?=.*[a-zA-Z])(?=.*\\d)(?=.*[\\W_]).{8,}$");
if (!passwordRegex.IsMatch(password))
{
return Json(new { success = false, message = "비밀번호는 8자 이상이며, 영문, 숫자, 특수문자를 포함해야 합니다.", data = -2 });
}
// 3. 3번 이상 연속되는 동일한 문자/숫자/기호 검사
var repeatRegex = new Regex("(.)\\1\\1");
if (repeatRegex.IsMatch(password))
{
return Json(new { success = false, message = "연속으로 3번 이상 반복되는 문자를 사용할 수 없습니다.", data = -3 });
}
#endregion
// 모든 유효성 검사를 통과한 경우 진행
var p = new Hashtable();
long userNo = -1;
#region + Get: users.findme
// userno 추출을 위한 목적
p.Clear();
p.Add("userid", userid);
Users user = Dao.Get<Users>("users.findme", p)
.FirstOrDefault() ?? new Users();
userNo = user.userno;
#endregion
#region + Save: users.change.password ( )
if (userNo > 0)
{
p.Clear();
p.Add("userid", userid);
p.Add("userpass", Base.Lib.KISA_SHA256.SHA256Hash(password));
p.Add("userno", userNo);
p.Add("uip", GetUserIP());
Dao.Save("users.change.password", p);
}
#endregion
return Json(new { success = true, message = "비밀번호가 성공적으로 변경되었습니다.", data = 0 });
}
catch (Exception ex)
{
SetError($"UserChangePassword: {ex.Message}");
return Json(new { success = false, message = "처리 중 오류가 발생했습니다. 다시 시도해주세요.", data = -10 });
}
}
}
}

View File

@ -339,7 +339,33 @@
from userpasslog
where userno = #userno# and userpass = #userpass#
</select>
<!-- 비밀번호 찾기 > 비밀번호 변경 -->
<update id="users.change.password" parameterClass="hashtable">
<![CDATA[
UPDATE users
SET userpass = #userpass#
, udt = now()
, status = (CASE WHEN status = 9 THEN 1 ELSE status END)
, pwcalltime = null
, pwcallno = null
, uip = #uip#
, uno = #userno#
WHERE userid = #userid#;
INSERT INTO userpasslog (userno, userpass, cdt, uip)
SELECT #userno#, #userpass#, NOW(), #uip#
FROM (SELECT 1) a
LEFT OUTER JOIN userpasslog b
ON b.userno = #userno#
WHERE b.userno IS NULL;
UPDATE userpasslog
SET userpass = #userpass#
WHERE userno = #userno#;
]]>
</update>
<update id="users.up" parameterClass="hashtable">
<isNotNull property="userpass">
insert into userpasslog (userno, userpass, cdt, uip)
@ -715,15 +741,19 @@
<update id="users.asno.save" parameterClass="hashtable">
update users set asno=#asno# where userno=#userno#
</update>
<!-- 비밀번호 찾기 -->
<select id="users.findme" parameterClass="hashtable" resultClass="users">
SELECT a.userno,a.username
,a.userid
SELECT a.userno
,a.username
,a.userid
,a.status
,a.ci
,CAST(AES_DECRYPT(UNHEX(a.mobile), <include refid="sql.digest"></include>) AS char) mobile
,CAST(AES_DECRYPT(UNHEX(a.email), <include refid="sql.digest"></include>) AS char) email
FROM users a
WHERE a.usertype IN (1,11)
<!-- hs => 이메일과 핸드폰번호 찾기의 상황을 알 수 없어 STATUS를 타입별로 분리하고, EMAIL에만 9를 추가 -->
<!-- hs => 이메일과 핸드폰번호 찾기의 상황을 알 수 없어 STATUS를 타입별로 분리하고, EMAIL에만 9를 추가, CI도 추가 -->
<isNotNull property="email">
AND a.email=HEX(AES_ENCRYPT(#email#, <include refid="sql.digest"></include>))
AND (a.STATUS = 1 OR a.STATUS = 9)
@ -732,7 +762,15 @@
AND a.mobile=HEX(AES_ENCRYPT(#mobile#, <include refid="sql.digest"></include>))
AND a.STATUS = 1
</isNotNull>
</select>
<isNotNull property="ci">
AND a.CI = #ci#
AND (a.STATUS = 1 OR a.STATUS = 9)
</isNotNull>
<isNotNull property="userid">
AND a.userid = #userid#
AND (a.STATUS = 1 OR a.STATUS = 9)
</isNotNull>
</select>
<select id="users.findmes" parameterClass="hashtable" resultClass="users">
select a.userno,a.username
@ -774,7 +812,15 @@
</select>
<update id="users.resetuser" parameterClass="hashtable">
update users set userpass=#userpass#, pwcalltime=null,pwcallno=null where userno=#userno# and pwcalltime is not null and pwcallno=#pwcallno# and date_add(pwcalltime, interval 24 hour) &gt; <include refid="sql.now"></include>
update users
set userpass = #userpass#
, pwcalltime = null
, pwcallno = null
, status = (CASE WHEN status = 9 THEN 1 ELSE status END)
where userno=#userno#
and pwcalltime is not null
and pwcallno=#pwcallno#
and date_add(pwcalltime, interval 24 hour) &gt; <include refid="sql.now"></include>
</update>
<update id="users.usercheck" parameterClass="hashtable">
update usercheck a

View File

@ -7,7 +7,9 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Helpers;
using System.Web.Mvc;
using XPayClientNet;
@ -147,15 +149,6 @@ namespace NP.FO.Controllers
Server.ClearError();
if (Request.IsAjaxRequest())
{
//public static bool IsAjaxRequest(this HttpRequest request)
//{
// if (request == null)
// {
// throw new ArgumentNullException("request");
// }
//
// return (request["X-Requested-With"] == "XMLHttpRequest") || ((request.Headers != null) && (request.Headers["X-Requested-With"] == "XMLHttpRequest"));
//}
return JsonError<Exception>(Base.ENUM.JSONCode.Error, vm.msg + ":" + Request.Url.AbsolutePath + (exception ?? new Exception()).Message, exception);
}
return View("Error", vm);
@ -500,6 +493,46 @@ namespace NP.FO.Controllers
return View();
}
/// <summary>
/// ID/PW 찾기 신규
/// </summary>
/// <returns></returns>
public ActionResult FindIDPW()
{
return View();
}
/// <summary>
/// 비빌번호 재설정
/// </summary>
/// <param name="vm"></param>
/// <returns></returns>
public ActionResult NewPassword(VMUser vm)
{
try
{
#region + Get: users.findme
var p = new Hashtable();
p.Add("ci", vm.User.ci);
// status 1: 활성, 8: 휴면, 9: 비활성(정지, 삭제), 98: 탈퇴신청, 99: 탈퇴
vm.User = Dao.Get<Users>("users.findme", p)
.FirstOrDefault() ?? new Users();
#endregion
}
catch (Exception ex)
{
SetError($"NewPassword 에러 : {ex.Message}");
}
return View(vm);
}
#region (mobile(,),ipin)
/// <summary>
/// 본인인증 요청(mobile(가입이전,휴대폰인증),ipin)
@ -513,8 +546,7 @@ namespace NP.FO.Controllers
String REQ_SITE_NM = "YNICTE";
String REQ_URL = "";
String RETURN_MSG = "";
String SITE_URL = "https://" + Request.Url.Host;
//String SITE_URL = GetConfig("fronturl");
String SITE_URL = "https://" + Request.Url.Host;
//인증요청사유코드
//00 : 회원가입
//01 : 성인인증
@ -541,8 +573,7 @@ namespace NP.FO.Controllers
//return url 설정
RETURN_URL = "https://" + Request.Url.Host + "/Account/CertOk3" + sel + "?reason=" + reason + (!string.IsNullOrEmpty(param1) ? "&param1=" + param1 : "") + (!string.IsNullOrEmpty(param2) ? "&param2=" + param2 : "");
RTN_URL = "https://" + Request.Url.Host + "/Account/CertOk3" + sel + "?reason=" + reason + (!string.IsNullOrEmpty(param1) ? "&param1=" + param1 : "") + (!string.IsNullOrEmpty(param2) ? "&param2=" + param2 : "");
//RETURN_URL = GetConfig("fronturl") + "/Account/CertOk3" + sel + "?reason=" + reason + (!string.IsNullOrEmpty(param1) ? "&param1=" + param1 : "") + (!string.IsNullOrEmpty(param2) ? "&param2=" + param2 : "");
//RTN_URL = GetConfig("fronturl") + "/Account/CertOk3" + sel + "?reason=" + reason + (!string.IsNullOrEmpty(param1) ? "&param1=" + param1 : "") + (!string.IsNullOrEmpty(param2) ? "&param2=" + param2 : "");
//모바일팝업
if (sel == "MOBI")
{
@ -613,7 +644,7 @@ namespace NP.FO.Controllers
String REQ_URL = "";
String RETURN_MSG = "";
String SITE_URL = "https://" + Request.Url.Host;
//String SITE_URL = GetConfig("fronturl");
//인증요청사유코드
//00 : 회원가입
//01 : 성인인증
@ -640,8 +671,7 @@ namespace NP.FO.Controllers
//return url 설정
RETURN_URL = "https://" + Request.Url.Host + "/Account/CertOk3" + sel + "2?reason=" + reason + (!string.IsNullOrEmpty(param1) ? "&param1=" + param1 : "") + (!string.IsNullOrEmpty(param2) ? "&param2=" + param2 : "");
RTN_URL = "https://" + Request.Url.Host + "/Account/CertOk3" + sel + "2?reason=" + reason + (!string.IsNullOrEmpty(param1) ? "&param1=" + param1 : "") + (!string.IsNullOrEmpty(param2) ? "&param2=" + param2 : "");
//RETURN_URL = GetConfig("fronturl") + "/Account/CertOk3" + sel + "?reason=" + reason + (!string.IsNullOrEmpty(param1) ? "&param1=" + param1 : "") + (!string.IsNullOrEmpty(param2) ? "&param2=" + param2 : "");
//RTN_URL = GetConfig("fronturl") + "/Account/CertOk3" + sel + "?reason=" + reason + (!string.IsNullOrEmpty(param1) ? "&param1=" + param1 : "") + (!string.IsNullOrEmpty(param2) ? "&param2=" + param2 : "");
//모바일팝업
if (sel == "MOBI")
{
@ -768,14 +798,6 @@ namespace NP.FO.Controllers
CI_UPDATE = outputobj["CI_UPDATE"].ToString();
TEL_COM_CD = outputobj["TEL_COM_CD"].ToString();
TEL_NO = outputobj["TEL_NO"].ToString();
//Users users = new Users()
//{
// username = RSLT_NAME,
// birthday = RSLT_BIRTHDAY,
// di=DI,
// mobile = TEL_NO,
// jointype=0
//};
TEL_NO = string.IsNullOrEmpty(TEL_NO) || TEL_NO.Replace("-", "").Length < 10 ? (TEL_NO ?? "") : TEL_NO.Replace("-", "").Length == 10 ? string.Format("{0}-{1}-{2}", TEL_NO.Replace("-", "").Substring(0, 3), TEL_NO.Replace("-", "").Substring(3, 3), TEL_NO.Replace("-", "").Substring(6)) : string.Format("{0}-{1}-{2}", TEL_NO.Replace("-", "").Substring(0, 3), TEL_NO.Replace("-", "").Substring(3, 4), TEL_NO.Replace("-", "").Substring(7));
if (!string.IsNullOrEmpty(TEL_NO))
{
@ -1295,8 +1317,7 @@ namespace NP.FO.Controllers
case "LectNo":
result = "er.잘못된 강좌코드 입니다.";
break;
default:
// NoCert
default:
result = "er.인증에 실패하였습니다.";
break;
}
@ -1366,15 +1387,7 @@ namespace NP.FO.Controllers
CI = outputobj["CI"].ToString();
CI_UPDATE = outputobj["CI_UPDATE"].ToString();
VSSN = outputobj["VSSN"].ToString();
//Users users = new Users()
//{
// username = RSLT_NAME,
// birthday = RSLT_BIRTHDAY,
// di = DI,
// ci = CI,
// vssn = VSSN,
// jointype = 0
//};
if (!string.IsNullOrEmpty(VSSN))
{
if (reason == "Join")
@ -1511,7 +1524,7 @@ namespace NP.FO.Controllers
+ RSLT_NAME + ":" + RSLT_BIRTHDAY + ":" + RSLT_SEX_CD + ":" + RSLT_NTV_FRNR_CD + ":"
+ DI + ":" + CI + ":" + CI_UPDATE + ":" + VSSN + ":"
+ RETURN_MSG;
//vm.stringval = "CP_CD:" + CP_CD + "&RSLT_CD:" + RSLT_CD + "&RSLT_MSG:" + RSLT_MSG;
Dao.Save("sys.kcblog.in", new System.Collections.Hashtable() { { "cp_cd", CP_CD }, { "vssn", VSSN }, { "logmsg", "RSLT_CD:" + RSLT_CD + "&RSLT_MSG:" + RSLT_MSG } });
ViewBag.reason = reason;
}
@ -1596,15 +1609,7 @@ namespace NP.FO.Controllers
CI = outputobj["CI"].ToString();
CI_UPDATE = outputobj["CI_UPDATE"].ToString();
VSSN = outputobj["VSSN"].ToString();
//Users users = new Users()
//{
// username = RSLT_NAME,
// birthday = RSLT_BIRTHDAY,
// di = DI,
// ci = CI,
// vssn = VSSN,
// jointype = 0
//};
if (!string.IsNullOrEmpty(VSSN))
{
rsltCd = CertOkRslt.Ok.ToString();
@ -1760,7 +1765,7 @@ namespace NP.FO.Controllers
+ RSLT_NAME + ":" + RSLT_BIRTHDAY + ":" + RSLT_SEX_CD + ":" + RSLT_NTV_FRNR_CD + ":"
+ DI + ":" + CI + ":" + CI_UPDATE + ":" + VSSN + ":"
+ RETURN_MSG;
//vm.stringval = "CP_CD:" + CP_CD + "&RSLT_CD:" + RSLT_CD + "&RSLT_MSG:" + RSLT_MSG;
Dao.Save("sys.kcblog.in", new System.Collections.Hashtable() { { "cp_cd", CP_CD }, { "vssn", VSSN }, { "logmsg", "RSLT_CD:" + RSLT_CD + "&RSLT_MSG:" + RSLT_MSG } });
}
@ -1814,7 +1819,6 @@ namespace NP.FO.Controllers
paramStr += "," + reasonNo;
paramStr += "," + rsltCd;
//paramEncStr = "stringval:" + EncString(paramStr);
ViewBag.result = EncString(paramStr);
}
else
@ -1833,7 +1837,6 @@ namespace NP.FO.Controllers
paramStr += "," + reasonNo;
paramStr += "," + rsltCd;
//paramEncStr = "stringval:" + EncString(paramStr);
ViewBag.result = EncString(paramStr);
}
else
@ -1843,7 +1846,6 @@ namespace NP.FO.Controllers
}
break;
}
//ViewBag.reason = reason;
if (errChk)
{
@ -1896,17 +1898,11 @@ namespace NP.FO.Controllers
if (Request.Cookies.Get("RTNURL") != null)
{
var cookie = Request.Cookies.Get("RTNURL");
ViewBag.RTNURL = cookie.Value;
//원래 마케팅용이어서 expire는 제외
//cookie.Expires = DateTime.Now.AddDays(-1);
//Response.Cookies.Add(cookie);
ViewBag.RTNURL = cookie.Value;
}
if (Request.Cookies.Get("ru") != null)
{
ViewBag.RTNURL = Request.Cookies.Get("ru");
//원래 마케팅용이어서 expire는 제외
//cookie.Expires = DateTime.Now.AddDays(-1);
//Response.Cookies.Add(cookie);
ViewBag.RTNURL = Request.Cookies.Get("ru");
}
if (ViewBag.UserNo > 0)

View File

@ -1,16 +1,10 @@
using System;
using NP.Model;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using NP.Model;
using NP.Base.Auth;
using XPayClientNet;
using System.Security.Cryptography;
using System.Text;
using System.Collections;
namespace NP.FO.Controllers
{
@ -34,24 +28,17 @@ namespace NP.FO.Controllers
}
else
{
//if (Request.IsSecureConnection &&
// (Request.Url.AbsolutePath.ToUpper().StartsWith("/CROOM/") ||
// Request.Url.AbsolutePath.ToUpper() == "/CROOM" ||
// Request.Url.AbsolutePath.ToUpper().StartsWith("/CDMS/")) &&
// Request.Url.AbsolutePath.ToUpper() != "/CDMS/HTTPS")
if (
Request.IsSecureConnection &&
Request.Url.AbsolutePath.ToUpper().StartsWith("/CDMS/") &&
Request.Url.AbsolutePath.ToUpper() != "/CDMS/HTTPS" && false
)
{
//Response.Redirect("http://" + Request.Url.Host + Request.Url.PathAndQuery, true);
{
filterContext.Result = new RedirectResult("http://" + Request.Url.Host + Request.Url.PathAndQuery);
}
else if ("Y".Equals(GetConfig("usessl")) &&
!Request.IsSecureConnection &&
!Request.Url.AbsolutePath.StartsWith("/Open/") &&
//!(Request.Url.AbsolutePath.ToUpper().StartsWith("/CROOM/") || Request.Url.AbsolutePath.ToUpper() == "/CROOM") &&
!Request.Url.AbsolutePath.StartsWith("/Open/") &&
!(Request.Url.AbsolutePath.ToUpper().StartsWith("/CDMS/") || Request.Url.AbsolutePath.ToUpper() == "/CDMS") &&
!Request.Url.AbsolutePath.ToUpper().StartsWith("/ACCOUNT/ERROR"))
{
@ -63,15 +50,9 @@ namespace NP.FO.Controllers
{
Response.Cookies["yicte"].Value = Request.Cookies["yicte"].Value + ";SameSite=None; Secure";
}
var cm = new Model.MenuPage() { };
//if (Request.Url.AbsolutePath.ToUpper().StartsWith("/DIVISION/A"))
//{
// cm = GetMENUPAGES.Where(w => (w.usertype == 0 || w.usertype == SUserInfo.UserType) && w.pageurl.Equals(Request.Url.PathAndQuery)).OrderByDescending(od => od.pdepth).FirstOrDefault();
//}
//else
//{
var cm = new Model.MenuPage() { };
cm = GetMENUPAGES.Where(w => (w.usertype == 0 || w.usertype == SUserInfo.UserType) && (w.pageurl ?? "").ToUpper().Equals(Request.Url.AbsolutePath.ToUpper())).OrderByDescending(od => od.pdepth).FirstOrDefault();
//}
if (cm != null || Request.Url.AbsolutePath.ToUpper().Equals("/HOME/HTML")
|| Request.Url.AbsolutePath.ToUpper().StartsWith("/ACCOUNT/BANEMAIL")
|| Request.Url.AbsolutePath.ToUpper().StartsWith("/ACCOUNT/LOGOUT")
@ -104,15 +85,9 @@ namespace NP.FO.Controllers
new NP.Model.PageLog() { uno = SUserInfo.UserNo, uip = GetUserIP(), logsite = 1, pno = cm.pno < 1 ? (int?)null : cm.pno, loginfo = (cm.ppagename ?? "") + " > " + cm.pagename })));
}
}
else if (GetMENUPAGES.Where(w => (w.pageurl ?? "").ToUpper().Equals(Request.Url.AbsolutePath.ToUpper())).OrderByDescending(od => od.pdepth).FirstOrDefault() != null
//&& !Request.Url.AbsolutePath.ToUpper().StartsWith("/CDMS/PLAY")
//&& !Request.Url.AbsolutePath.ToUpper().StartsWith("/CONTENTS")
)
else if (GetMENUPAGES.Where(w => (w.pageurl ?? "").ToUpper().Equals(Request.Url.AbsolutePath.ToUpper())).OrderByDescending(od => od.pdepth).FirstOrDefault() != null)
{
filterContext.Result = new RedirectResult("/Account/Index?fobase=xx&ru=" + Request.Url.AbsolutePath, true);
//학습창 등이 안뜨고 로그인창으로 갈 경우 여기에 urlreferrer suserinfo, viewbag.ssuser~, GetMENUPAGES 등의 값들을 로깅해본다.
//~~
//Response.Redirect("/Account/Index?ru=" + Request.Url.AbsolutePath, true);
filterContext.Result = new RedirectResult("/Account/Index?fobase=xx&ru=" + Request.Url.AbsolutePath, true);
}
else if (!Request.Url.AbsolutePath.ToUpper().StartsWith("/OPEN/") &&
!Request.Url.AbsolutePath.ToUpper().StartsWith("/HOME/POPUP") &&
@ -123,23 +98,11 @@ namespace NP.FO.Controllers
{
filterContext.Result = new RedirectResult("/Account/Error");
}
//if (Request.IsSecureConnection &&
//!Request.Url.AbsolutePath.ToUpper().StartsWith("/ACCOUNT/") &&
//!Request.Url.AbsolutePath.ToUpper().StartsWith("/OPEN/"))
//{
// //Redirect("http://" + Request.Url.Host + Request.Url.PathAndQuery);
// filterContext.Result = new RedirectResult("http://" + Request.Url.Host + Request.Url.PathAndQuery);
//}
else if (GetMENUPAGES.Where(w => (w.pageurl ?? "").ToUpper().Equals(Request.Url.AbsolutePath.ToUpper())).OrderByDescending(od => od.pdepth).FirstOrDefault() != null
//&& !Request.Url.AbsolutePath.ToUpper().StartsWith("/CDMS/PLAY")
//&& !Request.Url.AbsolutePath.ToUpper().StartsWith("/CONTENTS")
)
else if (GetMENUPAGES.Where(w => (w.pageurl ?? "").ToUpper().Equals(Request.Url.AbsolutePath.ToUpper())).OrderByDescending(od => od.pdepth).FirstOrDefault() != null)
{
filterContext.Result = new RedirectResult("/Account/Index?fobase=1&ru=" + Request.Url.AbsolutePath, true);
SetError("fobaseerror:path=" + Request.Url.AbsolutePath + "&urlrefer=" + Request.UrlReferrer + "&userinfo=" + SUserInfo.UserNo + "&ssuer=" + ViewBag.SSUserNo);
//학습창 등이 안뜨고 로그인창으로 갈 경우 여기에 urlreferrer suserinfo, viewbag.ssuser~, GetMENUPAGES 등의 값들을 로깅해본다.
//~~
//Response.Redirect("/Account/Index?ru=" + Request.Url.AbsolutePath, true);
SetError("fobaseerror:path=" + Request.Url.AbsolutePath + "&urlrefer=" + Request.UrlReferrer + "&userinfo=" + SUserInfo.UserNo + "&ssuer=" + ViewBag.SSUserNo);
}
else if (!Request.Url.AbsolutePath.ToUpper().StartsWith("/OPEN/") &&
!Request.Url.AbsolutePath.ToUpper().StartsWith("/HOME/POPUP") &&
@ -151,13 +114,6 @@ namespace NP.FO.Controllers
{
filterContext.Result = new RedirectResult("/Account/Error");
}
//if (Request.IsSecureConnection &&
//!Request.Url.AbsolutePath.ToUpper().StartsWith("/ACCOUNT/") &&
//!Request.Url.AbsolutePath.ToUpper().StartsWith("/OPEN/"))
//{
// //Redirect("http://" + Request.Url.Host + Request.Url.PathAndQuery);
// filterContext.Result = new RedirectResult("http://" + Request.Url.Host + Request.Url.PathAndQuery);
//}
}
}
private static IList<NP.Model.Popup> POPUPS { get; set; }
@ -223,8 +179,7 @@ namespace NP.FO.Controllers
get
{
if (BANNER == null)
{
//var banners = GetBANNERS;
{
BANNER = "";
if (GetBANNERS.Where(w => w.ismain == 0).Count() > 0)
{
@ -250,14 +205,10 @@ namespace NP.FO.Controllers
get
{
if (BANNERCROOM == null)
{
//var banners = GetBANNERS;
{
BANNERCROOM = "";
if (GetBANNERS.Where(w => w.ismain == 1).Count() > 0)
{
//BANNERCROOM = string.Join("", GetBANNERS.Where(w => w.ismain == 1).Select(s =>
// string.Format("<div class=\"mpgsdCont\" style=\"background:url({0})no-repeat left center;\"><img src=\"{1}\" alt=\"\" /></div>"
// , (new BaseModel()).rootfolder + s.fileurl, (new BaseModel()).rootfolder + s.fileurl2)));
{
}
}
return BANNERCROOM;
@ -339,8 +290,7 @@ namespace NP.FO.Controllers
sbmauth.Append(string.Format("<li class=\"topmenu{1}\"><a href=\"#\" onclick=\"gomenu({2}, {3}, '{4}', this)\">{0}</a><ul>", m.pagename, m.pno,menu.pno,menu.pagetype,menu.pageurl));
}
else
{
//sbmauth.Append(string.Format("<li class=\"topmenu{1}\"><a href=\"#\" onclick=\"gomenu({2}, {3}, '{4}', this)\">{0}</a><span></span><ul>", m.pagename, m.pno,menu.pno,menu.pagetype,menu.pageurl));
{
sbmauth.Append(string.Format("<li class=\"topmenu{1}\"><a href=\"#\" style=\"cursor:default;\">{0}</a><span></span><ul>", m.pagename, m.pno,menu.pno,menu.pagetype,menu.pageurl));
}
if (m.usertype == 0)
@ -350,8 +300,7 @@ namespace NP.FO.Controllers
sbm.Append(string.Format("<li class=\"topmenu{1}\"><a href=\"#\" onclick=\"msg('로그인 후 진행 할 수 있습니다.', null, null, null, 'location.href=\\'/Account/Index?ru={4}\\'')\">{0}</a><ul>", m.pagename, m.pno, menu.pno, menu.pagetype, menu.pageurl));
}
else
{
//sbm.Append(string.Format("<li class=\"topmenu{1}\"><a href=\"#\" onclick=\"gomenu({2}, {3}, '{4}', this)\">{0}</a><span></span><ul>", m.pagename, m.pno, menu.pno, menu.pagetype, menu.pageurl));
{
sbm.Append(string.Format("<li class=\"topmenu{1}\"><a href=\"#\" style=\"cursor:default;\">{0}</a><span></span><ul>", m.pagename, m.pno, menu.pno, menu.pagetype, menu.pageurl));
}
@ -419,23 +368,6 @@ namespace NP.FO.Controllers
}
protected void PayAll(VMPay vm)
{
//ViewBag.IsOldTLS = false;
//try
//{
// if (Request.IsSecureConnection)
// {
// System.Net.Security.SslStream sss = new System.Net.Security.SslStream(Request.UrlReferrer.);
// Console.WriteLine(sss.SslProtocol);
// //if (ssp.SslProtocol != System.Security.Authentication.SslProtocols.Tls12)
// //{
// // ViewBag.IsOldTLS = true;
// //}
// }
//}
//catch (Exception ex)
//{
// Console.WriteLine(ex.Message);
//}
vm.PayItems = new List<PayItem>() { };
if (vm.ispaycart)
{
@ -482,22 +414,11 @@ namespace NP.FO.Controllers
vm.pginfo = GetConfig("pginfo");
vm.pginfomobile = GetConfig("pginfomobile");
vm.signkey = ComputeHash(vm.pginfo.Split('|')[2]);
//vm.payitemname = Utf8ToEuckr();
vm.viewname5 = "https://" + Request.Url.Host + "/Course/PayClose";
//vm.viewname5 = GetConfig("fronturl") + "/Course/PayClose";
vm.viewname5 = "https://" + Request.Url.Host + "/Course/PayClose";
vm.previewname = vm.previewname ?? "/My/Cart";
vm.deliveramt = GetInt(GetConfig("deliveramt"));
}
//private String Utf8ToEuckr(String s)
//{
// byte[] pbSource = System.Text.Encoding.UTF8.GetBytes(s);
// byte[] pbDest = System.Text.Encoding.Convert(
// System.Text.Encoding.UTF8, System.Text.Encoding.GetEncoding("euc-kr"), pbSource);
// pbSource = System.Text.Encoding.Convert(System.Text.Encoding.GetEncoding("euc-kr"), System.Text.Encoding.UTF8, pbDest);
// char[] psUnicode = System.Text.UTF8Encoding.UTF8.GetChars(pbSource);
// return new string(psUnicode);
//}
protected ActionResult PayReturn(VMPay vm)
{
//20220802 추가
@ -833,8 +754,7 @@ namespace NP.FO.Controllers
{
item.eend2 = item.eend.ToString("yyyy년 MM월 dd일");
}
//vm.SelectCMInningscd.scdInfoSummary = string.Format("{0}", vm.SelectCMInningscd.estart.ToString("yyyy년 MM월 dd일까지"));
if (vm.SelectCMInningscd.estart < Convert.ToDateTime(DateTime.Now.ToString("yyyy-MM-dd 00:00:00")))
{
vm.SelectCMInningscd.isEnd = true;
@ -845,5 +765,4 @@ namespace NP.FO.Controllers
return View("ApplyComplete", vm);
}
}
}
}

View File

@ -329,6 +329,7 @@
<Content Include="img\common\intranet_logo_b.png" />
<Content Include="img\common\join_select01.png" />
<Content Include="img\common\join_select02.png" />
<Content Include="img\common\join_select03-4.png" />
<Content Include="img\common\join_select03.png" />
<Content Include="img\common\join_sns01.jpg" />
<Content Include="img\common\join_sns02.jpg" />
@ -756,6 +757,8 @@
<Content Include="Views\Home\Index_20250403백업_추후에배포.cshtml" />
<Content Include="Views\My\Document_250617_삭제예정.cshtml" />
<Content Include="Views\Course\SmartSearch.cshtml" />
<Content Include="Views\Account\FindIDPW.cshtml" />
<Content Include="Views\Account\NewPassword.cshtml" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Base\Base.csproj">

View File

@ -39,31 +39,51 @@
self.close();
</text>
} else if (ViewBag.reason == "AuthPlatformChange") {
<text>
if ("@(RSLT_NAME)" == $(opener.document).find("#mform").find("#username").val()) {
<text>
if ("@(RSLT_NAME)" == $(opener.document).find("#mform").find("#username").val()) {
$(opener.document).find("#mform").find("#authplatform").val("@(AuthPlatform.IPIN.GetHashCode())");
$(opener.document).find("#mform").find("#birthday").val("@(RSLT_BIRTHDAY)");
$(opener.document).find("#mform").find("#birthday").val("@(RSLT_BIRTHDAY)");
$(opener.document).find("#mform").find("#di").val("@(DI)");
$(opener.document).find("#mform").find("#ci").val("@(CI)");
$(opener.document).find("#mform").find("#vssn").val("@(VSSN)");
$(opener.document).find("#mform").find("#authPlatform_IPIN").show();
$(opener.document).find("#mform").find("#authPlatform_IPIN").css("padding-bottom","0px");
$(opener.document).find("#mform").find("#authPlatform_IPIN_Name").text("I-PIN 인증");
$(opener.document).find("#mform").find("#authPlatform_Mobile").hide();
try {
$(opener.document).find("#mform").find("#txtbirthday").text(moment("@(RSLT_BIRTHDAY)").format('YYYY.MM.DD'));
} catch (error) {
$(opener.document).find("#mform").find("#authPlatform_Mobile").hide();
try {
$(opener.document).find("#mform").find("#txtbirthday").text(moment("@(RSLT_BIRTHDAY)").format('YYYY.MM.DD'));
} catch (error) {
}
alert("인증완료");
} else {
alert("입력한 정보와 일치하지 않습니다.\n 재인증해주세요.");
} else {
alert("입력한 정보와 일치하지 않습니다.\n 재인증해주세요.");
}
self.close();
</text>
} else if (ViewBag.reason == "FINDIDPW") {
<text>
if ("@(RSLT_NAME)" != "" && "@CI" != "") {
try {
$(opener.document).find("#mform").find("#User_di").val("@(DI)");
$(opener.document).find("#mform").find("#User_ci").val("@(CI)");
$(opener.document).find("#mform").find("#User_authplatform").val("@(AuthPlatform.Mobile.GetHashCode())");
$(opener.document).find("#mform").attr("action", "/Account/NewPassword");
$(opener.document).find("#mform").submit();
alert("인증완료");
self.close();
}
catch (ex) {
console.error(ex.message);
}
} else {
alert("입력하신 아이디 또는 비밀번호와 일치하는 계정이 없습니다.\n 다시 확인해주세요.");
}
self.close();
</text>
} else if (ViewBag.reason == "LectinningAuth") {
<text>
alert("인증완료");
location.href = "http://" + location.hostname + "/Open/PlayOpen?lectno=@(ViewBag.lectno)&cmino=@(ViewBag.cmino)";
location.href = "http://" + location.hostname + "/Open/PlayOpen?lectno=@(ViewBag.lectno)&cmino=@(ViewBag.cmino)";
</text>
}
} else {

View File

@ -21,7 +21,8 @@
<div>
</div>
<script type="text/javascript">
<script type="text/javascript">
$(document).ready(function () {
if ('@(ViewBag.ret)' == '0') {
if ('@(RSLT_CD)' == 'B000') {
@ -40,8 +41,8 @@
self.close();
</text>
} else if (ViewBag.reason == "MobileNumberChange") {
<text>
if ("@(RSLT_NAME)" == $(opener.document).find("#mform").find("#username").val()) {
<text>
if ("@(RSLT_NAME)" == $(opener.document).find("#mform").find("#username").val()) {
$(opener.document).find("#mform").find("#mobile").val("@(TEL_NO)".replaceAll("-", ""));
$(opener.document).find("#mform").find("#mobilestr").text("@(TEL_NO)".replaceAll("-", ""));
alert("인증완료");
@ -52,25 +53,45 @@
}
self.close();
</text>
} else if (ViewBag.reason == "FINDIDPW") {
<text>
if ("@(RSLT_NAME)" != "" && "@CI" != "") {
try {
$(opener.document).find("#mform").find("#User_di").val("@(DI)");
$(opener.document).find("#mform").find("#User_ci").val("@(CI)");
$(opener.document).find("#mform").find("#User_authplatform").val("@(AuthPlatform.Mobile.GetHashCode())");
$(opener.document).find("#mform").attr("action", "/Account/NewPassword");
$(opener.document).find("#mform").submit();
alert("인증완료");
self.close();
}
catch (ex) {
console.error(ex.message);
}
} else {
alert("입력하신 아이디 또는 비밀번호와 일치하는 계정이 없습니다.\n 다시 확인해주세요.");
}
self.close();
</text>
} else if (ViewBag.reason == "AuthPlatformChange") {
<text>
if ("@(RSLT_NAME)" == $(opener.document).find("#mform").find("#username").val()) {
<text>
if ("@(RSLT_NAME)" == $(opener.document).find("#mform").find("#username").val()) {
$(opener.document).find("#mform").find("#authplatform").val("@(AuthPlatform.Mobile.GetHashCode())");
$(opener.document).find("#mform").find("#authPlatform_IPIN").hide();
$(opener.document).find("#mform").find("#authPlatform_Mobile").show();
$(opener.document).find("#mform").find("#authPlatform_Mobile_Name").text("휴대폰 인증");
$(opener.document).find("#mform").find("#birthday").val("@(RSLT_BIRTHDAY)");
$(opener.document).find("#mform").find("#birthday").val("@(RSLT_BIRTHDAY)");
$(opener.document).find("#mform").find("#di").val("@(DI)");
$(opener.document).find("#mform").find("#ci").val("@(CI)");
$(opener.document).find("#mform").find("#mobile").val("@(TEL_NO)".replaceAll("-", ""));
$(opener.document).find("#mform").find("#mobile").prop("readonly", true);
$(opener.document).find("#mform").find("#mobilestr").text("@(TEL_NO)".replaceAll("-", ""));
try {
try {
$(opener.document).find("#mform").find("#txtbirthday").text(moment("@(RSLT_BIRTHDAY)").format('YYYY.MM.DD'));
} catch (error) {
} catch (error) {
}
alert("인증완료");
} else {
} else {
alert("입력한 정보와 일치하지 않습니다.\n 재인증해주세요.");
}
self.close();

View File

@ -0,0 +1,65 @@
@model NP.Model.VMUser
<div class="container">
<form id="mform" action="" method="post">
@Html.HiddenFor(w => w.User.birthday)
@Html.HiddenFor(w => w.User.username)
@Html.HiddenFor(w => w.User.mobile)
@Html.HiddenFor(w => w.User.vssn)
@Html.HiddenFor(w => w.User.di)
@Html.HiddenFor(w => w.User.ci)
@Html.HiddenFor(w => w.intval2)
@Html.HiddenFor(w => w.User.jointype)
@Html.HiddenFor(w => w.User.authplatform)
</form>
<form id="frm" method="post" action="@ViewBag.result">
<input type="hidden" id="stringval" name="stringval" value="" />
</form>
<div class="jnSel">
<h4 style=" font-size: 20px; font-weight: 400; text-align: left; margin: 0 0 30px;">
인증 수단을 선택하여 주세요.
<span style="display: block;color: red; font-size: 16px;">
※ 로그인 5회 이상 실패로 로그인이 제한된 경우 본인인증 후 비밀번호 재설정을 통하여 해제가 가능합니다.
</span>
</h4>
<div>
<div class="jnsCont">
<img src="../img/common/join_select03-4.png" alt="이메일 인증">
<p>이메일 인증</p>
<a href="/Account/FindMe">인증하기</a>
</div>
</div>
<div>
<div class="jnsCont">
<img src="../img/common/join_select02.png" alt="휴대폰 본인인증">
<p>휴대폰 본인인증</p>
<a href="javascript:void(0);" onclick="certok3reqview('MOBI', 'FINDIDPW');">인증하기</a>
</div>
</div>
<div>
<div class="jnsCont">
<img src="../img/common/join_select01.png" alt="i-PIN 인증">
<p>i-PIN 인증</p>
<a href="javascript:void(0);" onclick="certok3reqview('IPIN', 'FINDIDPW');">인증하기</a>
</div>
</div>
</div>
@Html.Partial("./Partial/OkCert3", null, new ViewDataDictionary { })
</div>
<script>
$(function () {
});
function showAlertPopup() {
$('html, body').addClass('lock');
$("#alertPop").css({
"display": "flex",
"justify-content": "center",
"align-items": "center"
}).hide().fadeIn("fast");
}
</script>

View File

@ -7,12 +7,19 @@
{
<div class="lgnForm" style="max-width: 500px; margin: 0 auto; text-align: center;">
<p class="idpwTxt">
<span><b>@Model.User.username (@Model.User.userid)</b>님, 인증이 완료되었습니다.</span>
<span><b>@Model.User.username </b>님, 아이디는 @Model.User.userid 입니다.</span>
<span>새롭게 사용할 비밀번호를 입력해주세요.</span>
</p>
<ul class="lgnInput">
<li><input type="password" name="User.userpass" id="up1" placeholder="비밀번호(8자 이상, 영문/숫자/특수기호 포함)" /></li>
<li><input type="password" id="up2" placeholder="비밀번호 확인(동일한 비밀번호 입력)" /></li>
<li><input type="password" name="User.userpass" id="up1" placeholder="비밀번호(8자 이상, 영문/숫자/특수기호 포함)" autocomplete="new-password" /></li>
<li><input type="password" id="up2" placeholder="비밀번호 확인(동일한 비밀번호 입력)" autocomplete="new-password" /></li>
</ul>
<ul>
<li class="idpwBtn">
<p id="capslock-warning"></p>
<p id="password-match-warning"></p>
<br />
</li>
</ul>
<ul class="idpwBtn">
<li><a href="javascript:void(0);" onclick="save()">비밀번호 재설정</a></li>
@ -53,7 +60,20 @@
<script>
$(document).ready(function () {
initCapsLockWarning('', '#capslock-warning');
initializePasswordToggle('#up1, #up2');
$('#up1, #up2').on('keyup', function () {
const password = $('#up1').val();
const confirmPassword = $('#up2').val();
const messageElement = $('#password-match-warning');
if (password.length >= 8 && confirmPassword && password !== confirmPassword) {
messageElement.html('<strong style="color: red;">비밀번호가 일치하지 않습니다.</strong>');
} else {
messageElement.html('');
}
});
});
function findme() {

View File

@ -5,6 +5,7 @@
var googleClientId = ViewBag.googleClientID;
}
<head>
<!-- 소셜로그인에 대해서는 추후 사용하도록 둔다 -->
@*<script type="text/javascript" src="/js/naveridlogin_js_sdk_2.0.0.js"></script>
<script type="text/javascript" src="/js/kakao.js"></script>
<script src="https://apis.google.com/js/platform.js?onload=googleInit" async defer></script>*@
@ -17,10 +18,10 @@
<div class="lgnWrap">
<ul class="lgnForm">
<li>
<input type="text" name="uid" id="uid" placeholder="아이디" value="@ViewBag.SavedId" />
<input type="text" name="uid" id="uid" placeholder="아이디" value="@ViewBag.SavedId" autocomplete="username" />
</li>
<li>
<input type="password" name="upw" id="upw" placeholder="비밀번호" />
<input type="password" name="upw" id="upw" placeholder="비밀번호" autocomplete="current-password" />
<button type="button" id="togglePassword" class="toggle-password-btn"></button>
</li>
</ul>
@ -28,14 +29,22 @@
<input type="checkbox" name="issaveid" id="issaveid" @(ViewBag.SavedId == "" ? "" : "checked")>
<label for="issaveid">아이디저장</label>
</div>
<p style="margin-bottom: 30px; border: 1px solid red; padding: 5px;">※ 2021년 7월 1일 이전에 회원가입을 하신 분 들은 본인인증을 위하여 회원가입을 다시 진행 해주시길 부탁드립니다.</p>
@*<div class="notice-box-red">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" class="bi bi-exclamation-circle-fill icon" viewBox="0 0 16 16">
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM8 4a.905.905 0 0 0-.9.995l.35 3.507a.552.552 0 0 0 1.1 0l.35-3.507A.905.905 0 0 0 8 4zm.002 6a1 1 0 1 0 0 2 1 1 0 0 0 0-2z" />
</svg>
<p>2021년 7월 1일 이전에 가입하신 회원님은 본인인증을 위해<br/> 회원가입을 다시 진행해 주시길 바랍니다.</p>
</div>*@
<p id="capslock-warning"></p>
<br/>
<a href="javascript:void(0);" class="lgnBtn" onclick="login();">로그인</a>
<ul class="lgnFind">
<li><a href="/Account/FindMe">아이디/비밀번호찾기</a></li>
<li><a href="/Account/FindIDPW">아이디/비밀번호찾기</a></li>
<li><a href="/Account/JoinIdVeri">회원가입</a></li>
</ul>
<br />
@*<div id="naverIdLogin" style="display:none"></div>*@
@ -44,12 +53,15 @@
<script>
$(document).ready(function () {
$(document).ready(function () {
// Caps Lock
initCapsLockWarning('#upw', '#capslock-warning');
if (opener != null && opener._ismain == 1) {
opener.location.href = "/Account/Index";
self.close();
}
}
else {
if (!ismobile()) {
if (val("uid").length > 0) { focus("upw"); } else { focus("uid"); }
@ -79,21 +91,24 @@
});
}
/** INPUT 패스워드 눈알처리 */
$('.lgnForm').on('click', '#togglePassword', function () {
const passwordInput = $('#upw');
if (passwordInput.length > 0) {
if (passwordInput[0].type === 'password') {
passwordInput[0].type = 'text';
} else {
passwordInput[0].type = 'password';
}
}
$(this).toggleClass('is-toggled');
$('.lgnForm').on('click', '#togglePassword', function () {
const passwordInput = $('#upw');
if (passwordInput.length > 0) {
if (passwordInput[0].type === 'password') {
passwordInput[0].type = 'text';
} else {
passwordInput[0].type = 'password';
}
}
$(this).toggleClass('is-toggled');
});
// 우선 숨겨둔다
/* snsInit(); */
});
@ -158,16 +173,16 @@
++logincnt;
if (logincnt > 4) {
//msg("비밀번호 5회 이상 오류로 로그인이 제한되었습니다.\n교육원으로 문의해주세요.");
msg("로그인 5회 실패로 인하여 로그인이 제한된 계정 입니다.<br/>교육원으로 문의 부탁드립니다.<br/>T : 1544-7660<br/>");
msg("로그인 5회 실패로 인하여 로그인이 제한 되었습니다.<br/>제한을 해제 하시려면 아이디 / 비밀번호 찾기에서<br/>본인인증 후 비밀번호를 재설정 해주세요.");
logincnt = 0;
}
else {
msg("입력하신 아이디/비밀번호와 일치하는 계정이 없습니다. 다시 확인해주세요.<br/>5회이상 비밀번호 오류 시 로그인이 제한됩니다.");
msg("입력하신 아이디 또는 비밀번호와 일치하는 계정이 없습니다.<br/>다시 확인해주세요.<br/><br/><b style='color:blue;'>※ 5회이상 비밀번호 오류 시 로그인이 제한되므로 정확하게 입력해주세요.</b>");
}
}
else if (capResult.msg == "-5") { // 비활성상태
//msg("비밀번호 5회 이상 오류로 로그인이 제한되었습니다.\n교육원으로 문의해주세요.");
msg("로그인 5회 실패로 인하여 로그인이 제한된 계정 입니다.<br/>교육원으로 문의 부탁드립니다.<br/>T : 1544-7660<br/>");
msg("로그인 5회 실패로 인하여 로그인이 제한 되었습니다.<br/>제한을 해제 하시려면 아이디 / 비밀번호 찾기에서<br/>본인인증 후 비밀번호를 재설정 해주세요.");
}
else if (capResult.msg == "-6") { // 탈퇴신청중
msg("회원탈퇴가 접수되었습니다.<br/>탈퇴 완료까지 평일 기준 1일 정도 소요될 예정입니다.");
@ -180,7 +195,9 @@
}
submit();
}
} else { msg("입력하신 아이디/비밀번호와 일치하는 계정이 없습니다. 다시 확인해주세요.<br/>5회이상 비밀번호 오류 시 로그인이 제한됩니다."); }
} else {
msg("입력하신 아이디 또는 비밀번호와 일치하는 계정이 없습니다.<br/>다시 확인해주세요.<br/><br/><b style='color:blue;'>※ 5회이상 비밀번호 오류 시 로그인이 제한되므로 정확하게 입력해주세요.</b>");
}
}
</script>

View File

@ -21,23 +21,16 @@
<div class="jnsCont">
<img src="../img/common/join_select01.png" alt="i-PIN 인증">
<p>i-PIN 인증</p>
<a href="#" onclick="certok3request('IPIN', 'Join');">인증하기</a>
<a href="#" onclick="certok3reqview('IPIN', 'Join');">인증하기</a>
</div>
</div>
<div>
<div class="jnsCont">
<img src="../img/common/join_select02.png" alt="휴대폰 본인인증">
<p>휴대폰 본인인증</p>
<a href="#" onclick="certok3request('MOBI', 'Join');">인증하기</a>
<a href="#" onclick="certok3reqview('MOBI', 'Join');">인증하기</a>
</div>
</div>
@*<div>
<div class="jnsCont">
<img src="../img/common/join_select03.png" alt="카드인증">
<p>카드인증</p>
<a href="#">인증하기</a>
</div>
</div>*@
</div>
@Html.Partial("./Partial/OkCert3", null, new ViewDataDictionary { })
<p class="subDsc mt20">본인 인증 시 제공되는 정보는 해당 인증기관에서 직접 수집하며, 인증 이외의 용도로 이용 또는 저장하지 않습니다.</p>
@ -53,20 +46,7 @@
$("#frm").submit();
}
else {
msg(val.replace("er.",""))
msg(val.replace("er.", ""))
}
}
</script>
@*@{
string errMsg = "";
if (ViewBag.rsltCd != "")
{
errMsg = ((string)ViewBag.rsltCd).Replace("er.", "");
<script>
$(function () {
msg("@errMsg")
})
</script>
}
}*@
</script>

View File

@ -0,0 +1,194 @@
@model NP.Model.VMUser
@{
// 상태 (확장대비)
int userStatus = Model.User.status;
// 인증CI
string ci = Model.User.ci ?? "";
}
@if (!string.IsNullOrEmpty(ci) && !string.IsNullOrEmpty(Model.User.userid))
{ // 인증 결과가 있는 사용자만
<form method="post">
@Html.AntiForgeryToken()
<input type="hidden" id="userid" name="userid" value="@Model.User.userid" />
<div class="lgnForm" style="max-width: 500px; margin: 0 auto; text-align: center;">
<p class="idpwTxt">
<span><b>@Model.User.username </b>님, 아이디는 @Model.User.userid 입니다.</span>
<span>새롭게 사용할 비밀번호를 입력해주세요.</span>
</p>
<ul class="lgnInput" style="margin-top:5px;">
<li><input type="password" id="up1" placeholder="비밀번호(8자 이상, 영문/숫자/특수기호 포함)" autocomplete="new-password" /></li>
<li><input type="password" id="up2" placeholder="비밀번호 확인(동일한 비밀번호 입력)" autocomplete="new-password" /></li>
</ul>
<ul>
<li class="idpwBtn" style="margin-top:5px;">
<p id="capslock-warning"></p>
<p id="password-match-warning"></p>
<br />
</li>
</ul>
<ul class="idpwBtn">
<li><a href="javascript:void(0);" onclick="fnPasswordChange()">비밀번호 재설정</a></li>
<li><a href="/">취소</a></li>
</ul>
</div>
@section scriptsHeader{
@Html.Partial("./Partial/ScriptPost")
}
</form>
<script>
$(function () {
initCapsLockWarning('', '#capslock-warning');
initializePasswordToggle('#up1, #up2');
$('#up1, #up2').on('keyup', function () {
const password = $('#up1').val();
const confirmPassword = $('#up2').val();
const messageElement = $('#password-match-warning');
if (password.length >= 8 && confirmPassword && password !== confirmPassword) {
messageElement.html('<strong style="color: red;">비밀번호가 일치하지 않습니다.</strong>');
} else {
messageElement.html('');
}
});
});
@*/**
* 비밀번호 변경 유효성 검사 및 서버 요청 함수
* if/else if 구조를 사용하여 return 없이 각 유효성 검사를 순차적으로 진행
* 1. 입력 값 공백 여부 확인
* 2. 비밀번호 복잡도 규칙 확인
* 3. 연속된 문자/숫자 반복 확인
* 4. 비밀번호와 비밀번호 확인 값 일치 여부 확인
*/*@
function fnPasswordChange() {
try {
var up1 = $.trim($('#up1').val());
var up2 = $.trim($('#up2').val());
var passwordRegex = /^(?=.*[a-zA-Z])(?=.*\d)(?=.*[\W_]).{8,}$/;
var repeatRegex = /(.)\1\1/;
if (up1 === "" || up2 === "") {
msg('비밀번호를 입력해주세요.');
$('#up1').focus();
} else if (!passwordRegex.test(up1)) {
msg('비밀번호는 8자 이상이며, 영문/숫자/특수기호를 모두 포함해야 합니다.');
$('#up1').focus();
} else if (repeatRegex.test(up1)) {
msg('연속으로 3번 이상 반복되는 문자/숫자를 사용할 수 없습니다.');
$('#up1').focus();
} else if (up1 !== up2) {
msg('비밀번호 확인이 일치하지 않습니다.');
$('#up2').focus();
} else {
// param
const formData = new URLSearchParams();
formData.append('userid', document.getElementById('userid').value);
formData.append('password', up2);
formData.append('__RequestVerificationToken', document.querySelector('input[name="__RequestVerificationToken"]').value);
fetch("/FOCommon/UserChangePassword", {
method: "POST",
body: formData
})
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then(data => {
console.log('서버로부터 받은 전체 응답:', data);
if (data.success) {
confirmCustom(
"비밀번호가 변경 되었습니다.",
[
{
text: '확인',
callback: function () {
window.location.href = "/";
}
}
],
{ top: '30%' }
);
} else {
msg(data.message);
console.log('실패 코드 (from server): ' + data.data);
}
})
.catch(error => {
msg("서버와 통신 중 오류가 발생했습니다. 잠시 후 다시 시도해주세요.");
console.error("통신에러 : ", error);
});
}
} catch (e) {
msg("오류가 발생했습니다");
console.error("AJAX Error: ", e.message);
}
}
</script>
}
else
{ // 인증결과가 없는 회원의 확인창을 띄울 빈 껍데기
<style>
.idpwBtn2 {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
list-style: none;
padding: 0;
margin: 0;
}
</style>
<div class="lgnForm" style="max-width: 500px; margin: 0 auto; text-align: center;">
<p class="idpwTxt">
<span>새롭게 사용할 비밀번호를 입력해주세요.</span>
</p>
<ul class="lgnInput">
<li><input type="password" placeholder="비밀번호(8자 이상, 영문/숫자/특수기호 포함)" readonly="readonly" /></li>
<li><input type="password" placeholder="비밀번호 확인(동일한 비밀번호 입력)" readonly="readonly" /></li>
</ul>
<ul class="idpwBtn2">
<li><a href="/">취소</a></li>
</ul>
</div>
<script>
$(function () {
confirmCustom(
"입력하신 정보에 맞는 회원이 없습니다.<br/>새로 회원가입을 진행해주세요.",
[
{
text: '회원가입 바로가기',
callback: function () {
window.location.href = '/Account/JoinIdVeri';
}
},
{
text: '확인',
callback: function () {
window.location.href = '/Account/FindIDPW';
}
}
],
{ top: '30%' }
);
});
</script>
}

View File

@ -15,9 +15,7 @@
<head>
<script type="text/javascript" src="/js/naveridlogin_js_sdk_2.0.0.js"></script>
<script type="text/javascript" src="/js/kakao.js"></script>
<style type="text/css">
/** 회원탈퇴 관련 css */
<style>
.mpgpop_base {
display: none;
justify-content: center;
@ -83,7 +81,7 @@
text-decoration: none;
font-weight: bold;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
background-color: #2563eb;
background-color: #2563eb;
color: #ffffff !important;
}
@ -105,8 +103,39 @@
line-height: 1.6;
font-size: 0.95rem;
}
</style>
/** 알림 팝업 버튼 스타일 (mpgpop_ 접두사 적용) */
.mpgpop_btn_base {
display: block;
text-align: center;
text-decoration: none;
border: 1px solid #999;
border-radius: 4px;
background-color: #fff;
color: #333;
cursor: pointer;
transition: background-color 0.2s ease;
}
/* 마우스를 올렸을 때의 효과 */
.mpgpop_btn_base:hover {
background-color: #f5f5f5;
}
/* '회원가입 바로가기' 버튼 전용 스타일 */
.mpgpop_btn_signup {
width: 220px;
padding: 12px 0;
font-weight: bold;
}
/* '확인' 버튼 전용 스타일 */
.mpgpop_btn_confirm {
width: 120px;
padding: 10px 0;
}
</style>
</head>
<form id="mform" method="post" enctype="multipart/form-data">
<div id="naverIdLogin" style="display:none"></div>

View File

@ -43,6 +43,10 @@
<div id="bglayer" style="display:none;"></div>
<div id="layermessage"><div></div><a href="#" class="mainokbutton" onclick="javascript:hidelayermsg();">확인</a></div>
<div id="bgprogress">...</div>
<div id="customConfirmBox" style="display: none;">
<div id="customConfirmBox-msg"></div>
<div id="customConfirmBox-buttons"></div>
</div>
<div id="confirmbox"><div id="confirmboxmsg">dddd</div><div class="confirmbtnbox"><a href="#" class="btn btn1 confirmokbutton" onclick="confirmok();" style="color:white !important">예</a><a href="#" class="btn btn0 confirmokbutton" onclick="confirmtoggle();" style="color:white !important">아니오</a></div></div>
<script>
var _ismain = 1;

View File

@ -143,6 +143,62 @@
white-space: nowrap;
color: #fff;
}
/* 팝업 전체 배경 및 위치 */
#customConfirmBox {
position: fixed;
line-height: 20px;
right: 5px;
z-index: 10001;
font-weight: bold;
border-radius: 5px;
display: none;
top: 30%;
left: 20%;
width: 60%;
text-align: center;
height: auto;
bottom: auto;
padding: 30px;
opacity: 0.9;
background-color: #fefefe;
color: #333;
border: 1px solid #ddd;
word-break: break-all;
}
/* 메시지 텍스트 영역 */
#customConfirmBox-msg {
}
/* 버튼들을 감싸는 영역 */
#customConfirmBox-buttons {
}
#customConfirmBox-buttons button {
margin: 0 auto;
display: block;
min-width: 110px;
width: auto;
padding: 0 20px;
height: 30px;
line-height: 30px;
text-align: center;
background: #002C5D;
color: #fff;
font-size: 10pt;
font-weight: 300;
border-radius: 15px;
border: none;
}
#customConfirmBox-buttons button:first-child {
margin-top: 20px;
}
#customConfirmBox-buttons button:not(:first-child) {
margin-top: 5px;
}
.btn0 {background-color: #002c5d; padding: 7px 30px;}
.btn1 {background-color: #002c5d; padding: 7px 40px;}
.btn:hover{background-color: #144d89; color: yellow;background-image: url(/img/repute_tail.png); background-position: 10px center; background-repeat: no-repeat; background-size: 20px;}

View File

@ -129,4 +129,28 @@ input[type="password"]::-ms-reveal {
/* ★ 실제 체크박스가 체크되었을 때 ★ 체크 표시 보여주기 */
.lgnChk input[type="checkbox"]:checked + label::after {
opacity: 1; /* 보이게 처리 */
}
}
/* 알림 상자 기본 스타일 */
.notice-box-red {
display: flex;
align-items: center;
background-color: #f8d7da;
border-left: 5px solid #dc3545;
padding: 15px 20px;
margin-bottom: 30px;
}
/* 알림 상자 안의 아이콘 스타일 */
.notice-box-red .icon {
fill: #dc3545;
min-width: 24px;
margin-right: 15px;
}
/* 알림 상자 안의 p 태그 스타일 */
.notice-box-red p {
margin: 0;
color: #721c24;
line-height: 1.5;
}

View File

@ -6781,7 +6781,7 @@ html.lock, body.lock {
font-size: 14pt;
border: none; /* 테두리 제거 */
border-radius: 8px; /* 둥근 모서리 처리 */
border-radius: 12px; /* 둥근 모서리 처리 */
text-decoration: none; /* a 태그의 밑줄 제거 */
cursor: pointer; /* 마우스 오버 시 손가락 모양 커서 */
font-weight: bold; /* 폰트 약간 두껍게 */
@ -10981,12 +10981,22 @@ span.org {
clear: both;
}
.jnSel > div { /*float: left;*/
width: 33.3333%;
.jnSel > div {
/* float: left; */
width: calc(33.3333% - 3px);
padding: 0 30px;
display: inline-block;
box-sizing: border-box;
}
.jnSel > div:nth-of-type(1) {
padding-left: 0;
}
.jnSel > div:last-child {
padding-right: 0;
}
.jnsCont {
background: #f8f8f8;
border: solid 1px #e1e1e1;

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -1394,6 +1394,9 @@ function isRepeatedString(str) {
}
var _confirmokcb;
var _confirmokcbcancel;
/**
* 확인(Confirm) 상자를 토글(표시/숨김)하고, 사용자의 응답에 따라 특정 콜백 함수를 실행합니다.
* 함수는 일반적으로 사용자에게 확인 메시지를 보여주고 "예" 또는 "아니오" 같은 선택을 요구할 사용됩니다.
@ -1453,8 +1456,6 @@ function confirmtoggle(b, m, cb, cbcancel, top) {
}
}
var _confirmokcb;
var _confirmokcbcancel;
function confirmok() {
$("#confirmbox").hide();
if (_confirmokcb != "" &&
@ -1466,6 +1467,70 @@ function confirmok() {
}
eval(_confirmokcb);
}
/**
* 사용자 정의 버튼을 포함하는 확인(Confirm) 상자를 표시.
* 함수는 다양한 버튼 텍스트와 각각의 콜백 함수를 동적으로 생성하여 사용자에게 선택지를 제공.
*
* @param {string} message - 확인 상자에 표시될 메시지 내용.
* @param {Array<Object>} buttons - 버튼 구성을 위한 객체 배열. 객체는 'text' 'callback' 속성을 포함해야 .
* @param {string} buttons[].text - 버튼에 표시될 텍스트.
* @param {function} buttons[].callback - 버튼 클릭 실행될 콜백 함수.
* @param {Object} [options] - 추가 옵션 객체 (선택 사항).
* @param {string} [options.top] - 확인 상자의 수직('top') 위치를 지정하는 CSS (: "100px", "20%").
*/
/**
* 사용자 정의 버튼을 포함하는 확인(Confirm) 상자를 표시.
* (id가 "customConfirmBox" DOM 요소를 사용하도록 수정됨)
*
* @param {string} message - 확인 상자에 표시될 메시지 내용.
* @param {Array<Object>} buttons - 버튼 구성을 위한 객체 배열.
* @param {Object} [options] - 추가 옵션 객체 (선택 사항).
*/
function confirmCustom(message, buttons, options) {
// 옵션 기본값 설정
options = options || {};
buttons = buttons || [];
const confirmBox = $("#customConfirmBox");
const messageBox = $("#customConfirmBox-msg");
const buttonContainer = $("#customConfirmBox-buttons");
// 1. 메시지 설정
messageBox.html(message);
// 2. 기존 버튼 모두 제거
buttonContainer.empty();
// 3. 버튼 배열을 순회하며 동적으로 버튼 생성
buttons.forEach(function (btnInfo) {
const button = $("<button type='button'></button>");
button.text(btnInfo.text);
button.on("click", function () {
confirmBox.hide();
bglayer(false);
if (typeof btnInfo.callback === 'function') {
btnInfo.callback();
}
});
buttonContainer.append(button);
});
// 4. 위치 옵션 처리
if (options.top) {
confirmBox.css("top", options.top);
} else {
confirmBox.css("top", '');
}
// 5. 확인 상자 표시
bglayer();
confirmBox.show();
}
window.ismobile = function () {
var check = false;
(function (a) { if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4))) check = true; })(navigator.userAgent || navigator.vendor || window.opera);
@ -1535,4 +1600,121 @@ function strEnConvert(str) {
let rslt = str.replace(/&#/gi, "%26%23");
return rslt;
}
/**
* CapsLock 활성화 상태를 감지하여 사용자에게 알려주는 기능을 초기화하는 함수.
* 특정 입력 필드 또는 페이지 전역의 CapsLock 상태를 감지해 지정된 영역에 경고 메시지를 표시함.
*
* @param {string} inputSelectors - CapsLock 상태를 감지할 입력 필드의 CSS 선택자.
* 여러 개일 경우 콤마(,) 구분 (: '#pw, #pw2').
* 문자열('') 전달하면 페이지 전역의 CapsLock 상태를 감지함.
* @param {string} displaySelector - CapsLock 경고 메시지를 표시할 HTML 요소의 CSS 선택자 (: '#caps-lock-area').
* @returns {void}
*/
function initCapsLockWarning(inputSelectors, displaySelector) {
const displayElement = document.querySelector(displaySelector);
if (!displayElement) {
console.error('표시할 영역을 찾을 수 없습니다:', displaySelector);
return;
}
/**
* 키보드 이벤트로부터 CapsLock 상태를 확인하고, 상태에 따라 경고 메시지를 표시하거나 숨기는 내부 함수.
* @param {KeyboardEvent} event - 발생한 키보드 이벤트 객체.
* @returns {void}
*/
const checkCapsLock = (event) => {
const capsLockOn = event.getModifierState && event.getModifierState('CapsLock');
if (capsLockOn) {
displayElement.innerHTML = '<strong style="color: red;">Caps Lock 이 켜져 있습니다.</strong>' + '';
displayElement.style.display = 'block';
} else {
displayElement.innerHTML = '';
displayElement.style.display = 'none';
}
};
if (inputSelectors && inputSelectors.trim() !== '') {
const elements = document.querySelectorAll(inputSelectors);
if (elements.length === 0) {
console.error('입력 필드를 찾을 수 없습니다:', inputSelectors);
return;
}
elements.forEach(element => {
element.addEventListener('keyup', checkCapsLock);
element.addEventListener('keydown', checkCapsLock);
element.addEventListener('blur', () => {
displayElement.style.display = 'none';
});
});
} else {
window.addEventListener('keyup', checkCapsLock);
window.addEventListener('keydown', checkCapsLock);
}
}
/**
* @description 비밀번호 보이기/숨기기 기능을 초기화하는 함수.
* @param {string} targetInputSelectors - 기능을 적용할 input 요소들의 CSS 선택자 (콤마로 구분 가능)
*/
function initializePasswordToggle(targetInputSelectors) {
const passwordInputs = document.querySelectorAll(targetInputSelectors);
if (passwordInputs.length === 0) {
console.error("비밀번호 입력창을 찾을 수 없습니다:", targetInputSelectors);
return;
}
passwordInputs.forEach(passwordInput => {
const originalWidth = passwordInput.getBoundingClientRect().width;
const wrapper = document.createElement('div');
passwordInput.parentNode.insertBefore(wrapper, passwordInput);
wrapper.appendChild(passwordInput);
Object.assign(wrapper.style, {
position: 'relative',
width: `${originalWidth}px`,
});
const eyeOpenSvg = `<svg class="eye-open" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/></svg>`;
const eyeClosedSvg = `<svg class="eye-closed" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
<path d="M12 7c2.76 0 5 2.24 5 5 0 .65-.13 1.26-.36 1.83l2.92 2.92c1.51-1.26 2.7-2.89 3.44-4.75-1.73-4.39-6-7.5-11-7.5-1.4 0-2.74.25-3.98.7l2.16 2.16C10.74 7.13 11.35 7 12 7zM1 12c1.73 4.39 6 7.5 11 7.5 1.55 0 3.03-.3 4.38-.84l-2.9-2.9c-.6.3-1.28.44-2.01.44-2.76 0-5-2.24-5-5 0-.72.15-1.4.44-2.01L7.53 9.8C6.2 10.3 5.06 11.08 4.2 12z"/>
<path d="M12 9c1.66 0 3 1.34 3 3 0 .22-.03.44-.08.65l1.55 1.55c.67-.33 1.41-.53 2.2-.53-2.76 0-5 2.24-5 5 0-.79.2-1.53.53-2.2l-1.55-1.55c.05-.21.08-.43.08-.65 0-1.66-1.34-3-3-3z"/>
<path fill="none" stroke="currentColor" stroke-width="1.5" d="M4.27 3.27L20.73 19.73"/>
</svg>`;
const toggleButton = document.createElement('button');
toggleButton.type = 'button';
toggleButton.innerHTML = eyeOpenSvg + eyeClosedSvg;
wrapper.appendChild(toggleButton);
Object.assign(passwordInput.style, {
paddingRight: '45px',
width: '100%',
boxSizing: 'border-box'
});
Object.assign(toggleButton.style, {
position: 'absolute', top: '50%', right: '15px', transform: 'translateY(-50%)',
background: 'none', border: 'none', cursor: 'pointer',
padding: '0', width: '24px', height: '24px'
});
const eyeClosedIcon = toggleButton.querySelector('.eye-open');
const eyeOpenIcon = toggleButton.querySelector('.eye-closed');
Object.assign(eyeOpenIcon.style, { width: '100%', height: '100%', fill: '#888' });
Object.assign(eyeClosedIcon.style, { width: '100%', height: '100%', fill: '#888', display: 'none' });
toggleButton.addEventListener('click', function () {
const isPassword = passwordInput.type === 'password';
passwordInput.type = isPassword ? 'text' : 'password';
eyeOpenIcon.style.display = isPassword ? 'none' : 'block';
eyeClosedIcon.style.display = isPassword ? 'block' : 'none';
});
});
}