I'm needing help with a project I've been breaking my head for two days. I am using Microsoft C # MVC5 technology and framework 4.5, I also use Entity Framework 6 with Repository Pattern, Unit of Work, and Unity to perform the dependency injection.
I have a controller called AccountController
that is responsible for logging in and logging out the user on the system, this controller receives the application's repository methods through dependency injection through the builder.
AccountController
public class AccountController : BaseController
{
private readonly IUsuarioApp _usuarioApp;
private readonly IUnitOfWorkAsync _unitOfWorkAsync;
public AccountController() { }
public AccountController(IUsuarioApp usuarioApp, IUnitOfWorkAsync unitOfWorkAsync)
{
_unitOfWorkAsync = unitOfWorkAsync;
_usuarioApp = usuarioApp;
}
// GET: Login
[AllowAnonymous]
public ActionResult Login()
{
return View();
}
//
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login([Bind(Include = "Email, Password")]LoginViewModel model, string returnUrl)
{
try
{
if (!ModelState.IsValid) return View(model);
var usuarioAutenticado = _usuarioApp.AutenticarUsuarioPor(model.Email, model.Password);
var logDeAcesso = new LogDeAcesso { DataDeAcesso = DateTime.Now, UsuarioId = usuarioAutenticado.Id };
usuarioAutenticado.DataDoUltimoAcesso = logDeAcesso.DataDeAcesso;
_usuarioApp.Update(usuarioAutenticado);
_usuarioApp.GetRepository<LogDeAcesso>().Insert(logDeAcesso);
_unitOfWorkAsync.SaveChanges();
SessionContext.SetAuthenticationToken(usuarioAutenticado.Id.ToString(), false, ConvertToUsuarioViewModel(usuarioAutenticado));
return RedirectToAction("Index", "Home");
}
catch (Exception ex)
{
ModelState.AddModelError("", "Tentativa de login inválido.");
return View(model);
}
}
public ActionResult LogOff(int id)
{
try
{
var ultimoLogsDeAcessoCriado = _usuarioApp.GetRepository<LogDeAcesso>().Query(model => model.UsuarioId == id).OrderBy(model => model.OrderByDescending(c => c.DataDeAcesso)).Select().FirstOrDefault();
if (ultimoLogsDeAcessoCriado == null || ultimoLogsDeAcessoCriado.DataDeSaida != DateTime.MinValue) throw new Exception("Erro ao tentar deslogar do sistema.");
ultimoLogsDeAcessoCriado.DataDeSaida = DateTime.Now;
_usuarioApp.GetRepository<LogDeAcesso>().Update(ultimoLogsDeAcessoCriado);
_unitOfWorkAsync.SaveChanges();
FormsAuthentication.SignOut();
Session.Clear(); //Pode não ser necessário, mas não é problemático o uso como prevenção
Session.Abandon();
//Limpar o cookie de Autenticação
var resetFormsCookie = new HttpCookie(FormsAuthentication.FormsCookieName, "");
resetFormsCookie.Expires = DateTime.Now.AddYears(-1);
Response.Cookies.Add(resetFormsCookie);
//Limpar a session cookie
var resetSessionCookie = new HttpCookie("ASP.NET_SessionId", "");
resetSessionCookie.Expires = DateTime.Now.AddYears(-1);
Response.Cookies.Add(resetSessionCookie);
//Invalida o Cache no lado do Cliente
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetNoStore();
}
catch (Exception ex)
{
Danger("Error: " + ex.Message);
}
return RedirectToAction("Login", "Account");
}
#region Helpers
private UsuarioViewModel ConvertToUsuarioViewModel(Usuario usuario)
{
return new UsuarioViewModel
{
Id = usuario.Id,
Nome = usuario.Nome,
UltimoAcesso = usuario.DataDoUltimoAcesso
};
}
#endregion
}
As it is seen, when logging in the user is authenticated through the email and the password, an instance of the Access Log is created where we register the access date of the access and later the cookie is created where the cookie will be stored, which will allow the access the pages of the Site.
After obtaining the access, the user can click on the logoff button, in which it will trigger the ActionResult LogOff
method that will get the last access log created based on the user id, update with the data of the System exit date, will clear the Session and Cookies by redirecting it to the Login page. From there he will only have access to the other pages if he re-logs in.
It dynamically works fine, but does it have a problem, and if the user does not click the logoff button, and instead, close the tab or browser? As it was built, it will remain authenticated in the 20-minute IIS defaults to cookie expiration time, not to mention that this way I will not be able to register the Exit Date in the Access Log. p>
Thinking about this, I configured a session timeout in Web.config
Web.Config
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
<globalization culture="pt-BR" uiCulture="pt-BR" />
<authentication mode="Forms">
<forms loginUrl="~/Account/Login" timeout="2880" slidingExpiration="true" />
</authentication>
<sessionState
mode="InProc"
stateConnectionString="tcpip=127.0.0.1:42424"
stateNetworkTimeout="20"
sqlConnectionString="data source=127.0.0.1;Integrated Security=SSPI"
sqlCommandTimeout="20"
customProvider=""
cookieless="UseCookies"
cookieName="ASP.NET_SessionId"
timeout="1"
allowCustomSqlDatabase="false"
regenerateExpiredSessionId="true"
partitionResolverType=""
useHostingIdentity="true">
<providers>
<clear />
</providers>
</sessionState>
<machineKey validationKey="466AFE06F664B2E3662F97B81D30E87907F9921E51C95C618A670B396403AD98DD032BCE7610EEAE1FB1DA7B3ED7ACE56537E66FD6DF20E701351697E57C3D9C" decryptionKey="CD10ABC11246E6998AB7B9A8CC142CDD6C8AEF7FB12D15CF12158BEAD647C603" validation="SHA1" decryption="AES" />
</system.web>
</configuration>
Only when timeout happens and I enter Global.asax
in method Session_End()
, I can not access repositories and FormsAuthentication
.
I'd like to know what I have to do to repeat within Session_End()
exactly what I do when I click the logoff button. Could someone help?
EDIT
Well, I think I can better elucidate what I'm looking for. I am trying to develop a way to make a database in the database and destroy the authentication ticket, Session and Cookie at the time the TimeOut of downtime occurs.