ASP.. NET核心Web API认证
本文关键字:API 认证 Web 核心 NET ASP | 更新日期: 2023-09-27 18:02:32
我正在努力如何在我的web服务中设置身份验证。该服务是使用ASP构建的。. NET Core web api.
我的所有客户端(WPF应用程序)应该使用相同的凭据来调用web服务操作。
经过一番研究,我想出了基本的身份验证——在HTTP请求的头部发送用户名和密码。但是经过几个小时的研究,在我看来,基本身份验证并不是ASP的发展方向。网络核心。
我发现的大多数资源都是使用OAuth或其他中间件实现身份验证的。但是对于我的场景,以及使用ASP的身份部分来说,这似乎过大了。网络核心。
所以什么是正确的方式来实现我的目标-简单的身份验证与用户名和密码在ASP。. NET核心web服务?
现在,在我被指出正确的方向之后,这是我的完整解决方案:
这是中间件类,它在每个传入请求上执行,并检查请求是否具有正确的凭据。如果没有凭据或凭据错误,服务立即响应401 Unauthorized错误。
public class AuthenticationMiddleware
{
private readonly RequestDelegate _next;
public AuthenticationMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
string authHeader = context.Request.Headers["Authorization"];
if (authHeader != null && authHeader.StartsWith("Basic"))
{
//Extract credentials
string encodedUsernamePassword = authHeader.Substring("Basic ".Length).Trim();
Encoding encoding = Encoding.GetEncoding("iso-8859-1");
string usernamePassword = encoding.GetString(Convert.FromBase64String(encodedUsernamePassword));
int seperatorIndex = usernamePassword.IndexOf(':');
var username = usernamePassword.Substring(0, seperatorIndex);
var password = usernamePassword.Substring(seperatorIndex + 1);
if(username == "test" && password == "test" )
{
await _next.Invoke(context);
}
else
{
context.Response.StatusCode = 401; //Unauthorized
return;
}
}
else
{
// no authorization header
context.Response.StatusCode = 401; //Unauthorized
return;
}
}
}
中间件扩展需要在服务Startup类的Configure方法中调用
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseMiddleware<AuthenticationMiddleware>();
app.UseMvc();
}
就这些!:)
一个非常好的中间件资源在。net核心和身份验证可以在这里找到:https://www.exceptionnotfound.net/writing-custom-middleware-in-asp-net-core-1-0/
您可以实现处理基本身份验证的中间件。
public async Task Invoke(HttpContext context)
{
var authHeader = context.Request.Headers.Get("Authorization");
if (authHeader != null && authHeader.StartsWith("basic", StringComparison.OrdinalIgnoreCase))
{
var token = authHeader.Substring("Basic ".Length).Trim();
System.Console.WriteLine(token);
var credentialstring = Encoding.UTF8.GetString(Convert.FromBase64String(token));
var credentials = credentialstring.Split(':');
if(credentials[0] == "admin" && credentials[1] == "admin")
{
var claims = new[] { new Claim("name", credentials[0]), new Claim(ClaimTypes.Role, "Admin") };
var identity = new ClaimsIdentity(claims, "Basic");
context.User = new ClaimsPrincipal(identity);
}
}
else
{
context.Response.StatusCode = 401;
context.Response.Headers.Set("WWW-Authenticate", "Basic realm='"dotnetthoughts.net'"");
}
await _next(context);
}
这段代码是在asp.net core的测试版中编写的。
若要将其仅用于特定的控制器,例如:
app.UseWhen(x => (x.Request.Path.StartsWithSegments("/api", StringComparison.OrdinalIgnoreCase)),
builder =>
{
builder.UseMiddleware<AuthenticationMiddleware>();
});
我认为你可以使用JWT (Json Web Tokens)。
首先需要安装System.IdentityModel.Tokens.Jwt:
$ dotnet add package System.IdentityModel.Tokens.Jwt
您需要为令牌生成和身份验证添加一个控制器,如下所示:
public class TokenController : Controller
{
[Route("/token")]
[HttpPost]
public IActionResult Create(string username, string password)
{
if (IsValidUserAndPasswordCombination(username, password))
return new ObjectResult(GenerateToken(username));
return BadRequest();
}
private bool IsValidUserAndPasswordCombination(string username, string password)
{
return !string.IsNullOrEmpty(username) && username == password;
}
private string GenerateToken(string username)
{
var claims = new Claim[]
{
new Claim(ClaimTypes.Name, username),
new Claim(JwtRegisteredClaimNames.Nbf, new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds().ToString()),
new Claim(JwtRegisteredClaimNames.Exp, new DateTimeOffset(DateTime.Now.AddDays(1)).ToUnixTimeSeconds().ToString()),
};
var token = new JwtSecurityToken(
new JwtHeader(new SigningCredentials(
new SymmetricSecurityKey(Encoding.UTF8.GetBytes("Secret Key You Devise")),
SecurityAlgorithms.HmacSha256)),
new JwtPayload(claims));
return new JwtSecurityTokenHandler().WriteToken(token);
}
}
之后更新Startup.cs类如下所示:
namespace WebAPISecurity
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddAuthentication(options => {
options.DefaultAuthenticateScheme = "JwtBearer";
options.DefaultChallengeScheme = "JwtBearer";
})
.AddJwtBearer("JwtBearer", jwtBearerOptions =>
{
jwtBearerOptions.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("Secret Key You Devise")),
ValidateIssuer = false,
//ValidIssuer = "The name of the issuer",
ValidateAudience = false,
//ValidAudience = "The name of the audience",
ValidateLifetime = true, //validate the expiration and not before values in the token
ClockSkew = TimeSpan.FromMinutes(5) //5 minute tolerance for the expiration date
};
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseAuthentication();
app.UseMvc();
}
}
就是这样,现在剩下的就是把[Authorize]
属性放在你想要的控制器或动作上。
这是一个完整的直接教程的链接。
http://www.blinkingcaret.com/2017/09/06/secure-web-api-in-asp-net-core/我已经为基本身份验证实现了BasicAuthenticationHandler
,因此您可以将其与标准属性Authorize
和AllowAnonymous
一起使用。
public class BasicAuthenticationHandler : AuthenticationHandler<BasicAuthenticationOptions>
{
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
var authHeader = (string)this.Request.Headers["Authorization"];
if (!string.IsNullOrEmpty(authHeader) && authHeader.StartsWith("basic", StringComparison.OrdinalIgnoreCase))
{
//Extract credentials
string encodedUsernamePassword = authHeader.Substring("Basic ".Length).Trim();
Encoding encoding = Encoding.GetEncoding("iso-8859-1");
string usernamePassword = encoding.GetString(Convert.FromBase64String(encodedUsernamePassword));
int seperatorIndex = usernamePassword.IndexOf(':', StringComparison.OrdinalIgnoreCase);
var username = usernamePassword.Substring(0, seperatorIndex);
var password = usernamePassword.Substring(seperatorIndex + 1);
//you also can use this.Context.Authentication here
if (username == "test" && password == "test")
{
var user = new GenericPrincipal(new GenericIdentity("User"), null);
var ticket = new AuthenticationTicket(user, new AuthenticationProperties(), Options.AuthenticationScheme);
return Task.FromResult(AuthenticateResult.Success(ticket));
}
else
{
return Task.FromResult(AuthenticateResult.Fail("No valid user."));
}
}
this.Response.Headers["WWW-Authenticate"]= "Basic realm='"yourawesomesite.net'"";
return Task.FromResult(AuthenticateResult.Fail("No credentials."));
}
}
public class BasicAuthenticationMiddleware : AuthenticationMiddleware<BasicAuthenticationOptions>
{
public BasicAuthenticationMiddleware(
RequestDelegate next,
IOptions<BasicAuthenticationOptions> options,
ILoggerFactory loggerFactory,
UrlEncoder encoder)
: base(next, options, loggerFactory, encoder)
{
}
protected override AuthenticationHandler<BasicAuthenticationOptions> CreateHandler()
{
return new BasicAuthenticationHandler();
}
}
public class BasicAuthenticationOptions : AuthenticationOptions
{
public BasicAuthenticationOptions()
{
AuthenticationScheme = "Basic";
AutomaticAuthenticate = true;
}
}
在Startup.cs - app.UseMiddleware<BasicAuthenticationMiddleware>();
注册。使用这段代码,您可以用标准属性authorize:
[Authorize(ActiveAuthenticationSchemes = "Basic")]
[Route("api/[controller]")]
public class ValuesController : Controller
AllowAnonymous
属性。您可以使用ActionFilterAttribute
public class BasicAuthAttribute : ActionFilterAttribute
{
public string BasicRealm { get; set; }
protected NetworkCredential Nc { get; set; }
public BasicAuthAttribute(string user,string pass)
{
this.Nc = new NetworkCredential(user,pass);
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var req = filterContext.HttpContext.Request;
var auth = req.Headers["Authorization"].ToString();
if (!String.IsNullOrEmpty(auth))
{
var cred = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(auth.Substring(6)))
.Split(':');
var user = new {Name = cred[0], Pass = cred[1]};
if (user.Name == Nc.UserName && user.Pass == Nc.Password) return;
}
filterContext.HttpContext.Response.Headers.Add("WWW-Authenticate",
String.Format("Basic realm='"{0}'"", BasicRealm ?? "Ryadel"));
filterContext.Result = new UnauthorizedResult();
}
}
并将属性添加到控制器
[BasicAuth("USR", "MyPassword")]
正如前面的文章所说,一种方法是实现自定义的基本身份验证中间件。我在这个博客中找到了最好的工作代码和解释:使用自定义中间件的基本授权
我引用了同一个博客,但不得不做了两个改编:
- 当在启动文件中添加中间件->配置函数时,总是在添加app.UseMvc()之前添加自定义中间件。
从appsettings读取用户名、密码时。在启动文件中添加静态只读属性。然后从appsettings.json中读取。最后,从项目中的任何地方读取值。例子:
public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } public static string UserNameFromAppSettings { get; private set; } public static string PasswordFromAppSettings { get; private set; } //set username and password from appsettings.json UserNameFromAppSettings = Configuration.GetSection("BasicAuth").GetSection("UserName").Value; PasswordFromAppSettings = Configuration.GetSection("BasicAuth").GetSection("Password").Value; }
在这个公共Github repo中https://github.com/boskjoett/BasicAuthWebApi您可以看到一个简单的ASP示例。. NET Core 2.2 web API,端点受基本身份验证保护
NET Core 2.0 with Angular
https://fullstackmark.com/post/13/jwt-authentication-with-aspnet-core-2-web-api-angular-5-net-core-identity-and-facebook-login确保使用身份验证过滤器的类型
[authorization (AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]