재우니의 블로그

 

 

클라이언트 측에서는 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");
}