افزودن و اعتبارسنجی خودکار Anti-Forgery Tokens در برنامههای Angular مبتنی بر ASP.NET Core
نویسنده: وحید نصیری
تاریخ: ۱۳۹۶/۰۴/۱۶ ۱۰:۰
آدرس: www.dntips.ir
| مطالب | ۳۶۹۴ |
| نویسندگان | ۲۷۶ |
| گروههای مطالب | ۱۰۲۴ |
| نقشههای راه | ۱۱۹ |
| دورهها | ۱۴ |
| اشتراکها | ۱۷۹۱۴ |
<form asp-controller="Manage" asp-action="ChangePassword" method="post"> <!-- Form details --> </form>
<form method="post" action="/Manage/ChangePassword"> <!-- Form details --> <input name="__RequestVerificationToken" type="hidden" value="CfDJ8NrAkSldwD9CpLR...LongValueHere!" /> </form>
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Antiforgery;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
namespace AngularTemplateDrivenFormsLab.Utils
{
public class AntiforgeryTokenMiddleware
{
private readonly RequestDelegate _next;
private readonly IAntiforgery _antiforgery;
public AntiforgeryTokenMiddleware(RequestDelegate next, IAntiforgery antiforgery)
{
_next = next;
_antiforgery = antiforgery;
}
public Task Invoke(HttpContext context)
{
var path = context.Request.Path.Value;
if (path != null && !path.StartsWith("/api/", StringComparison.OrdinalIgnoreCase))
{
var tokens = _antiforgery.GetAndStoreTokens(context);
context.Response.Cookies.Append(
key: "XSRF-TOKEN",
value: tokens.RequestToken,
options: new CookieOptions
{
HttpOnly = false // Now JavaScript is able to read the cookie
});
}
return _next(context);
}
}
public static class AntiforgeryTokenMiddlewareExtensions
{
public static IApplicationBuilder UseAntiforgeryToken(this IApplicationBuilder builder)
{
return builder.UseMiddleware<AntiforgeryTokenMiddleware>();
}
}
} public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseAntiforgeryToken(); public void ConfigureServices(IServiceCollection services)
{
services.AddAntiforgery(x => x.HeaderName = "X-XSRF-TOKEN");
services.AddMvc();
} [HttpPost]
[ValidateAntiForgeryToken]
public IActionResult ChangePassword()
{
// ...
return Json(…);
} public void ConfigureServices(IServiceCollection services)
{
services.AddAntiforgery(x => x.HeaderName = "X-XSRF-TOKEN");
services.AddMvc(options =>
{
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
});
}
>npm install >ng build --watch
>dotnet restore >dotnet watch run
Antiforgery token validation failed. The provided antiforgery token was meant for a different claims-based user than the current user.
const lcUrl = request.url.toLowerCase();
if (request.method === "GET" || request.method === "HEAD" ||
lcUrl.startsWith("http://") || lcUrl.startsWith("https://")) {
// skip
} imports: [ HttpClientModule,
HttpClientXsrfModule.withConfig({
cookieName: 'My-Xsrf-Cookie',
headerName: 'My-Xsrf-Header'
}) ] [AllowAnonymous]
[IgnoreAntiforgeryToken]
[HttpPost("[action]")]
public async Task<IActionResult> Login([FromBody] User loginUser) private void regenerateAntiForgeryCookie(IEnumerable<Claim> claims)
{
this.HttpContext.User = new ClaimsPrincipal(new ClaimsIdentity(claims, JwtBearerDefaults.AuthenticationScheme));
var tokens = _antiforgery.GetAndStoreTokens(this.HttpContext);
this.HttpContext.Response.Cookies.Append(
key: "XSRF-TOKEN",
value: tokens.RequestToken,
options: new CookieOptions
{
HttpOnly = false // Now JavaScript is able to read the cookie
});
} new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString(),
ClaimValueTypes.String, _configuration.Value.Issuer), using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web;
using System.Web.Helpers;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
using System.Web.Mvc;
using ActionFilterAttribute = System.Web.Http.Filters.ActionFilterAttribute;
namespace NgxAntiforgeryWebApi.Providers
{
public class XsrfCookieGeneratorAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
var xsrfTokenCookie = new HttpCookie("XSRF-TOKEN")
{
Value = ComputeXsrfTokenValue(),
HttpOnly = false // Now JavaScript is able to read the cookie
};
HttpContext.Current.Response.AppendCookie(xsrfTokenCookie);
}
private string ComputeXsrfTokenValue()
{
string cookieToken, formToken;
AntiForgery.GetTokens(null, out cookieToken, out formToken);
return $"{cookieToken}:{formToken}";
}
}
public class XsrfTokensValidationAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
IEnumerable<string> headerValues;
if (!actionContext.Request.Headers.TryGetValues("X-XSRF-TOKEN", out headerValues))
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.BadRequest) { ReasonPhrase = "X-XSRF-TOKEN header is missing." };
return;
}
if (headerValues == null)
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.BadRequest) { ReasonPhrase = "X-XSRF-TOKEN header value is empty." };
return;
}
var xsrfTokensValue = headerValues.FirstOrDefault();
if (string.IsNullOrEmpty(xsrfTokensValue) || !xsrfTokensValue.Contains(":"))
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.BadRequest) { ReasonPhrase = "X-XSRF-TOKEN header value is null." };
return;
}
var values = xsrfTokensValue.Split(':');
if (values.Length != 2)
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.BadRequest) { ReasonPhrase = "X-XSRF-TOKEN header value is malformed." };
return;
}
var cookieToken = values[0];
var formToken = values[1];
try
{
AntiForgery.Validate(cookieToken, formToken);
}
catch (HttpAntiForgeryException ex)
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.BadRequest) { ReasonPhrase = ex.Message };
}
}
}
} var xsrfTokenCookie = new HttpCookie("XSRF-TOKEN")
{
Value = ComputeXsrfTokenValue(),
HttpOnly = false, // Now JavaScript is able to read the cookie
Path = "/",
Domain = "localhost"
}; this.http
.post<Xyz>(`${this.apiUrl}`, data, { withCredentials: true /* For CORS */ })
.map(response => response || {})
.catch(this.handleError); @Injectable()
export class CORSInterceptor implements HttpInterceptor {
constructor() {}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
request = request.clone({
withCredentials: true
});
return next.handle(request);
}
} {
"/api": {
"target": {
"host": "localhost",
"protocol": "http:",
"port": 8357
},
"secure": false,
"changeOrigin": true,
"logLevel": "info"
}
}