Luk*_*988 6 c# authentication cookies jwt asp.net-core
我们有一个带有API后端(ASP.NET Core WebAPI)的SPA(角度):
SPA正在侦听app.mydomain.com,API 正在侦听app.mydomain.com/API
我们使用内置的JWT进行身份验证Microsoft.AspNetCore.Authentication.JwtBearer;我有一个app.mydomain.com/API/auth/jwt/login创建令牌的控制器。SPA将它们保存到本地存储中。所有作品都很完美。经过安全审核后,我们被告知要为cookie切换本地存储。
问题是app.mydomain.com/APISPA以及移动应用程序和一些客户的服务器2服务器解决方案都使用API on 。
因此,我们必须保持JWT不变,但要添加Cookies。我发现了几篇文章,它们结合了Cookie和JWT在不同的控制器上,但是我需要它们在每个控制器上并行工作。
如果客户端发送cookie,则通过cookie进行身份验证。如果客户端发送JWT承载,则通过JWT进行身份验证。
是否可以通过内置的ASP.NET身份验证或DIY中间件来实现?
谢谢!
好的,我一直在尝试实现这一目标,并且通过以下代码解决了使用 jwt 身份验证令牌和 Cookie 身份验证的相同问题。
API 服务提供者UserController.cs
这使用(Cookie 和 JWT Bearer)两种身份验证方案为用户提供不同的服务
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)]
[Route("[controller]")]
[ApiController]
public class UsersController : ControllerBase
{
private readonly IUserServices_Api _services;
public UsersController(IUserServices_Api services)
{
this._services = services;
}
[HttpGet]
public IEnumerable<User> Getall()
{
return _services.GetAll();
}
}
Run Code Online (Sandbox Code Playgroud)
我的启动.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddAuthentication(options => {
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddCookie(options =>
{
options.LoginPath = "/Account/Login";
options.AccessDeniedPath = "/Home/Error";
})
.AddJwtBearer(options =>
{
options.SaveToken = true;
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
{
ValidateIssuer = true,
ValidateAudience = true,
ValidAudience = " you site link blah blah",
ValidIssuer = "You Site link Blah blah",
IssuerSigningKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(sysController.GetSecurityKey()))
,
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
};
});
}
Run Code Online (Sandbox Code Playgroud)
此外,如果您想为特定控制器自定义身份验证,则必须为授权指定身份验证类型,例如:
[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)]
public IActionResult Index()
{
return View(); // This can only be Access when Cookie Authentication is Authorized.
}
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public IActionResult Index()
{
return View(); // And this one will be Access when JWT Bearer is Valid
}
Run Code Online (Sandbox Code Playgroud)
小智 7
我一直有同样的问题,我刚刚在 stackoverflow 的另一个问题中找到了它似乎是解决方案。
请看看这个。
我会自己尝试该解决方案并使用结果更新此答案。
编辑:似乎不可能以相同的方法实现双重身份验证类型,但我提到的链接中提供的解决方案说:
不可能使用两个 Schemes Or-Like 来授权一个方法,但是您可以使用两个公共方法来调用一个私有方法
Run Code Online (Sandbox Code Playgroud)//private method private IActionResult GetThingPrivate() { //your Code here } //Jwt-Method [Authorize(AuthenticationSchemes = $"{JwtBearerDefaults.AuthenticationScheme}")] [HttpGet("bearer")] public IActionResult GetByBearer() { return GetThingsPrivate(); } //Cookie-Method [Authorize(AuthenticationSchemes = $"{CookieAuthenticationDefaults.AuthenticationScheme}")] [HttpGet("cookie")] public IActionResult GetByCookie() { return GetThingsPrivate(); }
无论如何,您应该查看链接,它确实对我有帮助。归功于尼古拉斯的答案。
我无法找到有关执行此操作的好方法的很多信息 - 仅仅为了支持 2 个授权方案而不得不复制 API 是一种痛苦。
我一直在研究使用反向代理的想法,在我看来这是一个很好的解决方案。
请注意,防伪令牌 (#2,#4) 至关重要,否则您可能会将 API 暴露给 CSRF 攻击。
示例(带有 IdentityServer4 的 .NET Core 2.1 MVC):
为了得到一个工作示例,我从 IdentityServer4 快速入门切换到混合流并添加 API 访问开始。这设置了我使用的 MVC 应用程序使用 cookie 并可以从身份服务器请求 access_token 以调用 API 的场景。
我使用Microsoft.AspNetCore.Proxy作为反向代理并修改了快速启动。
MVC Startup.ConfigureServices:
services.AddAntiforgery();
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
Run Code Online (Sandbox Code Playgroud)
MVC 启动。配置:
app.MapWhen(IsApiRequest, builder =>
{
builder.UseAntiforgeryTokens();
var messageHandler = new BearerTokenRequestHandler(builder.ApplicationServices);
var proxyOptions = new ProxyOptions
{
Scheme = "https",
Host = "api.mydomain.com",
Port = "443",
BackChannelMessageHandler = messageHandler
};
builder.RunProxy(proxyOptions);
});
private static bool IsApiRequest(HttpContext httpContext)
{
return httpContext.Request.Path.Value.StartsWith(@"/api/", StringComparison.OrdinalIgnoreCase);
}
Run Code Online (Sandbox Code Playgroud)
ValidateAntiForgeryToken (Marius Schulz):
public class ValidateAntiForgeryTokenMiddleware
{
private readonly RequestDelegate next;
private readonly IAntiforgery antiforgery;
public ValidateAntiForgeryTokenMiddleware(RequestDelegate next, IAntiforgery antiforgery)
{
this.next = next;
this.antiforgery = antiforgery;
}
public async Task Invoke(HttpContext context)
{
await antiforgery.ValidateRequestAsync(context);
await next(context);
}
}
public static class ApplicationBuilderExtensions
{
public static IApplicationBuilder UseAntiforgeryTokens(this IApplicationBuilder app)
{
return app.UseMiddleware<ValidateAntiForgeryTokenMiddleware>();
}
}
Run Code Online (Sandbox Code Playgroud)
BearerTokenRequestHandler:
public class BearerTokenRequestHandler : DelegatingHandler
{
private readonly IServiceProvider serviceProvider;
public BearerTokenRequestHandler(IServiceProvider serviceProvider, HttpMessageHandler innerHandler = null)
{
this.serviceProvider = serviceProvider;
InnerHandler = innerHandler ?? new HttpClientHandler();
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var httpContextAccessor = serviceProvider.GetService<IHttpContextAccessor>();
var accessToken = await httpContextAccessor.HttpContext.GetTokenAsync("access_token");
request.Headers.Authorization =new AuthenticationHeaderValue("Bearer", accessToken);
var result = await base.SendAsync(request, cancellationToken);
return result;
}
}
Run Code Online (Sandbox Code Playgroud)
_Layout.cshtml:
@Html.AntiForgeryToken()
Run Code Online (Sandbox Code Playgroud)
然后使用您的 SPA 框架,您可以提出请求。为了验证我只是做了一个简单的 AJAX 请求:
<a onclick="sendSecureAjaxRequest()">Do Secure AJAX Request</a>
<div id="ajax-content"></div>
<script language="javascript">
function sendSecureAjaxRequest(path) {
var myRequest = new XMLHttpRequest();
myRequest.open('GET', '/api/secureResource');
myRequest.setRequestHeader("RequestVerificationToken",
document.getElementsByName('__RequestVerificationToken')[0].value);
myRequest.onreadystatechange = function () {
if (myRequest.readyState === XMLHttpRequest.DONE) {
if (myRequest.status === 200) {
document.getElementById('ajax-content').innerHTML = myRequest.responseText;
} else {
alert('There was an error processing the AJAX request: ' + myRequest.status);
}
}
};
myRequest.send();
};
</script>
Run Code Online (Sandbox Code Playgroud)
这是一个概念验证测试,所以你的里程可能非常多,而且我对 .NET Core 和中间件配置很陌生,所以它可能看起来更漂亮。我对此做了有限的测试,只对 API 发出了 GET 请求,没有使用 SSL (https)。
正如预期的那样,如果从 AJAX 请求中删除防伪令牌,它将失败。如果用户尚未登录(经过身份验证),则请求失败。
与往常一样,每个项目都是独一无二的,因此请始终验证您的安全要求是否得到满足。请查看对此答案留下的任何评论,以了解有人可能提出的任何潜在安全问题。
另一方面,我认为一旦子资源完整性 (SRI) 和内容安全策略 (CSP) 在所有常用浏览器上可用(即淘汰旧浏览器),就应该重新评估本地存储以存储 API 令牌,这将降低复杂性令牌存储。 现在应该使用 SRI 和 CSP 来帮助减少支持浏览器的攻击面。
通过此代码,您可以同时使用 cookie 和 header。如果 cookie 为空,则自动检查标头。
在 AddJwtBearer 选项中添加此代码。
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
context.Token = context.Request.Cookies["Authorization"];
return Task.CompletedTask;
}
};
Run Code Online (Sandbox Code Playgroud)
完整用法是:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
{
options.RequireHttpsMetadata = false;
options.SaveToken = false;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidAudience = Configuration["JwtToken:Audience"],
ValidIssuer = Configuration["JwtToken:Issuer"],
IssuerSigningKey =
new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JwtToken:Key"]))
};
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
context.Token = context.Request.Cookies["Authorization"];
return Task.CompletedTask;
}
};
});
Run Code Online (Sandbox Code Playgroud)
标头 => 授权:持有者您的令牌
或者
Cookie => Authorization=Your-Token //不要在Cookie中添加Bearer
Mar*_*eio -1
ASP.NET Core 2.0 Web API
请按照这篇文章实施基于JWT 令牌的身份验证
如果您使用的是 Visual Studio,请确保应用带有过滤器的承载类型认证类型
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
Run Code Online (Sandbox Code Playgroud)
用于控制器或操作。
| 归档时间: |
|
| 查看次数: |
9163 次 |
| 最近记录: |