将身份验证同时用于 MVC 页面和 Web API 页面?
Posted
技术标签:
【中文标题】将身份验证同时用于 MVC 页面和 Web API 页面?【英文标题】:Combine the use of authentication both for MVC pages and for Web API pages? 【发布时间】:2015-11-16 15:28:16 【问题描述】:我有一个 MVC 5 Web 应用程序,可以使用 Login.cshtml 页面登录并获取 cookie,并且登录工作正常。但是,我想使用 Web API 登录,然后(也许)设置一个 cookie,以便我登录我的 MVC 页面......(或使用 MVC 登录然后访问 Web API)但是web api返回一个不记名令牌而不是cookie令牌......所以这不起作用。有没有一种方法可以为我的 MVC 页面和我的 Web API 页面结合使用身份验证?
更新:
这不是真正的代码问题,更多的是概念问题。
普通 MVC 网页会检查一个名为“.AspNet.ApplicationCookie”的 cookie,默认情况下,以确定请求者的身份。此 cookie 是通过调用 ApplicationSignInManager.PasswordSignInAsync 生成的。
另一方面,WebAPI 调用会检查名为 Authorization... 的项目的请求标头,并使用该值来确定请求者的身份。这是从对“/Token”的 WebAPI 调用返回的。
这些是非常不同的值。我的网站需要同时使用 MVC 页面和 WebAPI 调用(以动态更新这些页面)...并且两者都需要经过身份验证才能执行其任务。
我能想到的唯一方法是实际进行两次身份验证...一次使用 WebAPI 调用,另一次使用登录帖子。 (见下面我的回答)。
这似乎很hacky...但我对授权代码的了解不够,无法知道是否有更合适的方法来完成此操作。
【问题讨论】:
请出示代码 【参考方案1】:实现这一点的最佳方法是在您的 MVC 项目中拥有一个授权服务器(一个生成令牌的 Web API)和令牌消费中间件。 IdentityServer 应该会有所帮助。但是我已经这样做了:
按照here 的说明,我使用 JWT 和 Web API 和 ASP.Net Identity 构建了一个授权服务器。
完成此操作后,您的 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);
您可以找到CustomOAuthProvider
和CustomJwtFormat
类here。
我在我想要使用相同令牌保护的所有其他 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。
PS 我已经通过发布 JWT(授权服务器或身份验证和资源服务器)的 Web API 完成了此操作,并且能够在 ASP.Net MVC 网站、Angular 内置的 SPA 站点中使用它,在 python(资源服务器)、spring(资源服务器)和 android 应用程序中内置的安全 API。
【讨论】:
我认为有一个错字:app.UseCookieAuthentication(new CookieAuthenticationOptions());
应该是私有方法 ConfigureOAuthTokenGeneration 中的app.UseCookieAuthentication(cookieOptions);
是否可以使用外部身份验证提供程序对 Microsoft Azure AD 进行身份验证(例如,调用 app.UseWindowsAzureActiveDirectoryBearerAuthentication)?【参考方案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)
);
);
);
【讨论】:
【参考方案3】:我假设您尝试做的是让 MVC 提供的页面具有调用 Web API 方法的 javascript。如果您使用 ASP.NET Identity 来处理身份验证(看起来您正在这样做),那么 MVC 应该使用可以传递给 Web API 进行身份验证的 OAuth 令牌。
这是一些 JavaScript 代码的 sn-p,它在类似情况下对我有用:
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)
【讨论】:
你的描述完全正确......但问题是你如何以及在哪里获得 access_token,首先存储在 sessionStorage 中......以及你在哪里以及如何获得用于 MVC 页面身份验证的 cookie。 访问令牌由 MVC 框架存储在 sessionStorage 中,我的代码不必将其存储在那里。如果您的代码没有发生这种情况,请发布您如何配置 ASP.NET Identity/OAuth。【参考方案4】:我和你有类似的情况,但我使用不同的方式进行身份验证。
我有一个 web 和一个 api,它们都适用于 Intranet 用户。我不使用用户的身份来传递 web 和 api。相反,我创建了一个单独的 web 帐户,每次 web 都会使用这个特殊帐户连接到 api。
因为,我们还需要确保用户不应该直接连接到 api。他们应该只连接到 web ui。
希望对您有所帮助。
【讨论】:
【参考方案5】:从上面的 cmets 中,据我了解,您有一个场景,您通过浏览器执行登录,但还必须使用 ajax 调用调用 web-api 方法。
浏览器调用是基于会话 cookie 的。虽然来自浏览器的 ajax 调用将在标头中包含会话 cookie,但需要存在身份验证标头以供 web-api 执行验证。
因此,在成功登录时,您还必须生成一个基于 web-api 的令牌,将其设置为 cookie(可通过 javascript 访问),然后在进行 ajax 调用时,从 cookie 中获取并包含它作为“授权”标题中的标题。
【讨论】:
以上是关于将身份验证同时用于 MVC 页面和 Web API 页面?的主要内容,如果未能解决你的问题,请参考以下文章
身份验证/授权 MVC 5 和 Web API - Katana/Owin
同时对 Asp.Net core 2.0 MVC 应用程序 (cookie) 和 REST API (JWT) 进行身份验证
如何使用 Web API 来对 MVC 应用程序进行身份验证