我目前正试图在新发布的Asp.Net Core 2.0上使用Angular 4(4.3.5),特别是防伪代币,使安全性正常工作。
我使用的是JavascriptServices,它提供了入门应用程序(它是VisualStudio2017.3中默认的Angular模板)。Javascript服务在.cs.html页面上托管Angular网站的主页。事实证明,这是非常有益的,因为我可以使用标准表单身份验证(dot-net-core-Identity)锁定所有内容,当用户未登录时,它会将用户重定向到/Account/login的单独(非角度)登录页面。然后,您可以登录该页面并重定向到主页,水疗中心就会在授权用户的上下文中启动并运行。
该工作应用程序可在此处找到。
难题的最后一块是让ValidateAntiForgeryToken属性发挥作用。当您登录到Account/Login页面时,这很好,因为它不是在angular 4的上下文中运行的。但是,当我在主页上的Angular 4中运行时,当我向服务器发布帖子时,如果存在ValidateAntiForgeryToken属性,该帖子将被阻止。
因此,我注释掉了Account/Logout方法上的ValidateAntiForgeryToken属性。这是因为我正在使用Angular http帖子从该网站注销。它在不使用属性时工作,但在使用时失败/被阻止。
在这里找到Angular 4文档后,我更改了反伪造代币的名称,以匹配Angular 4识别的内容。为此,我修改了Startup.cs文件,添加了一些行,如下所示:
public void ConfigureServices(IServiceCollection services)
{
services.AddAntiforgery(options =>
{
options.Cookie.Name = "XSRF-TOKEN";
options.Cookie.HttpOnly = false;
});
...
}
这将使Angular应用程序能够访问Angular 4期望的名称的反伪造cookie。
在我的应用程序中,我刚刚切换到使用新的HttpClient服务(显然Http服务已经被弃用!),它应该使用一个拦截器来自动向服务器发送XSRF_TOKEN。
但我一直无法做到这一点。
我尝试了使用HttpClient服务的标准后期调用:
this.httpClient.post(this.baseUrl + 'Account/Logout', "", options).subscribe(result => {
location.replace("/");
}, error => {
console.error(error);
})
我尝试手动添加标题:
let token = this.cookieService.get("XSRF-TOKEN");
console.log(token);
var httpHeaders = new HttpHeaders({ 'XSRF-TOKEN': token })
this.httpClient.post(this.baseUrl + 'Account/Logout', "", { headers: httpHeaders }).subscribe(result => {
location.replace("/");
}, error => {
console.error(error);
})
我尝试使用旧的服务,包括添加和不添加标题:
let token = this.cookieService.get("XSRF-TOKEN");
console.log(token);
let headers = new Headers({
//'Content-Type': 'application/json',
'X-XSRF-TOKEN': token
});
let options = new RequestOptions({ headers: headers });
this.http.post(this.baseUrl + 'Account/Logout', "", options).subscribe(result => {
location.replace("/")
}, error => console.error(error));
不幸的是,我运气不好。有其他人成功地做到了吗?
好的,我已经找到了一个解决方案。
我的Index.chtml页面现在看起来是这样的:
@Html.AntiForgeryToken()
<app>Loading...</app>
<script src="~/dist/vendor.js" asp-append-version="true"></script>
@section scripts {
<script src="~/dist/main-client.js" asp-append-version="true"></script>
}
这样做的目的是在服务器端生成一个防伪令牌,并将其放置在页面的隐藏输入字段中。当您查看页面源时,隐藏的输入字段如下所示:
<input name="__RequestVerificationToken" type="hidden" value="CfDJ8DaEnvKVNL9EhPVzHKQWhC-PeT4eNm_svdTEyGZje4WnH34sBfG_D_AphtPzBM1JEkQUHsSX1KWBivxAOtPsOvfMKs5N_dLn0Sr3xRG-N2s0oFaa3-yvG87qdzXYm1yBSYH7dlRiBu5It3wi2iYzWqyo4B1i_iRtmikz41gmuldze8VE72zVqmeHZav5rQiHkw" />
在我的Logout方法中,我获取令牌并将其在标头中提交给服务器端控制器。标头的名称为RequestVerificationToken,不需要下划线。
Logout() {
let token: any = $("input[name=__RequestVerificationToken]").val();
if (token !== null) {
var httpHeaders: any = new HttpHeaders({ 'RequestVerificationToken': token });
this.httpClient.post("/Account/Logout", null, { headers: httpHeaders }).subscribe(() => {
window.location.replace("/Account/Login");
}, error => {
console.log(error);
});
}
}
在服务器端,AntiForgery过滤器运行,将其与提交的值进行比较,如果它是预期值,则将允许执行服务器端的Account/Logout方法。
服务器端方法如下:
//
// POST: /Account/Logout
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Logout()
{
await _signInManager.SignOutAsync();
return RedirectToAction(nameof(HomeController.Index), "Home");
}
在Logout方法中设置一个断点将证明它是执行的。
这里可能有个陷阱。我不确定令牌是否在每次请求时都会更改。我还没有对此进行任何测试。如果是这样,则需要在每次请求之后向页面添加一个新的令牌。
此外,我不需要修改默认cookie的行为。我不是以Angular 4的方式来做这件事的。它纯粹是ASP.Net方法。也就是说,在Startup.cs文件中的ConfigureServices方法中,我已经注释掉了Cookie的更改:
public void ConfigureServices(IServiceCollection services)
{
//services.AddAntiforgery(options =>
//{
// options.Cookie.Name = "XSRF-TOKEN";
// options.Cookie.HttpOnly = false;
//});
如果你认为你已经找到了更好的方法,请尽一切努力发布你的解决方案。
这里也有同样的问题。我的解决方案:
-
向请求管道添加一个Func,如下所示(F#):
member this.Configure(app: IApplicationBuilder, env: IHostingEnvironment, appLifetime : IApplicationLifetime, antiforgery : IAntiforgery) = let tokenMiddleware = fun (context : HttpContext) (next: Func<Task>) -> let path = context.Request.Path.Value if path <> null && not (path.ToLower().Contains("/api")) then let tokens = antiforgery.GetAndStoreTokens(context) context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken, CookieOptions ( HttpOnly = false, Secure = false ) ) next.Invoke () app .UseStaticFiles() .UseIdentityServer() .Use(tokenMiddleware)
这里的重点是将secure设置为false,否则脚本代码将无法获取cookie。
-
在Angular中,我必须手动设置标题选项
login(login: UserLogin, completed: () => void, failed: (message: string) => void) { const token = this.cookieService.get('XSRF-TOKEN'); const httpHeaders = (token) ? new HttpHeaders({ 'X-XSRF-TOKEN': token }) : null; this.http.post(this.apiUrl() + AccountServiceService.Login_Url, login, { headers: httpHeaders })
对我来说还可以。HIH-
尝试用[AutoValidateAntiforgeryToken]
替换[ValidateAntiForgeryToken]
.NET Core 2更改了进程。
https://github.com/aspnet/Antiforgery/blob/dev/src/Microsoft.AspNetCore.Antiforgery/Internal/DefaultAntiforgeryTokenGenerator.cs
此问题似乎与HttpClient有关。HttpXsrfInterceptor没有为绝对URL设置xsrf令牌,HttpClient也没有在HttpPost上设置X-xsrf-token。
我建议,如果你能够使用相对url,你可以在没有this.baseUrl
:的情况下尝试以下操作
this.httpClient.post('Account/Logout', "", options).subscribe(result => {
location.replace("/");
}, error => {
console.error(error);
})
您还需要将以下内容添加到您的startup.cs文件中,以将XSRF-TOKEN作为Set Cookie:传递
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IAntiforgery antiforgery)
{
...
app.Use(next => context =>
{
if (
string.Equals(context.Request.Path.Value, "/", StringComparison.OrdinalIgnoreCase) ||
string.Equals(context.Request.Path.Value, "/index.html", StringComparison.OrdinalIgnoreCase))
{
// We can send the request token as a JavaScript-readable cookie, and Angular will use it by default.
var tokens = antiforgery.GetAndStoreTokens(context);
context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken,
new CookieOptions() { HttpOnly = false });
}
return next(context);
});
...
或者,您可以尝试编写自己的XSRFInterceptor。此处显示了一个示例。