如何在.NetCore API中的不同应用程序上验证从一个应用程序发出的AntiForgeryToken?

Ahm*_*der 3 cross-domain asp.net-core-mvc .net-core asp.net-core asp.net-core-webapi

我有两个应用程序,ASP.NET Core Web APP(应用程序1)和.NET CORE API(应用程序2)。我在(应用程序 1)中发出 AntiForgeryToken 并将其以隐藏字段的形式返回(实际上 .netcore 自动生成)。现在,当用户提交表单时,数据将转到(应用程序 2)并使用 CORS,我能够将(应用程序 1)发出的 AntiForgeryToken 的 cookie 发送到(应用程序 2),以便我可以验证它并在(应用程序 2)上进行验证添加了 validateAntiFOrgeryToken 属性,以便我可以验证它。下面是我在 chrome 开发工具响应选项卡上收到的错误。

\n\n

我尝试了一切并搜索了所有内容,但这个特定错误无法找到答案。

\n\n
System.InvalidOperationException: No service for type \'Microsoft.AspNetCore.Mvc.ViewFeatures.Filters.ValidateAntiforgeryTokenAuthorizationFilter\' has been registered.\n   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)\n   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)\n   at Microsoft.AspNetCore.Mvc.ValidateAntiForgeryTokenAttribute.CreateInstance(IServiceProvider serviceProvider)\n   at Microsoft.AspNetCore.Mvc.Filters.DefaultFilterProvider.ProvideFilter(FilterProviderContext context, FilterItem filterItem)\n   at Microsoft.AspNetCore.Mvc.Filters.DefaultFilterProvider.OnProvidersExecuting(FilterProviderContext context)\n   at Microsoft.AspNetCore.Mvc.Filters.FilterFactory.CreateUncachedFiltersCore(IFilterProvider[] filterProviders, ActionContext actionContext, List`1 filterItems)\n   at Microsoft.AspNetCore.Mvc.Filters.FilterFactory.GetAllFilters(IFilterProvider[] filterProviders, ActionContext actionContext)\n   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvokerCache.GetCachedResult(ControllerContext controllerContext)\n   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvokerProvider.OnProvidersExecuting(ActionInvokerProviderContext context)\n   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionInvokerFactory.CreateInvoker(ActionContext actionContext)\n   at Microsoft.AspNetCore.Mvc.Routing.ActionEndpointFactory.<>c__DisplayClass7_0.<CreateRequestDelegate>b__0(HttpContext context)\n   at Microsoft.AspNetCore.Routing.EndpointMiddleware.Invoke(HttpContext httpContext)\n--- End of stack trace from previous location where exception was thrown ---\n   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)\n   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)\n\nHEADERS\n=======\nAccept: application/json, text/javascript, */*; q=0.01\nAccept-Encoding: gzip, deflate, br\nAccept-Language: en-GB,en-US;q=0.9,en;q=0.8\nCache-Control: no-cache\nConnection: close\nContent-Length: 98\nContent-Type: application/json\nCookie: .AspNetCore.Antiforgery.P09gDl3q4JU=CfDJ8MTlw3i2dFxEnHbgYLq-NTBvWTMlXSM5JV9sH03i3b4ulUq0JlSns86jxwas797wsxz9mOS2JDlK6nhntJGc80bpNNwUyUnOQou-iTEwsykSBE7-yfc05pjknlLMNciWmrLzxHaJ-kpG8Tjnqo7jxbc\nHost: localhost:44375\nPragma: no-cache\nReferer: https://localhost:44389/account/signup\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36\norigin: https://localhost:44389\nx-xsrf-token: CfDJ8MTlw3i2dFxEnHbgYLq-NTB8DUVF73lPTj3HAOL2bhD-sIDv2N4Fs0JowHt6W-WB5oQltt8ELtCH03XYq2QAxz96SYseDR-pZ6XMXAXRE8mpGUatGKtWp_yNohpbYt2ZQD25NzYqYdw-fCi3hsdQf1g\nsec-fetch-site: same-site\nsec-fetch-mode: cors\n
Run Code Online (Sandbox Code Playgroud)\n\n

这是应用程序 1 的代码:

\n\n

( 看法 )

\n\n
<div class="form-container">\n\n    <div class="form-inputs">\n        <form id="signup-form" method="post">\n            <!-- Input and Submit elements -->\n            @Html.AntiForgeryToken()\n            <input type="email" name="email" data-send="true" placeholder="Email Address" />\n            <span class="error"></span>\n            <input type="password" name="password" data-send="true" placeholder="Password" data-validate="true" data-val-min="8" data-val-max="20" data-val-length-error="Password must be between 8 and 20 characters" data-val-empty-error="Password is required" />\n            <span class="error"></span>\n            <input type="password" name="confirmpassword" data-send="true" data-validate="true" data-val-compare="password" data-val-empty-error="Confirm Password is required" data-val-compare-error="Password doesn\'t match" placeholder="Confirm Password" />\n            <span class="error"></span>\n            <input type="text" name="username" placeholder="Username" data-send="true" data-validate="true" data-val-min="3" data-val-max="10" data-val-length-error="Username must be between 3 and 10 characters" data-val-empty-error="Username is required" />\n            <span class="error"></span>\n            <div class="button-holder">\n                <button id="signup" class="action"><span id="loader"><i class="fas fa-spinner"></i></span><span id="button-text">signup</span></button>\n            </div>\n            <div class="terms-holder">\n                <p>By Creating an account you agree to our <span><a href="#">Terms of Use</a></span> and <span><a href="#">Privacy Policy</a></span></p>\n            </div>\n        </form>\n\n\n\n    </div>\n</div>\n
Run Code Online (Sandbox Code Playgroud)\n\n

这是应用程序 2 的代码:

\n\n

(启动.cs)

\n\n
using System.Text;\nusing Authentication.Models;\nusing Authentication.Services;\nusing Microsoft.AspNetCore.Authentication.JwtBearer;\nusing Microsoft.AspNetCore.Builder;\nusing Microsoft.AspNetCore.Hosting;\nusing Microsoft.AspNetCore.Http;\nusing Microsoft.Extensions.Configuration;\nusing Microsoft.Extensions.DependencyInjection;\nusing Microsoft.Extensions.Hosting;\nusing Microsoft.IdentityModel.Tokens;\n\nnamespace API\n{\n    public class Startup\n    {\n        public Startup(IConfiguration configuration)\n        {\n            Configuration = configuration;\n        }\n\n        public IConfiguration Configuration { get; }\n        readonly string OnlyOrigin = "OnlyOrigin";\n\n        // This method gets called by the runtime. Use this method to add services to the container.\n        public void ConfigureServices(IServiceCollection services)\n        {\n\n\n            services.AddCors(options => \n            {\n                options.AddDefaultPolicy(\n                builder => builder.WithOrigins("https://localhost:44389")\n                    .AllowAnyMethod()\n                    .AllowCredentials()\n                    .WithHeaders("content-type","X-XSRF-TOKEN")\n                    );\n\n            });\n            services.AddAntiforgery(options =>\n            {\n                // Set Cookie properties using CookieBuilder properties\xe2\x80\xa0.\n                options.HeaderName = "X-XSRF-TOKEN";\n                options.SuppressXFrameOptionsHeader = false;\n            });\n            // configure strongly typed settings objects\n            var appSettingsSection = Configuration.GetSection("AppSettings");\n            services.Configure<AppSettings>(appSettingsSection);\n\n            // configure jwt authentication\n            var appSettings = appSettingsSection.Get<AppSettings>();\n            var key = Encoding.ASCII.GetBytes(appSettings.Secret);\n            services.AddAuthentication(x =>\n            {\n                x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;\n                x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;\n            })\n            .AddJwtBearer(x =>\n            {\n                x.RequireHttpsMetadata = false;\n                x.SaveToken = true;\n                x.TokenValidationParameters = new TokenValidationParameters\n                {\n                    ValidateIssuerSigningKey = true,\n                    IssuerSigningKey = new SymmetricSecurityKey(key),\n                    ValidateIssuer = false,\n                    ValidateAudience = false\n                };\n            });\n            services.AddControllers();\n            services.AddScoped<IAccount, AccountManager>();\n            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();\n        }\n\n        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.\n        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)\n        {\n            if (env.IsDevelopment())\n            {\n                app.UseDeveloperExceptionPage();\n            }\n            app.UseCors();\n            app.UseHttpsRedirection();\n\n            app.UseRouting();\n\n            app.UseAuthorization();\n\n            app.UseEndpoints(endpoints =>\n            {\n                endpoints.MapControllers();\n            });\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

( 控制器 ) :

\n\n
using API.Models;\nusing Authentication.Models;\nusing Authentication.Services;\nusing Microsoft.AspNetCore.Cors;\nusing Microsoft.AspNetCore.Http;\nusing Microsoft.AspNetCore.Mvc;\n\nnamespace API.Controllers\n{\n    [Route("api/authentication")]\n    [ApiController]\n    public class AuthenticationController : ControllerBase\n    {\n        private readonly IAccount accountService;\n        private readonly HttpContext Context;\n\n        public AuthenticationController(IAccount accountService, IHttpContextAccessor contextAccessor)\n        {\n            this.accountService = accountService;\n            this.Context = contextAccessor.HttpContext;\n        }\n\n        [HttpPost]\n        [Consumes("application/json")]\n        [Produces("application/json")]\n        [Route("account/signup")]\n        //[EnableCors("Only Origin")]\n        [ValidateAntiForgeryToken]\n        public string Signup([FromBody] Account account)\n\n        {\n            if(!ModelState.IsValid)\n            {\n                return "not valid";\n            }\n            return accountService.CreateAccount(account);\n\n        }\n\n        [HttpPost]\n        [Consumes("application/json")]\n        [Produces("application/json")]\n        [Route("account/signin")]\n        [ValidateAntiForgeryToken]\n        public IActionResult Signin(LoginViewModel loginViewModel)\n        {\n            if (!ModelState.IsValid)\n            {\n                return BadRequest(new Result\n                {\n                    Type = "Error",\n                    Return = "not valid"\n                });\n            }\n            Result result = accountService.Login(loginViewModel.Email, loginViewModel.Password);\n            return Ok(result);\n        }\n\n\n\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

(jQuery):

\n\n
this.send = function () {\n        alert(csrf);\n        $.ajax({\n            url: "https://localhost:44375/api/authentication/account/signup",\n            headers: {\n                \'Content-Type\': \'application/json\',\n                \'X-XSRF-TOKEN\': csrf\n            },\n            xhrFields: {\n                withCredentials: true\n            },\n            method: \'POST\',\n            dataType: \'json\',\n            data: getDataToSend(),\n            success: function (data) {\n                console.log(\'succes: \' + data);\n            }\n        });\n    }\n
Run Code Online (Sandbox Code Playgroud)\n\n

(请求标头):

\n\n
:authority: localhost:44375\n:method: POST\n:path: /api/authentication/account/signup\n:scheme: https\naccept: application/json, text/javascript, */*; q=0.01\naccept-encoding: gzip, deflate, br\naccept-language: en-GB,en-US;q=0.9,en;q=0.8\ncache-control: no-cache\ncontent-length: 98\ncontent-type: application/json\ncookie: .AspNetCore.Antiforgery.P09gDl3q4JU=CfDJ8MTlw3i2dFxEnHbgYLq-NTBvWTMlXSM5JV9sH03i3b4ulUq0JlSns86jxwas797wsxz9mOS2JDlK6nhntJGc80bpNNwUyUnOQou-iTEwsykSBE7-yfc05pjknlLMNciWmrLzxHaJ-kpG8Tjnqo7jxbc\norigin: https://localhost:44389\npragma: no-cache\nreferer: https://localhost:44389/account/signup\nsec-fetch-mode: cors\nsec-fetch-site: same-site\nuser-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36\nx-xsrf-token: CfDJ8MTlw3i2dFxEnHbgYLq-NTB8DUVF73lPTj3HAOL2bhD-sIDv2N4Fs0JowHt6W-WB5oQltt8ELtCH03XYq2QAxz96SYseDR-pZ6XMXAXRE8mpGUatGKtWp_yNohpbYt2ZQD25NzYqYdw-fCi3hsdQf1g\n
Run Code Online (Sandbox Code Playgroud)\n

小智 5

如果您不希望发生该异常,则必须使用

services.AddControllersWithViews()
Run Code Online (Sandbox Code Playgroud)

代替

services.AddControllers()
Run Code Online (Sandbox Code Playgroud)