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

时间:2015-08-22 22:33:32

标签: asp.net-mvc cookies asp.net-web-api bearer-token

我有一个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,以确定请求者的身份。此cookie是通过调用ApplicationSignInManager.PasswordSignInAsync生成的。

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

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

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

这看起来非常h​​acky ......但我不明白授权代码是否足以知道是否有更合适的方法来完成此任务。

4 个答案:

答案 0 :(得分:10)

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

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

完成此操作后,您的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站点和Web API中的[Authorize]属性将尊重这个cookie。

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

答案 1 :(得分:2)

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

对我来说看起来相当hacky ...如果有一些方法可以使用持票令牌登录MVC,或者调用WebApi会给我一个可以正常使用的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) {
                }
            });
        });
    });

答案 2 :(得分:0)

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

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

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

希望这对你有所帮助。

答案 3 :(得分:0)

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

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

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