AngularJS 内置了对 XSRF(AKA 防伪)的支持
XSRF 是一种技术,未经授权的网站可以借此获取您用户的
私人数据。 Angular 提供了一种机制来对抗 XSRF。什么时候
执行 XHR 请求时,$http 服务从 cookie 中读取令牌
(默认为 XSRF-TOKEN)并将其设置为 HTTP 标头 (X-XSRF-TOKEN)。
由于只有在您的域上运行的 JavaScript 才能读取 cookie,
您的服务器可以确信 XHR 来自运行的 JavaScript
在您的域上。跨域请求不会设置header。
如果您想利用此功能,您必须创建将创建和验证 XSRF-TOKEN cookie 的操作过滤器。
这是我使用的简化版本。
AntiForgeryTokenCookieAttribute - 使用此属性添加 XSRF-TOKEN cookie
/// <summary>
/// Create a XSRF token in the XSRF-TOKEN cookie which is automatically read by AngularJS
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class AntiForgeryTokenCookieAttribute : ActionFilterAttribute
{
private readonly IAntiForgeryVerificationTokenStore _verificationTokenStore = new AntiForgeryVerificationTokenCookieStore(); //TODO: make configurable
private const string CookieName = "XSRF-TOKEN"; //TODO: make configurable
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
var oldVerificationToken = _verificationTokenStore.GetVerificationToken(filterContext.HttpContext.Request);
string newVerificationToken;
string newToken;
AntiForgery.GetTokens(oldVerificationToken, out newVerificationToken, out newToken);
if (newVerificationToken != null)
{
_verificationTokenStore.StoreVerificationToken(filterContext.HttpContext.Response, newVerificationToken);
}
filterContext.HttpContext.Response.Cookies.Add(new HttpCookie(CookieName, newToken));
}
}
ValidateAntiForgeryTokenHeaderAttribute
/// <summary>
/// Validate the XSRF token stored in the X-XSRF-TOKEN header.
/// If the header doesn't exist, look for the XSRF token in the from post.
///
/// Compatible with ValidateAntiForgeryTokenAttribute
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class ValidateAntiForgeryTokenHeaderAttribute : FilterAttribute, IAuthorizationFilter
{
private readonly IAntiForgeryVerificationTokenStore _verificationTokenStore = new AntiForgeryVerificationTokenCookieStore(); //TODO: make configurable
private const string TokenHeaderName = "X-XSRF-TOKEN"; //TODO: make configurable
public void OnAuthorization(AuthorizationContext filterContext)
{
var token = filterContext.HttpContext.Request.Headers[TokenHeaderName];
if (token != null)
{
//validate the token stored in header
var verificationToken = _verificationTokenStore.GetVerificationToken(filterContext.HttpContext.Request);
if (verificationToken == null) { throw new HttpAntiForgeryException("Required verification token not found"); }
AntiForgery.Validate(verificationToken, token);
}
else
{
//validate the token stored in form. Same as ValidateAntiForgeryTokenAttribute
AntiForgery.Validate();
}
}
}
IAntiForgeryVerificationTokenStore
public interface IAntiForgeryVerificationTokenStore
{
string GetVerificationToken(HttpRequestBase request);
void StoreVerificationToken(HttpResponseBase response, string token);
}
AntiForgeryVerificationTokenCookieStore
public class AntiForgeryVerificationTokenCookieStore : IAntiForgeryVerificationTokenStore
{
public string GetVerificationToken(HttpRequestBase request)
{
if (request == null) { throw new ArgumentNullException("request"); }
var token = request.Cookies[AntiForgeryConfig.CookieName];
return token != null ? token.Value : null;
}
public void StoreVerificationToken(HttpResponseBase response, string token)
{
if (response == null) { throw new ArgumentNullException("response"); }
if (token == null) { throw new ArgumentNullException("token"); }
response.Cookies.Add(new HttpCookie(AntiForgeryConfig.CookieName, token) { HttpOnly = true, Secure = AntiForgeryConfig.RequireSsl });
}
}