gpt4 book ai didi

C# Entity Framework 核心端点 JWT 身份验证

转载 作者:太空宇宙 更新时间:2023-11-03 15:04:07 25 4
gpt4 key购买 nike

我正在使用 Entity Framework Core 使用 ASP.NET Core 编写的 Web API。

目前我正面临一个让我无法休眠的问题:)

我使用一个名为 TokenProviderMiddleWare 的类来保护我的端点(在本教程页面之后:https://stormpath.com/blog/token-authentication-asp-net-core)

这是我从数据库中检索用户并检查提供的密码是否与数据库匹配的函数:

        private Task<ClaimsIdentity> GetUserIdentity(string email, string password)
{
var driver = context.Drivers.SingleOrDefault(d => d.Email == email);

if (driver == null)
return Task.FromResult<ClaimsIdentity>(null);

if (driver.Password != password)
{
SetBadLoginAttempt(driver);
context.SaveChangesAsync();
return Task.FromResult<ClaimsIdentity>(null);
}

if (driver.IsLoginDisabled)
{
return Task.FromResult<ClaimsIdentity>(null);
}

ResetBadLoginAttempt(driver);
context.SaveChangesAsync();

return Task.FromResult(new ClaimsIdentity(
new System.Security.Principal.GenericIdentity(email, "Token"),
new Claim[] {
new Claim("fullName", driver.Name),
}
));
}`

如果我同时运行两次登录,我会收到此错误:

Connection id "0HL5KO6M27JFT": An unhandled exception was thrown by the application.
System.InvalidOperationException: An attempt was made to use the context
while it is being configured. A DbContext instance cannot be used inside OnConfiguring since it is still being configured at this
point.

通过在此函数的第一行执行此操作可以解决此问题:

Driver driver = null;
lock(context)
{
driver = context.Drivers.SingleOrDefault(d => d.Email == email);
}

但我认为这很丑陋且不可扩展。

简而言之,我想通过 EntityFramework 在数据库中检查我的用户。我的 DbContext 由 .NET Core 通过构造函数注入(inject)。而且我认为存在某种并发问题......

我在其中使用此代码的类如下所示:

using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using SmartoonAPI.Persistence;
using System.Linq;
using System.Collections.Generic;
using SmartoonDomain.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;

namespace SmartoonAPI.JWT
{
public class TokenProviderMiddleware
{
private readonly RequestDelegate next;
private readonly TokenProviderOptions options;
private readonly ISmartoonContext context;

public TokenProviderMiddleware(RequestDelegate next, IOptions<TokenProviderOptions> options, ISmartoonContext context)
{
this.context = context;
this.next = next;
this.options = options.Value;
}

public Task Invoke(HttpContext context)
{
// If the request path doesn't match, skip
if (!context.Request.Path.Equals(options.Path, StringComparison.Ordinal))
{
return next(context);
}

// Request must be POST with Content-Type: application/x-www-form-urlencoded
if (!context.Request.Method.Equals("POST")
|| !context.Request.HasFormContentType)
{
context.Response.StatusCode = 400;
return context.Response.WriteAsync("Bad request.");
}

return GenerateToken(context);
}

private async Task GenerateToken(HttpContext context)
{
ClaimsIdentity identity;
if (!string.IsNullOrEmpty(context.Request.Form["email"]) && !string.IsNullOrEmpty(context.Request.Form["password"]))
identity = await GetUserIdentity(context.Request.Form["email"], context.Request.Form["password"]);
else
identity = await GetApplicationIdentity(context.Request.Form["appid"], context.Request.Form["secret"]);

if (identity == null)
{
context.Response.StatusCode = 400;
await context.Response.WriteAsync("Login failed!");
return;
}

var now = DateTime.UtcNow;

// Specifically add the jti (random nonce), iat (issued timestamp), and sub (subject/user) claims.
// You can add other claims here, if you want:
var claims = new List<Claim>()
{
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.Iat, now.ToUniversalTime().ToString(), ClaimValueTypes.Integer64)
};

claims.AddRange(identity.Claims);

// Create the JWT and write it to a string
var jwt = new JwtSecurityToken(
issuer: options.Issuer,
audience: options.Audience,
claims: claims,
notBefore: now,
expires: now.Add(options.Expiration),
signingCredentials: options.SigningCredentials);
var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);

var response = new
{
access_token = encodedJwt,
expires_in = (int)options.Expiration.TotalSeconds
};

// Serialize and return the response
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(JsonConvert.SerializeObject(response, new JsonSerializerSettings { Formatting = Formatting.Indented }));
}

private LoginAble SetBadLoginAttempt(LoginAble loginAble)
{
if (loginAble.LoginDisabledOn.HasValue && (DateTime.Now - loginAble.LoginDisabledOn.Value).TotalMinutes > 30)
{
ResetBadLoginAttempt(loginAble);
loginAble.BadLoginAttempt++;
return loginAble;
}

loginAble.BadLoginAttempt++;

if (loginAble.BadLoginAttempt < 3)
{
return loginAble;
}
else
{
loginAble.IsLoginDisabled = true;
loginAble.LoginDisabledOn = DateTime.Now;
}
return loginAble;
}

private LoginAble ResetBadLoginAttempt(LoginAble loginAble)
{
loginAble.BadLoginAttempt = 0;
loginAble.IsLoginDisabled = false;
loginAble.LoginDisabledOn = null;
return loginAble;
}

private Task<ClaimsIdentity> GetUserIdentity(string email, string password)
{
var driver = context.Drivers.SingleOrDefault(d => d.Email == email);

if (driver == null)
return Task.FromResult<ClaimsIdentity>(null);

if (driver.Password != password)
{
SetBadLoginAttempt(driver);
context.SaveChangesAsync();
return Task.FromResult<ClaimsIdentity>(null);
}

if (driver.IsLoginDisabled)
{
return Task.FromResult<ClaimsIdentity>(null);
}

ResetBadLoginAttempt(driver);
context.SaveChangesAsync();

return Task.FromResult(new ClaimsIdentity(
new System.Security.Principal.GenericIdentity(email, "Token"),
new Claim[] {
new Claim("fullName", driver.Name),
}
));
}

private Task<ClaimsIdentity> GetApplicationIdentity(string appId, string secret)
{
var appCredential = context.AppCredentials.SingleOrDefault(a => a.AppId == appId);

if (appCredential == null)
return Task.FromResult<ClaimsIdentity>(null);

if (appCredential.Secret != secret)
{
SetBadLoginAttempt(appCredential);
context.SaveChangesAsync();
return Task.FromResult<ClaimsIdentity>(null);
}

ResetBadLoginAttempt(appCredential);
context.SaveChangesAsync();

return Task.FromResult(new ClaimsIdentity(
new System.Security.Principal.GenericIdentity(appCredential.AppId, "Token"),
new Claim[] {
new Claim("description", appCredential.Description),
}
));
}
}
}

最佳答案

ASP.NET Core 中间件在每个应用程序中实例化一次,而数据库上下文默认在每次请求时实例化并在请求完成时被释放。将数据库上下文直接注入(inject) Invoke 方法应该可以解决问题。

关于C# Entity Framework 核心端点 JWT 身份验证,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44591752/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com