NET Core 3.1 MVC Authorization/Authentication 带有在单独的 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

我有 3 个项目:

  1. Net Core 3.1 MVC 项目。
  2. 具有 JWT 身份验证的 Net Core 3.1 Web Api 项目 --> 通过 Entity Framework
  3. 连接到数据库
  4. (也使用网络 api 进行身份验证和数据检索的 Xamarin 应用程序)。

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

我想在WebApi项目中做认证,把token传给Net Core MVC项目

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

MVC 项目 Startup.csclass

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
            }
        }

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

嘿,我有解决方案,请参考以下要点

  1. 首先您需要添加身份验证。 public void ConfigureServices(IServiceCollection services)
           services.AddSession();
           services.AddAuthentication(options =>
           {
                 options.DefaultAuthenticateScheme = >JwtBearerDefaults.AuthenticationScheme;
                 options.DefaultAuthenticateScheme = >JwtBearerDefaults.AuthenticationScheme;
                 options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                 options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
           })
       // Adding Jwt Bearer
       .AddJwtBearer(options =>
       {
           options.SaveToken = true;
           options.RequireHttpsMetadata = false;
           options.TokenValidationParameters = new TokenValidationParameters()
           {
               ValidateIssuer = true,
               ValidateAudience = true,
               ValidAudience = Configuration["JWTConfig:ValidAudience"],
               ValidIssuer = Configuration["JWTConfig:ValidIssuer"],
               IssuerSigningKey = new >SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JWTConfig:Secret"]))
           };
       });
  1. 之后,您必须使用 Session 来存储身份验证令牌,并且在这个令牌中,您必须加密角色列表的令牌组合,无论角色想要传递给授权。 Here i have used JWT Bearer token
  2. 使用此 session 您必须在 public void Configure(IApplicationBuilder app, IWebHostEnvironment env)startup.cs 文件中配置以使用 header 身份验证。
   app.UseSession();
   app.Use(async (context, next) =>
   {
      var token = context.Session.GetString("Token");
      if (!string.IsNullOrEmpty(token))
      {
          context.Request.Headers.Add("Authorization", "Bearer " + token);
      }
      await next();
   });
  1. 然后你必须添加你的控制器
   [Authorize(Roles = "Employee,Student")]
   public ActionResult Leave()
   {
         // your code here
   }