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

问题描述 投票:23回答:5

我有一个MVC 5 Web应用程序,可以使用Login.cshtml页面登录并获取cookie并且登录正常。但是,我想使用Web API进行登录,然后(可能)设置一个cookie,以便我登录我的MVC页面...(或使用MVC登录登录,然后访问Web API)但是web api返回一个bearer token而不是cookie令牌...所以这不起作用。有没有办法结合使用我的MVC页面和我的Web API页面的身份验证?

更新:

这不是一个真正的代码问题,而是一个概念问题。

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

另一方面,WebAPI调用检查名为Authorization的项目的请求标头...并使用该值来确定请求者标识。这是从WebAPI调用“/ Token”返回的。

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

我能想到的唯一方法是实际验证两次......一次是使用WebAPI调用,另一次是使用Login帖子。 (见下面的答案)。

这似乎非常hacky ...但我不明白授权代码足以知道是否有更合适的方法来实现这一点。

asp.net-mvc cookies asp.net-web-api bearer-token
5个回答
10
投票

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

我使用JWT与Web API和ASP.Net Identity构建了一个授权服务器,如here所述。

完成后,您的Web API 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

我在我想要使用相同令牌保护的所有其他API(资源服务器)中编写了一个消费逻辑(即中间件)。由于您希望在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站点中的[Authorize]属性和Web API将遵循此cookie。

附:我通过发布JWT(授权服务器或Auth和资源服务器)的Web API完成了这项工作,并且能够在ASP.Net MVC网站中使用它,在Angular中构建的SPA站点,在python(资源服务器)中构建的安全API,spring (资源服务器)和Android应用程序。


2
投票

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

对我来说似乎很讨厌......如果有一些方法可以使用持票令牌登录MVC,或者对WebApi的调用会返回给我一个可用于正常MVC页面请求的cookie,这将是很好的。

如果有人有更好的方式我会喜欢听到它。

这是我添加到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) {
                }
            });
        });
    });

1
投票

我假设你要做的是有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) {

0
投票

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

我有一个网络和一个API,所有内容用户。我不使用用户的身份来传递web和api。相反,我创建了一个单独的网络帐户,每次网络将使用此特殊帐户连接到api。

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

希望这对你有所帮助。


0
投票

根据您的上述评论,根据我的理解,您有一个场景,您可以通过浏览器执行登录,但也必须使用ajax调用来调用web-api方法。

浏览器调用是基于会话cookie的。虽然来自浏览器的ajax调用会在标头中包含会话cookie,但是需要为web-api提供身份验证标头以执行验证。

因此,在成功登录时,您还必须生成基于web-api的令牌,将其设置为cookie(可通过javascript访问),然后在进行ajax调用时,从cookie中获取并将其作为标头包含在你的'授权'标题。

© www.soinside.com 2019 - 2024. All rights reserved.