使用Angular 4和Asp.Net Core 2获取ValidateAntiForgeryToken



我目前正试图在新发布的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。此处显示了一个示例。

相关内容

  • 没有找到相关文章

最新更新