httpAccessor.HttpContext.SignInAsync用户为空



上下文

我有一个分为两个的web应用程序;公用事业";。

  1. Web API
  2. 剃刀页

因此,我的应用程序既是API应用程序,也是ASP.NET Razor Pages应用程序,在Web API端进行身份验证时,我使用JWT Bearer,在Web应用程序端使用简单Cookie。

问题

在使用Cookie身份验证时,我遵循了Microsoft在没有ASP.NET核心标识的情况下使用Cookie身份验证,但它绝对不起作用。

我使用带有SignInAsync方法的自定义AuthManager。cookie确实已创建,但我的HttpContext中的ClaimsPrincipal为空

我在互联网上根本没有找到任何解决方案,唯一可行的解决方案是使用自定义中间件,但我甚至不知道从哪里开始。

如果有人遇到和我一样的问题。

感谢


public async Task<bool> LogInAsync(string email, string password, bool rememberMe)
{
UserModel user = _userService.UserLogin(email, password).MapFromBLL();
if (user is null) return false;
user.Roles = _roleService.GetUserRoles(user.Id).Select(r => r.MapFromBLL());
if (user.Roles is null || user.Roles.Count() == 0) return false;
List<Claim> claims = new List<Claim>()
{
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.Name, user.Email),
new Claim("Stamp", user.SecurityStamp.ToString())
};
IEnumerable<Claim> roleClaims = user.Roles.Select(ur => new Claim(ClaimTypes.Role, ur.Name));
claims.AddRange(roleClaims);
ClaimsIdentity claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
AuthenticationProperties authProperties = new AuthenticationProperties
{
AllowRefresh = true,
ExpiresUtc = DateTime.Now.AddDays(_jwtModel.ExpirationInDays),
IsPersistent = true,
IssuedUtc = DateTime.Now,
};
ClaimsPrincipal claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
try
{
await _httpAccessor.HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, claimsPrincipal, authProperties);
return true;
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
return false;
}
}

启动.cs

services.AddAuthentication(options =>
{
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.SlidingExpiration = true;
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.None;
options.Cookie.IsEssential = true;
options.Cookie.SameSite = SameSiteMode.Strict;
options.LoginPath = "/Account/Login";
options.AccessDeniedPath = "/Forbidden";
options.EventsType = typeof(SecurityStampUpdatedCookieAuthenticationEvent);
})
.AddJwtBearer(options =>
{
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidIssuer = jwtModel.Issuer,
ValidAudience = jwtModel.Audience,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtModel.Secret)),
ClockSkew = TimeSpan.Zero
};
});

2022年2月10日编辑

我把这篇文章分成两章:;上下文";以及";问题";

我的解决方案

好的,所以我找到了解决问题的办法。和往常一样,经过10个小时的研究,它是愚蠢的。在我的Startup.cs中,我使用JwtBearerDefaults.AuthenticationScheme作为DefaultAuthenticateScheme。所以我用CookieAuthenticationDefaults.AuthenticationScheme更改了它,现在奇迹般地它工作了。登录后,我的ClaimsPrincipal用户已满,它可以正确检索cookie。

Startup.cs中的我的新services.AddAuthentication()

services.AddAuthentication(options =>
{
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})

我真的不知道我是否应该保留options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;。如果有人能照亮我。

我没有改变我的方法public async Task<bool> SignInAsync()中的任何内容,我只是在一开始添加了await _httpAccessor.HttpContext.SignOutAsync(),只是为了确定。

public async Task<bool> LogInAsync(string email, string password, bool rememberMe)
{
await LogOutAsync();
// The same code as before
}

LogOutAsync()方法

public async Task LogOutAsync()
{
try
{
await _httpAccessor.HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
}
}

遇到的问题

授权

在解决了这个问题之后,我又遇到了一个问题。现在我的默认方案是基于Cookie的,我不能只使用[Authorize],除非它同时使用Cookie或Jwt。为了解决这个问题,我刚刚在services.AddAuthentication()之后的Startup.cs中添加了此代码

AuthorizationPolicy multiSchemePolicy = new AuthorizationPolicyBuilder(CookieAuthenticationDefaults.AuthenticationScheme, JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser()
.Build();
services.AddAuthorization(options =>
{
options.DefaultPolicy = multiSchemePolicy;
});

我在这里找到的代码

API未授权返回登录页面,而不是401或403状态代码

这有点奇怪,现在cookie和JwtBearer工作得很好,当我试图访问[Authorize]路由时,有时它会将HTML页面返回到Login,而不是401状态代码,有时不会。

为了避免这个问题,我在这里找到了一个解决方案

使用services.ConfigureApplicationCookie对我不起作用,相反,我调整了services.AddCookie()

.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.LoginPath = "/Account/Login";
options.AccessDeniedPath = "/Forbidden";
options.Events = new CookieAuthenticationEvents()
{
OnRedirectToLogin = (ctx) =>
{
if (ctx.Request.Path.StartsWithSegments("/api") && ctx.Response.StatusCode == 200)
{
ctx.Response.StatusCode = 401;
}
return Task.CompletedTask;
},
OnRedirectToAccessDenied = (ctx) =>
{
if (ctx.Request.Path.StartsWithSegments("/api") && ctx.Response.StatusCode == 200)
{
ctx.Response.StatusCode = 403;
}
return Task.CompletedTask;
}
};
options.EventsType = typeof(SecurityStampUpdatedCookieAuthenticationEvent);
});

瞧,我希望我至少帮助了你们中的一个人。如果有什么需要改变的,请告诉我!

完整代码

启动.cs

services.AddAuthentication(options =>
{
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidIssuer = jwtModel.Issuer,
ValidAudience = jwtModel.Audience,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtModel.Secret)),
ClockSkew = TimeSpan.Zero
};
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.LoginPath = "/Account/Login";
options.AccessDeniedPath = "/Forbidden";
options.Events = new CookieAuthenticationEvents()
{
OnRedirectToLogin = (ctx) =>
{
if (ctx.Request.Path.StartsWithSegments("/api") && ctx.Response.StatusCode == 200)
{
ctx.Response.StatusCode = 401;
}
return Task.CompletedTask;
},
OnRedirectToAccessDenied = (ctx) =>
{
if (ctx.Request.Path.StartsWithSegments("/api") && ctx.Response.StatusCode == 200)
{
ctx.Response.StatusCode = 403;
}
return Task.CompletedTask;
}
};
options.EventsType = typeof(SecurityStampUpdatedCookieAuthenticationEvent);
});
AuthorizationPolicy multiSchemePolicy = new AuthorizationPolicyBuilder(CookieAuthenticationDefaults.AuthenticationScheme, JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser()
.Build();
services.AddAuthorization(options =>
{
options.DefaultPolicy = multiSchemePolicy;
});

AuthManager.cs

public async Task<bool> LogInAsync(string email, string password, bool rememberMe)
{
await LogOutAsync();
UserModel user = _userService.UserLogin(email, password).MapFromBLL();
if (user is null) return false;
user.Roles = _roleService.GetUserRoles(user.Id).Select(r => r.MapFromBLL());
if (user.Roles is null || user.Roles.Count() == 0) return false;
List<Claim> claims = new List<Claim>()
{
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.Name, user.Email),
new Claim("Stamp", user.SecurityStamp.ToString())
};
IEnumerable<Claim> roleClaims = user.Roles.Select(ur => new Claim(ClaimTypes.Role, ur.Name));
claims.AddRange(roleClaims);
ClaimsIdentity claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
AuthenticationProperties authProperties = new AuthenticationProperties
{
AllowRefresh = true,
ExpiresUtc = DateTime.Now.AddDays(_jwtModel.ExpirationInDays),
IsPersistent = true,
IssuedUtc = DateTime.Now,
};
ClaimsPrincipal claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
try
{
await _httpAccessor.HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, claimsPrincipal, authProperties);
return true;
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
return false;
}
}
public async Task LogOutAsync()
{
try
{
await _httpAccessor.HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
}
}

最新更新