【问题标题】:authenticate MVC website user from Web API by using MVC Core Identity使用 MVC Core Identity 从 Web API 验证 MVC 网站用户
【发布时间】:2020-03-25 10:28:04
【问题描述】:

我有两个 .net core 2.2 项目;第一个是 MVC 项目,它类似于用户可以登录的表示层和处理 DB 操作的其他 Web API。我想在 Web API 中处理登录请求,并使用 MVC Core Identity 将登录结果返回给 MVC 项目。我的 DbContext 在 Web API 项目中。是否有根据 Web API 请求的结果创建身份 cookie?

【问题讨论】:

  • 您可以使用Request.GetOwinContext().Authentication.SignIn() 做到这一点。

标签: c# asp.net-mvc asp.net-web-api asp.net-core-mvc asp.net-identity


【解决方案1】:

在这种情况下,应该使用访问令牌而不是 cookie 来处理身份验证。

对于您的 Web API,在 IdentityServer 4 库的帮助下实现 资源所有者密码凭证 授权类型的 OAuth2 协议。最后您应该能够从 API 获取访问令牌以交换登录凭据

对于您的 MVC 项目,创建一个表来存储令牌-sessid 对,因此 当 MVC 应用程序在会话期间从 API 获取访问令牌时,它会将它们保存在表中。对于后续请求,MVC 应用程序将从表中获取令牌(通过使用 sessid)并使用它来访问 Web API。

【讨论】:

    【解决方案2】:

    首先在服务器端 MVC 的启动类中 添加->

     services.AddAuthentication(options => { 
            options.DefaultScheme = "Cookies"; 
        }).AddCookie("Cookies", options => {
            options.Cookie.Name = "auth_cookie";
            options.Cookie.SameSite = SameSiteMode.None;
            options.Events = new CookieAuthenticationEvents
            {                          
                OnRedirectToLogin = redirectContext =>
                {
                    redirectContext.HttpContext.Response.StatusCode = 401;
                    return Task.CompletedTask;
                }
            };                
        });
    

    然后在 MVC 的登录控制器中

    [HttpPost]
    public async Task<IActionResult> Login(string username, string password)
    {
        if (!IsValidUsernameAndPasswod(username, password))
            return BadRequest();
    
        var user = GetUserFromUsername(username);
    
        var claimsIdentity = new ClaimsIdentity(new[]
        {
            new Claim(ClaimTypes.Name, user.Username),
            //...
        }, "Cookies");
    
        var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
        await Request.HttpContext.SignInAsync("Cookies", claimsPrincipal);
    
        return NoContent();
    }
    

    请注意,我们引用了我们在 Startup.cs 中定义的“Cookies”身份验证方案。

    然后在你的 web api 上->

    CookieContainer cookieContainer = new CookieContainer();
    HttpClientHandler handler = new HttpClientHandler
    {
        CookieContainer = cookieContainer
    };
    handler.CookieContainer = cookieContainer;
    var client = new HttpClient(handler);
    
    var loginResponse = await client.PostAsync("http://yourdomain.com/api/account/login?username=theUsername&password=thePassword", null);
    if (!loginResponse.IsSuccessStatusCode){
        //handle unsuccessful login
    }
    
    var authCookie = cookieContainer.GetCookies(new Uri("http://yourdomain.com")).Cast<Cookie>().Single(cookie => cookie.Name == "auth_cookie");
    
    //Save authCookie.ToString() somewhere
    //authCookie.ToString() -> auth_cookie=CfDJ8J0_eoL4pK5Hq8bJZ8e1XIXFsDk7xDzvER3g70....
    

    这应该可以帮助您完成任务。当然可以根据您的要求更改值,但因此代码将是一个很好的参考点。

    还添加所需的代码以从您的 Web api 应用程序设置 cookie。 希望对您有所帮助!

    【讨论】:

      【解决方案3】:

      我不确定我是否完全了解您的问题陈述。我假设您正在寻求某种机制,使用户能够将他们的身份从 Web 客户端一路传递到您的 DbContext。 我还假设您并没有真正在 MVC 应用上验证用户身份,并且基本上将他们的请求代理到 WebAPI。

      如果是这样,您可能需要考虑制作一个JWT(最好是签名的)令牌作为您的 WebAPI 响应,然后将其存储在客户端上(我猜 cookie 是一个足够好的机制)。

      那么 MVC 项目自然会凭借 Session State 获得令牌,您所要做的就是将它与您发出的每个 WebAPI 请求一起传递。

      【讨论】:

        【解决方案4】:

        1.只有两个服务

        如果您的系统只有两个服务(前端和后端),您可以将 Cookie 用于前端的所有身份验证方案,并使用您的 api 进行用户验证。

        在您的 Web 应用程序中实现登录页面,并从您的登录操作方法(发布)调用后端端点(您的 api)验证用户,您可以在其中根据您的数据库验证凭据。请注意,您不需要在 Internet 上发布此端点。

        配置服务:

        services.AddAuthentication(options =>
        {
            options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        })
        .AddCookie(options =>
        {
            options.LoginPath = "/auth/login";
            options.LogoutPath = "/auth/logout";
        });
        

        AuthController:

        [HttpPost]
        public IActionResult Login([FromBody] LoginViewModel loginViewModel)
        {
            User user = authenticationService.ValidateUserCredentials(loginViewModel.Username, loginViewModel.Password);
        
            if (user != null)
            {
                var claims = new List<Claim>
                {
                    new Claim(ClaimTypes.Name, user.UserName),
                    new Claim(ClaimTypes.Role, user.Role),
                    new Claim(ClaimTypes.Email, user.Email)
                };
                var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
                var principal = new ClaimsPrincipal(identity);
        
                await HttpContext.SignInAsync(principal);
        
                return Redirect(loginViewModel.ReturnUrl);
            }
        
            ModelState.AddModelError("LoginError", "Invalid credentials");
            return View(loginViewModel);
        }
        

        2。 OAuth2 或 OpenId Connect 专用服务器

        但是,如果您想实现自己的授权服务或身份提供程序,可以由您的所有应用程序(正面和背面)使用,我建议您使用 OAuth2 或 OpenId 等标准创建您自己的服务器。此服务应专门用于此目的。

        如果您的服务是网络核心,您可以使用IdentityServer。它是一个通过 OpenIdConnect 认证的中间件,非常完整和可扩展。您拥有大量文档,并且对于 OAuth2 和 OpenId 都很容易实现。您可以添加您的 dbContext 以使用您的用户模型。

        您的 Web 应用 ConfigureServices:

        services.AddAuthentication(options =>
        {
            options.DefaultScheme = "Cookies";
            options.DefaultChallengeScheme = "oidc";
        })
        .AddCookie("Cookies")
        .AddOpenIdConnect("oidc", options =>
        {
            options.SignInScheme = "Cookies";
            options.Authority = "https://myauthority.com";
            options.ClientId = "client";
            options.ClientSecret = "secret";
            options.SaveTokens = true;
            options.Scope.Clear();
            options.Scope.Add("myapi");
            // ...
        }
        

        您的身份提供者配置服务:

        services.AddIdentityServer()
            .AddInMemoryClients(Config.GetClients())
            .AddInMemoryApiResources(Config.GetApis())
            .AddInMemoryIdentityResources(Config.GetIdentityResources())
            .AddConfigurationStore(options =>
            {
                options.ConfigureDbContext = builder =>
                    builder.UseSqlServer(connectionString,
                        sql => sql.MigrationsAssembly(migrationsAssembly));
            })
            .AddDeveloperSigningCredential();
        

        通过这种方式,您的前台将请求访问访问 api 所需的范围。前端将收到具有此范围的访问令牌(如果允许此客户端用于请求的范围)。这些 api 又可以使用如下验证中间件验证访问令牌:

        services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            .AddJwtBearer(options =>
            {
                options.Authority = "https://myauthority.com";
                options.Audience = "myapi";
            });
        

        3.自定义远程处理程序

        如果您仍然喜欢自己实现远程功能,您可以实现自定义RemoteAuthenticationHandler。此抽象类可帮助您重定向到远程登录服务(您的 api),并使用 Web 应用程序中的授权结果处理回调重定向的结果。此结果用于填充用户 ClaimsPrincipal,如果您以这种方式配置 Web 应用身份验证服务,则可以在 Cookie 中维护用户会话:

        services.AddAuthentication(options =>
        {
            options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = "CustomScheme";
        })
        .AddCookie()
        .AddRemoteScheme<CustomRemoteAuthenticationOptions, CustomRemoteAuthenticationHandler>("CustomScheme", "Custom", options =>
        {
            options.AuthorizationEndpoint = "https://myapi.com/authorize";
            options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            options.SaveTokens = true;
            options.CallbackPath = "/mycallback";
        });
        

        您可以查看远程处理程序 OAuthHandlerOpenIdConnectHandler 作为实施您的指南。

        实现您自己的处理程序(和处理程序选项)可能既麻烦又不安全,因此您应该考虑第一个选项。

        【讨论】:

          猜你喜欢
          • 2023-04-05
          • 2015-01-02
          • 2019-06-19
          • 2017-11-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-07-03
          • 2017-10-26
          相关资源
          最近更新 更多