NET Core 3.1 MVC 授权/身份验证,带有在单独的 Net Core 3.1 Web Api 中从外部获取的令牌 (JWT)

Posted

技术标签:

【中文标题】NET Core 3.1 MVC 授权/身份验证,带有在单独的 Net Core 3.1 Web Api 中从外部获取的令牌 (JWT)【英文标题】:NET Core 3.1 MVC Authorization/Authentication with token (JWT) obtained externally in separate Net Core 3.1 Web Api 【发布时间】:2020-11-07 19:37:04 【问题描述】:

我有 3 个项目:

    Net Core 3.1 MVC 项目。 带有 JWT auth 的 Net Core 3.1 Web Api 项目 --> 通过 Entity Framework 连接到 db (也使用 Web api 进行身份验证和数据检索的 Xamarin 应用程序)。

我不想从两个项目 (1,2) 分别连接到同一个数据库(感觉不是一个好主意,如果我错了,请纠正我,希望将 db crud 操作包含在 web api 中)。

我想在 Web Api 项目中进行身份验证并将令牌传递给 Net Core MVC 项目。

我不知道如何使用令牌授权此用户进行 MVC 项目,以便可以根据用户的角色访问控制器 等基本上用令牌将此用户登录到MVC项目中 在 web api 中获得。这甚至可能还是正确的方法?任何 请帮忙?

MVC 项目 Startup.cs 类

public class Startup
    
        public Startup(IConfiguration configuration)
        
            Configuration = configuration;
        

        public IConfiguration Configuration  get; 

        public void ConfigureServices(IServiceCollection services)
                    
            // add for razor pages and refresh without rebuilding
            services.AddRazorPages().AddRazorRuntimeCompilation();


            // add httpClient
            services.AddHttpClient();

            // start auth jwt
            services.AddSession(options => 
                options.IdleTimeout = TimeSpan.FromMinutes(1);
            );
            //services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

            //Provide a secret key to Encrypt and Decrypt the Token
            var SecretKey = Encoding.ASCII.GetBytes
                 ("mySecretKeyForAuthenticationAndAuthorization");
            //Configure JWT Token Authentication
            services.AddAuthentication(auth =>
            
                auth.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                auth.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            )
            .AddJwtBearer(token =>
            
                token.RequireHttpsMetadata = false;
                token.SaveToken = true;
                token.TokenValidationParameters = new TokenValidationParameters
                
                    ValidateIssuerSigningKey = true,
            //Same Secret key will be used while creating the token
            IssuerSigningKey = new SymmetricSecurityKey(SecretKey),
                    ValidateIssuer = true,
            //Usually, this is your application base URL
            ValidIssuer = "https://myAzureWebAPi.azurewebsites.net",
                    ValidateAudience = true,
            //Here, we are creating and using JWT within the same application.
            //In this case, base URL is fine.
            //If the JWT is created using a web service, then this would be the consumer URL.
            ValidAudience = "https://mylocalhost/",
                    RequireExpirationTime = true,
                    ValidateLifetime = true,
                    ClockSkew = TimeSpan.Zero
                ;
            );
            // end auth jwt


            // add for roles authorization
           services.AddAuthorization(config =>
           
               config.AddPolicy(Policies.Admin, Policies.AdminPolicy());
               config.AddPolicy(Policies.Client, Policies.ClientPolicy());
           );

            services.AddControllersWithViews();
        

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        
            if (env.IsDevelopment())
            
                app.UseDeveloperExceptionPage();
            
            else
            
                app.UseExceptionHandler("/Home/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            
            app.UseHttpsRedirection();
            app.UseStaticFiles();

            //add for jwt
            app.UseCookiePolicy();
            app.UseSession();
            //Add JWToken to all incoming HTTP Request Header
            app.Use(async (context, next) =>
            
                var JWToken = context.Session.GetString("JWToken");
                if (!string.IsNullOrEmpty(JWToken))
                
                    context.Request.Headers.Add("Authorization", "Bearer " + JWToken);
                
                await next();
            );


            app.UseRouting();


            // added for jwt
            app.UseAuthentication();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            
                endpoints.MapControllerRoute(    
                     name: "default",
                     pattern: "area=Client/controller=LoginPage/action=Index/id?");
            );
        
    

MVC LoginPageController.cs

    [Area("Client")]
        public class LoginPageController : Controller
        
            private readonly IHttpClientFactory _clientFactory;
            public LoginPageController(IHttpClientFactory clientFactory)
            
                _clientFactory = clientFactory;
            
      
            public IActionResult Index()
            
                return View();
            
    
    
            [HttpPost]
            public async Task<IActionResult> Login([Bind] LoginModel loginModel)
            
                var client = _clientFactory.CreateClient();
                //var client = new HttpClient();
    
                try
                
                    var json = JsonConvert.SerializeObject(loginModel);
                    var content = new StringContent(json, Encoding.UTF8, "application/json");
    
                    var outcome = await client.PostAsync("https://<AzureWebAPiUrl>/api/accounts/login", content);
    
                    if (outcome!= null && outcome.IsSuccessStatusCode)
                    
                        var jsonResult = await outcome.Content.ReadAsStringAsync();
                        var token = JsonConvert.DeserializeObject<Token>(jsonResult);
    
                        Console.WriteLine(token.user_role);
    
                        // store token in a session
                        HttpContext.Session.SetString("JWToken", token.access_token);
    
                        // Here is the problem, once I have the token how do I make this user be 
 //authenticated in the mvc project so that the [Authorize[Role = "someRole"] on controllers works
                    
    
                
                catch (Exception ex)
                
                    Console.WriteLine(ex.Message);
                
    
                return RedirectToAction("Index", "AdminDeals", new  area = "Admin"); // only if role is admin
            
        

【问题讨论】:

【参考方案1】:

这是您应该为您的应用程序实现 OAuth2.0 的典型场景 - 您有 2 种客户端(MVC 和 Xamrin),它们都需要经过身份验证才能访问您的 API 项目,您的 API 应该受到保护身份提供者,而不是自己进行身份验证。在 asp.net core 中,最流行的解决方案是Identity Server 4,你不必重新发明***,只需创建一个身份提供程序服务器,然后根据文档中的说明配置你的 API 和 MVC 项目,一切正常然后。同时,Identity Server 4 支持实体框架

【讨论】:

一个可靠的建议,谢谢,将在 IdentityServer4 上探索更多

以上是关于NET Core 3.1 MVC 授权/身份验证,带有在单独的 Net Core 3.1 Web Api 中从外部获取的令牌 (JWT)的主要内容,如果未能解决你的问题,请参考以下文章

.Net Core 2.1 WepAPI 中使用 JWT 的身份验证和授权

.NET Core - 在 IIS 上使用 Windows 身份验证从 MVC 应用程序调用 Web API 导致 HttpRequestException 401(未经授权)状态代码

用户授权 - 为用户分配授权策略 .Net Core 3.1

ASP.NET Core 3.1 MVC AddOpenIDConnect 与 IdentityServer3

从 ASP.NET Core 1.1 MVC 迁移到 2.0 后,自定义 cookie 身份验证不起作用

.Net Core 2 OpenID Connect 身份验证和多个身份