使用MVC核心身份从Web API验证MVC网站用户

时间:2019-11-29 21:49:44

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

我有两个.net core 2.2项目;第一个是MVC项目,类似于用户可以登录的表示层,另一个是用于处理数据库操作的Web API。我想在Web API中处理登录请求,并使用MVC Core Identity将登录结果返回到MVC项目。我的DbContext在Web API项目中。反正有根据Web API请求的结果创建身份cookie的问题?

4 个答案:

答案 0 :(得分:2)

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

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

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

答案 1 :(得分:0)

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

<tr>
    <td align="left" valign="top">
        <div style="height: 33px; line-height: 33px; font-size: 31px;">&nbsp;</div><div style="height: 75px; line-height: 75px; font-size: 73px;">&nbsp;</div>


            &lt;div style=&#34;text-align:center;float: left;display: inline-block;margin-left: 10px;font-family: arial,sans,sans-serif&#34;&gt;&lt;table border=1&gt;&lt;tr&gt;&lt;td&gt;Locked Proxy&lt;/td&gt;&lt;td&gt;Fail&lt;/td&gt;&lt;tr&gt;&lt;td&gt;Final Proxy&lt;/td&gt;&lt;td&gt;Fail&lt;/td&gt;&lt;tr&gt;&lt;td&gt;Final Script&lt;/td&gt;&lt;td&gt;Fail&lt;/td&gt;&lt;tr&gt;&lt;td&gt;PM&lt;/td&gt;&lt;td&gt;Fail&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;span&gt;&lt;div style=&#34;text-align:center;float: left;display: inline-block;margin-left: 10px;font-family: arial,sans,sans-serif&#34;&gt;&lt;table border=1&gt;&lt;tr&gt;&lt;td&gt;ME&lt;/td&gt;&lt;td&gt;Fail&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;span&gt;&lt;div style=&#34;text-align:center;float: left;display: inline-block;margin-left: 10px;font-family: arial,sans,sans-serif&#34;&gt;&lt;table border=1&gt;&lt;tr&gt;&lt;td&gt;Locked Script&lt;/td&gt;&lt;td&gt;Fail&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;span&gt;                           

    </td>
</tr>

然后在您的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;
            }
        };                
    });

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

然后在您的网络api->

[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();
}

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

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

答案 2 :(得分:0)

1。只有两项服务

如果您的系统只有两个服务(正面和背面),则可以使用Cookies进行正面的所有身份验证方案,并使用api进行用户验证。

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

ConfigureServices:

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");
    // ...
}

您的身份提供者ConfigureServices:

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作为实施您的指南。

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

答案 3 :(得分:0)

我不确定是否完全了解您的问题陈述。我假设您正在采用某种机制,使用户能够将其身份从网络客户端一直传递到您的DbContext。 我还假设您然后实际上不对MVC应用程序上的用户进行身份验证,并且基本上将他们的请求代理到WebAPI上。

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

然后,MVC项目自然会借助会话状态获得令牌,而您要做的就是将它与您提出的每个WebAPI请求一起传递。