sesiones arreglada
This commit is contained in:
@@ -106,6 +106,7 @@
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
await LimpiarEstadoUsuarioAsync();
|
||||
// Se crea una referencia a este componente para que JS pueda invocar SetToken
|
||||
dotNetRef = DotNetObjectReference.Create(this);
|
||||
await JSRuntime.InvokeVoidAsync("registerTokenReceiver", dotNetRef);
|
||||
@@ -114,16 +115,21 @@
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
LimpiarEstadoUsuario();
|
||||
LimpiarEstadoUsuarioLocal();
|
||||
}
|
||||
|
||||
private void LimpiarEstadoUsuario()
|
||||
private void LimpiarEstadoUsuarioLocal()
|
||||
{
|
||||
UserState.Token = "";
|
||||
UserState.NombreUsu = "";
|
||||
UserState.Clear();
|
||||
HttpContextAccessor?.HttpContext?.Session?.Clear();
|
||||
}
|
||||
|
||||
private async Task LimpiarEstadoUsuarioAsync()
|
||||
{
|
||||
LimpiarEstadoUsuarioLocal();
|
||||
await JSRuntime.InvokeVoidAsync("registroPersonalAuth.clear");
|
||||
}
|
||||
|
||||
public async Task LogIn()
|
||||
{
|
||||
mostrar = true;
|
||||
@@ -168,6 +174,7 @@
|
||||
UserState.Token = parsedJson["token"]?.ToString() ?? "";
|
||||
// Actualizamos el nombre del usuario (formateado como "APELLIDOS, NOMBRE")
|
||||
UserState.NombreUsu = $"{parsedJson["user"]?["apellidos"]?.ToString()}, {parsedJson["user"]?["nombre"]?.ToString()}";
|
||||
await JSRuntime.InvokeVoidAsync("registroPersonalAuth.save", UserState.Token, UserState.NombreUsu);
|
||||
Navigation.NavigateTo("/cumplimientoTrienios", true);
|
||||
}
|
||||
else
|
||||
@@ -178,7 +185,7 @@
|
||||
}
|
||||
|
||||
[JSInvokable]
|
||||
public Task SetToken(string token, string userJson)
|
||||
public async Task SetToken(string token, string userJson)
|
||||
{
|
||||
// Actualizamos el token en UserState
|
||||
UserState.Token = token;
|
||||
@@ -192,13 +199,54 @@
|
||||
{
|
||||
UserState.NombreUsu = "";
|
||||
}
|
||||
await JSRuntime.InvokeVoidAsync("registroPersonalAuth.save", UserState.Token, UserState.NombreUsu);
|
||||
Navigation.NavigateTo("/cumplimientoTrienios", true);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
<!-- Bloque de script inline para definir funciones de autenticación -->
|
||||
<script>
|
||||
window.registroPersonalAuth = {
|
||||
save: async function(token, nombreUsu) {
|
||||
const response = await fetch("/auth/state", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
credentials: "same-origin",
|
||||
body: JSON.stringify({
|
||||
token: token || "",
|
||||
nombreUsu: nombreUsu || ""
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("No se pudo guardar el estado de autenticacion.");
|
||||
}
|
||||
},
|
||||
clear: async function() {
|
||||
await fetch("/auth/state", {
|
||||
method: "DELETE",
|
||||
credentials: "same-origin"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function buildNombreUsuario(user) {
|
||||
if (!user) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const apellidos = user.APELLIDOS || "";
|
||||
const nombre = user.NOMBRE || "";
|
||||
|
||||
if (apellidos && nombre) {
|
||||
return `${apellidos}, ${nombre}`;
|
||||
}
|
||||
|
||||
return apellidos || nombre;
|
||||
}
|
||||
|
||||
function registerTokenReceiver(dotnetRef) {
|
||||
window.dotnetTokenReceiver = dotnetRef;
|
||||
}
|
||||
@@ -220,10 +268,14 @@
|
||||
if (window.dotnetTokenReceiver) {
|
||||
window.dotnetTokenReceiver.invokeMethodAsync("SetToken", data.token, JSON.stringify(data.user));
|
||||
} else {
|
||||
localStorage.setItem("token", data.token);
|
||||
localStorage.setItem("user", JSON.stringify(data.user));
|
||||
window.registroPersonalAuth
|
||||
.save(data.token, buildNombreUsuario(data.user))
|
||||
.then(function() {
|
||||
window.location.href = "/cumplimientoTrienios";
|
||||
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,13 +1,26 @@
|
||||
namespace RegistroPersonalAN.Models
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace RegistroPersonalAN.Models
|
||||
{
|
||||
public class UserState
|
||||
{
|
||||
private readonly object _lock = new object();
|
||||
private string _token;
|
||||
private string _NombreUsu;
|
||||
private string _token = string.Empty;
|
||||
private string _NombreUsu = string.Empty;
|
||||
private bool _Mostrar;
|
||||
private int _currentPage = 1;
|
||||
|
||||
public UserState(IHttpContextAccessor httpContextAccessor, UserStateCookieStore cookieStore)
|
||||
{
|
||||
var snapshot = cookieStore.Read(httpContextAccessor.HttpContext);
|
||||
if (snapshot is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_token = snapshot.Token;
|
||||
_NombreUsu = snapshot.NombreUsu ?? string.Empty;
|
||||
}
|
||||
|
||||
public string Token
|
||||
{
|
||||
@@ -22,10 +35,11 @@
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_token = value;
|
||||
_token = value ?? string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string NombreUsu
|
||||
{
|
||||
get
|
||||
@@ -39,10 +53,11 @@
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_NombreUsu = value;
|
||||
_NombreUsu = value ?? string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool Mostrar
|
||||
{
|
||||
get
|
||||
@@ -63,8 +78,42 @@
|
||||
|
||||
public int CurrentPage
|
||||
{
|
||||
get { lock (_lock) { return _currentPage; } }
|
||||
set { lock (_lock) { _currentPage = value; } }
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _currentPage;
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_currentPage = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsAuthenticated
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return !string.IsNullOrWhiteSpace(_token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_token = string.Empty;
|
||||
_NombreUsu = string.Empty;
|
||||
_Mostrar = false;
|
||||
_currentPage = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Microsoft.AspNetCore.WebUtilities;
|
||||
|
||||
namespace RegistroPersonalAN.Models
|
||||
{
|
||||
public sealed class UserStateCookieStore
|
||||
{
|
||||
private const string CookieName = "RegistroPersonalAN.AuthState";
|
||||
private readonly IDataProtector _protector;
|
||||
|
||||
public UserStateCookieStore(IDataProtectionProvider dataProtectionProvider)
|
||||
{
|
||||
_protector = dataProtectionProvider.CreateProtector("RegistroPersonalAN.UserStateCookieStore.v1");
|
||||
}
|
||||
|
||||
public UserStateCookiePayload? Read(HttpContext? httpContext)
|
||||
{
|
||||
if (httpContext?.Request.Cookies.TryGetValue(CookieName, out var cookieValue) != true ||
|
||||
string.IsNullOrWhiteSpace(cookieValue))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var protectedPayload = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(cookieValue));
|
||||
var json = _protector.Unprotect(protectedPayload);
|
||||
var payload = JsonSerializer.Deserialize<UserStateCookiePayload>(json);
|
||||
|
||||
if (payload is null || string.IsNullOrWhiteSpace(payload.Token))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return payload;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(HttpContext httpContext, UserStateCookiePayload payload)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(payload.Token))
|
||||
{
|
||||
Clear(httpContext);
|
||||
return;
|
||||
}
|
||||
|
||||
var json = JsonSerializer.Serialize(payload);
|
||||
var protectedPayload = _protector.Protect(json);
|
||||
var cookieValue = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(protectedPayload));
|
||||
|
||||
httpContext.Response.Cookies.Append(CookieName, cookieValue, BuildCookieOptions(httpContext));
|
||||
}
|
||||
|
||||
public void Clear(HttpContext httpContext)
|
||||
{
|
||||
httpContext.Response.Cookies.Delete(CookieName, BuildCookieOptions(httpContext));
|
||||
}
|
||||
|
||||
private static CookieOptions BuildCookieOptions(HttpContext httpContext)
|
||||
{
|
||||
return new CookieOptions
|
||||
{
|
||||
HttpOnly = true,
|
||||
IsEssential = true,
|
||||
Path = "/",
|
||||
SameSite = SameSiteMode.Lax,
|
||||
Secure = httpContext.Request.IsHttps
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class UserStateCookiePayload
|
||||
{
|
||||
public string Token { get; set; } = string.Empty;
|
||||
public string NombreUsu { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,10 @@
|
||||
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using RegistroPersonalAN.Components;
|
||||
using RegistroPersonalAN.Models;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
|
||||
// Configurar servicios
|
||||
builder.Services.AddRazorComponents()
|
||||
.AddInteractiveServerComponents();
|
||||
@@ -24,7 +22,7 @@ builder.Services.AddAuthentication(options =>
|
||||
options.LoginPath = "/home";
|
||||
options.AccessDeniedPath = "/AccessDenied";
|
||||
});
|
||||
// Necesario para ver porqu<EFBFBD> est<EFBFBD> fallando ciertas cosas que dan el error Circuit
|
||||
// Necesario para ver porqué está fallando ciertas cosas que dan el error Circuit
|
||||
builder.Services.AddServerSideBlazor().AddCircuitOptions(option => { option.DetailedErrors = true; });
|
||||
builder.Services.AddHttpContextAccessor();
|
||||
builder.Services.AddDistributedMemoryCache();
|
||||
@@ -39,17 +37,14 @@ builder.Services.AddHttpClient("CertClient").ConfigurePrimaryHttpMessageHandler(
|
||||
{
|
||||
return new HttpClientHandler
|
||||
{
|
||||
ClientCertificateOptions = ClientCertificateOption.Manual // Forzar la selecci<EFBFBD>n del certificado
|
||||
ClientCertificateOptions = ClientCertificateOption.Manual // Forzar la selección del certificado
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
builder.Services.AddBlazorBootstrap();
|
||||
builder.Services.AddAntiforgery();
|
||||
builder.Services.AddSingleton<UserState>();
|
||||
builder.Services.AddSingleton<UserStateCookieStore>();
|
||||
builder.Services.AddScoped<UserState>();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
@@ -67,9 +62,20 @@ app.UseRouting();
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
|
||||
app.UseAntiforgery();
|
||||
|
||||
app.MapPost("/auth/state", (HttpContext context, UserStateCookiePayload payload, UserStateCookieStore cookieStore) =>
|
||||
{
|
||||
cookieStore.Write(context, payload);
|
||||
return Results.Ok();
|
||||
}).DisableAntiforgery();
|
||||
|
||||
app.MapDelete("/auth/state", (HttpContext context, UserStateCookieStore cookieStore) =>
|
||||
{
|
||||
cookieStore.Clear(context);
|
||||
return Results.NoContent();
|
||||
}).DisableAntiforgery();
|
||||
|
||||
app.Use(async (context, next) =>
|
||||
{
|
||||
var userState = context.RequestServices.GetService<UserState>();
|
||||
@@ -77,6 +83,7 @@ app.Use(async (context, next) =>
|
||||
|
||||
// Permitir solicitudes internas y recursos necesarios
|
||||
if (path == "/" ||
|
||||
path.StartsWithSegments("/auth/state") ||
|
||||
path.StartsWithSegments("/_blazor") ||
|
||||
path.StartsWithSegments("/Content") ||
|
||||
path.StartsWithSegments("/Scripts") ||
|
||||
@@ -88,8 +95,8 @@ app.Use(async (context, next) =>
|
||||
return;
|
||||
}
|
||||
|
||||
// Redirigir al home si no hay token y la ruta no es p<EFBFBD>blica
|
||||
if (userState?.Token == null)
|
||||
// Redirigir al home si no hay token y la ruta no es pública
|
||||
if (string.IsNullOrWhiteSpace(userState?.Token))
|
||||
{
|
||||
Console.WriteLine($"Redirigiendo al home desde: {path}");
|
||||
context.Response.Redirect("/");
|
||||
@@ -100,8 +107,6 @@ app.Use(async (context, next) =>
|
||||
await next();
|
||||
});
|
||||
|
||||
|
||||
|
||||
app.MapRazorComponents<App>()
|
||||
.AddInteractiveServerRenderMode();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user