클라이언트 측에서는 JavaScript의 setInterval 함수를 사용하여 주기적으로 서버에 세션 상태를 확인하는 요청을 보내고, 서버 측에서는 이 요청을 받아 세션 상태를 확인한 후 필요한 작업을 수행하는 코드를 구현할 수 있습니다.
이 방식을 사용하면 클라이언트에서는 10초마다 서버에 세션 상태를 확인하는 요청을 보내고, 서버에서는 이 요청을 받아 세션 상태를 확인한 후 클라이언트에게 응답을 보냅니다. 클라이언트에서는 서버의 응답을 받아 필요하다면 경고창을 띄우거나 다른 페이지로 이동하게 됩니다. 이 예시에서는 'dbContext'가 데이터베이스 컨텍스트를, 'SignInManager'와 'UserManager'가 각각 ASP.NET Identity의 SignInManager와 UserManager를 나타내는 것으로 가정하였습니다.
또한, 'SessionInfo'는 사용자의 세션 정보를 나타내는 클래스이며, 'UserId', 'SessionId', 'LastAccessTime' 등의 속성을 가지고 있습니다. 이 클래스는 실제 구현에 따라 필요한 속성을 추가하거나 변경할 수 있습니다.
다음은 이를 구현한 예시입니다:
클라이언트 측
일반적인 HTTP 요청-응답 방식의 웹 애플리케이션에서는 클라이언트가 서버에 요청을 보내지 않는 한 서버에서 클라이언트로 데이터를 푸시할 수 없습니다.
따라서, 클라이언트 측에서는 setInterval 함수를 사용하여 10초마다 서버에 세션 상태를 확인하는 요청을 보냅니다.
$(document).ready(function() {
setInterval(function() {
$.ajax({
url: '/Account/CheckSession',
type: 'GET',
success: function(data) {
if (data.redirect) {
alert('다른 디바이스에서의 로그인이 감지되었습니다. 현재 디바이스에서만 로그인 상태가 유지됩니다.');
window.location.href = data.redirectUrl;
}
}
});
}, 10000); // 10초마다 실행
});
서버 측 세션 상태 확인
서버 측에서는 클라이언트의 요청을 받아 세션 상태를 확인합니다.
public class AccountController : Controller
{
[HttpGet]
public ActionResult CheckSession()
{
var currentSessionId = System.Web.HttpContext.Current.Session.SessionID;
var userId = User.Identity.GetUserId();
//database 에서 인증자 정보로 데이터 가져온다.
var sessionInfos = dbContext.SessionInfos.Where(x => x.UserId == userId).ToList();
//현재 세션값으로 인증자의 database SessionId 을 조회한다.
var currentSessionInfo = sessionInfos.FirstOrDefault(x => x.SessionId == currentSessionId);
// 현재 세션이 유효하지 않을 경우
if (currentSessionInfo == null)
{
return Json(new { redirect = true, redirectUrl = Url.Action("Login", "Account") }, JsonRequestBehavior.AllowGet);
}
// 현재 세션이 유효할 경우
return Json(new { redirect = false }, JsonRequestBehavior.AllowGet);
}
}
인증 후 세션 정보 db 저장
사용자가 로그인하면, 세션 정보를 DB에 저장합니다.
[HttpPost]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
switch (result)
{
case SignInStatus.Success:
// 아이디로 사용자 정보를 db 에서 가져온다.
var user = await UserManager.FindByNameAsync(model.Email);
//인증자 세션값을 SessionInfo 의 SessionId 에 담아 database 에 저장한다.
var sessionInfo = new SessionInfo
{
UserId = user.Id,
SessionId = System.Web.HttpContext.Current.Session.SessionID,
LastAccessTime = DateTime.UtcNow
};
//database 에 세션값을 저장한다. 저장한다.
dbContext.SessionInfos.Add(sessionInfo);
await dbContext.SaveChangesAsync();
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
}
로그아웃 후 세션 db 삭제
사용자가 로그아웃하면, 세션 정보를 DB에서 삭제합니다.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LogOff()
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
// 세션 정보를 DB에서 삭제
var userId = User.Identity.GetUserId();
var sessionInfo = dbContext.SessionInfos.FirstOrDefault(x => x.UserId == userId);
if (sessionInfo != null)
{
dbContext.SessionInfos.Remove(sessionInfo);
dbContext.SaveChanges();
}
return RedirectToAction("Index", "Home");
}