结合使用 MVC 页面和 Web API 页面的身份验证?

Combine the use of authentication both for MVC pages and for Web API pages?

我有一个 MVC 5 网络应用程序,可以使用 Login.cshtml 页面登录并获取 cookie,登录工作正常。但是,我想使用 Web API 登录,然后(也许)设置一个 cookie,以便我登录我的 MVC 页面...(或使用 MVC 登录然后访问 Web API) 但是网络 api returns 是不记名令牌而不是 cookie 令牌...所以这是行不通的。有没有办法为我的 MVC 页面和我的 Web API 页面结合使用身份验证?

更新:

这不是真正的代码问题,更多的是概念问题。

普通 MVC 网页会检查名为“.AspNet.ApplicationCookie”的 cookie,以确定请求者的身份。此 cookie 是通过调用 ApplicationSignInManager.PasswordSignInAsync.

生成的 另一方面,

WebAPI 调用检查请求 headers 是否存在名为 Authorization... 的项目,并使用该值来确定请求者的身份。这是从对“/Token”的 WebAPI 调用返回的。

这些是非常不同的值。我的网站需要同时使用 MVC 页面 WebAPI 调用(以动态更新这些页面)...并且都需要经过身份验证才能执行它们的任务。

我能想到的唯一方法是实际验证两次...一次是通过 WebAPI 调用,另一次是通过登录 post。 (见下面我的回答)。

这看起来很 hacky...但我对授权码的理解不够,不知道是否有更合适的方法来完成此操作。

我和你有类似的情况,但我使用不同的方式进行身份验证。

我有一个网站和一个api,都是为内网用户准备的。我不使用用户的身份来传递网络和 api。相反,我创建了一个个人网络帐户,每次网络都会使用这个特殊帐户连接到 api。

因为,我们还需要确保用户不应该直接连接到 api。他们应该只连接到网络 ui。

希望对您有所帮助。

Ugg...我必须做的是使用 Login.cshtml 表单并覆盖提交...进行 Ajax 调用以获取 WebApi 承载令牌...然后执行表单提交以获取实际的 MVC cookie。所以,我实际上发出了两个登录请求...一个用于 WebApi 令牌,另一个用于 MVC cookie。

对我来说似乎很老套...如果有某种方法可以使用不记名令牌登录 MVC 就好了...或者调用 WebApi 会 return 我一个 cookie我可以将其用于正常的 MVC 页面请求。

如果有人有更好的方法,我很想听听。

这是我添加到 Login.cshtml:

的脚本代码
    $(document).ready(function () {
        $('form:first').submit(function (e) {
            e.preventDefault();
            var $form = $(this);
            var formData = $form.serializeObject(); // https://github.com/macek/jquery-serialize-object
            formData.grant_type = "password";
            $.ajax({
                type: "POST",
                url: '@Url.Content("~/Token")',
                dataType: "json",
                data: formData, // seems like the data must be in json format
                success: function (data) {
                    sessionStorage.setItem('token', data.access_token);
                    $form.get(0).submit(); // do the actual page post now
                },
                error: function (textStatus, errorThrown) {
                }
            });
        });
    });

我假设您想要做的是让 MVC 服务的页面具有 javascript 调用 Web API 方法。如果您正在使用 ASP.NET Identity 来处理身份验证(看起来您正在这样做),那么 MVC 应该使用可以传递给 Web API 进行身份验证的 OAuth 令牌。

下面是一些 javascript 代码的片段,在类似情况下对我有用:

var token = sessionStorage.getItem('access_token');
var headers = {};
if (token) {
    headers.Authorization = 'Bearer ' + token;
}
$.ajax({
    type: <GET/POSt/...>,
    url: <your api>,
    headers: headers
}).done(function (result, textStatus) {

实现此目的的最佳方法是在您的 MVC 项目中拥有授权服务器(Web API 生成令牌)和令牌消费中间件。 IdentityServer 应该有帮助。但是我是这样做的:

我使用 JWT 与 Web API 和 ASP.Net Identity 构建了一个授权服务器,如解释的那样 here

完成后,您的网站 APIs startup.cs 将如下所示:

 // Configures cookie auth for web apps and JWT for SPA,Mobile apps
 private void ConfigureOAuthTokenGeneration(IAppBuilder app)
 {
    // Configure the db context, user manager and role manager to use a single instance per request
    app.CreatePerOwinContext(ApplicationDbContext.Create);
    app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
    app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);

    // Cookie for old school MVC application
    var cookieOptions = new CookieAuthenticationOptions
    {
        AuthenticationMode = AuthenticationMode.Active,
        CookieHttpOnly = true, // JavaScript should use the Bearer
        AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,                
        LoginPath = new PathString("/api/Account/Login"),
        CookieName = "AuthCookie"
    };
    // Plugin the OAuth bearer JSON Web Token tokens generation and Consumption will be here
    app.UseCookieAuthentication(cookieOptions);

    OAuthServerOptions = new OAuthAuthorizationServerOptions()
    {
        //For Dev enviroment only (on production should be AllowInsecureHttp = false)
        AllowInsecureHttp = true,
        TokenEndpointPath = new PathString("/oauth/token"),
        AccessTokenExpireTimeSpan = TimeSpan.FromDays(30),
        Provider = new CustomOAuthProvider(),                
        AccessTokenFormat = new CustomJwtFormat(ConfigurationManager.AppSettings["JWTPath"])
    };

    // OAuth 2.0 Bearer Access Token Generation
    app.UseOAuthAuthorizationServer(OAuthServerOptions);
}

您可以找到 CustomOAuthProviderCustomJwtFormathere.

我在我想使用相同令牌保护的所有其他 APIs(资源服务器)中编写了消费逻辑(即中间件)。由于你想在你的MVC项目中使用Web生成的令牌API,在实现授权服务器后,你需要做以下事情:

在您的 MVC 应用程序中,将此添加到 startup.cs:

public void Configuration(IAppBuilder app)
{
        ConfigureOAuthTokenConsumption(app);
}

private void ConfigureOAuthTokenConsumption(IAppBuilder app)
{
    var issuer = ConfigurationManager.AppSettings["AuthIssuer"];
    string audienceid = ConfigurationManager.AppSettings["AudienceId"];
    byte[] audiencesecret = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["AudienceSecret"]);

    app.UseCookieAuthentication(new CookieAuthenticationOptions { CookieName = "AuthCookie" , AuthenticationType=DefaultAuthenticationTypes.ApplicationCookie });

    //// Api controllers with an [Authorize] attribute will be validated with JWT
    app.UseJwtBearerAuthentication(
        new JwtBearerAuthenticationOptions
        {
            AuthenticationMode = AuthenticationMode.Passive,
            AuthenticationType = "JWT",
            AllowedAudiences = new[] { audienceid },
            IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
            {
                new SymmetricKeyIssuerSecurityTokenProvider(issuer, audiencesecret)                           
            }

        });
}

在您的 MVC 控制器中,当您收到令牌时,将其反序列化并从访问令牌生成一个 cookie:

AccessClaims claimsToken = new AccessClaims();
claimsToken = JsonConvert.DeserializeObject<AccessClaims>(response.Content);
claimsToken.Cookie = response.Cookies[0].Value;               
Request.Headers.Add("Authorization", "bearer " + claimsToken.access_token);
var ctx = Request.GetOwinContext();
var authenticateResult = await ctx.Authentication.AuthenticateAsync("JWT");
ctx.Authentication.SignOut("JWT");
var applicationCookieIdentity = new ClaimsIdentity(authenticateResult.Identity.Claims, DefaultAuthenticationTypes.ApplicationCookie);
ctx.Authentication.SignIn(applicationCookieIdentity);

生成机器密钥并将其添加到 Web API 和 ASP.Net MVC 站点的 web.config 中。

这样,将创建一个 cookie,并且 MVC 站点和 Web API 中的 [Authorize] 属性将接受此 cookie。

P.S. 我已经通过 Web API 发布 JWT(授权服务器或 Auth & 资源服务器)完成了此操作并且能够使用它在 ASP.Net MVC 网站中,内置 Angular 的 SPA 站点,内置 python(资源服务器)的安全 APIs,spring(资源服务器)和Android应用

根据您上面的评论,据我了解,您有一个场景,其中您通过浏览器执行登录,但还必须使用 ajax 调用来调用 web-api 方法。

浏览器调用基于 session-cookie。虽然来自浏览器的 ajax 调用会在 header 中包含 session cookie,但需要的是 header 的身份验证 web-api执行验证。

因此,在成功登录后,您还必须生成一个基于 web-api 的令牌,将其设置为 cookie(可由 javascript 访问),然后在创建 ajax 调用,从 cookie 中提取它并将其作为 header 包含在您的 'Authorization' header.