asp.net-mvc - 如何设置 MVC 请求以使用 Oauth 2.0 访问令牌?
问题描述
我正在开发一个 Asp.net Web API + MVC 项目。在创建项目时,我选择了“个人用户帐户”选项进行身份验证。登录后,提供者发出访问令牌,我在 ajax 调用的 HTTP 标头中使用此令牌,它适用于 Ajax 调用。我也有用于导航到不同页面的 MVC 操作。这些操作使用 System.web.MVC 的 [Authorize] 属性进行保护。导航到这些操作授权失败,我被重定向到登录页面。如何配置 Oauth 2.0 访问令牌以用于非 ajax 请求(MVC 请求)。
启动.Auth.cs:
public partial class Startup
{
public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
public static string PublicClientId { get; private set; }
// For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context and user manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseCookieAuthentication(new CookieAuthenticationOptions {
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Home/Login"),
LogoutPath = new PathString("/Account/LogOff"),
ExpireTimeSpan = TimeSpan.FromHours(1.0),
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Configure the application for OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
AllowInsecureHttp = true // dont use this for production,
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
}
WebApiConfig.cs:
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Configure Web API to use only bearer token authentication.
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
ApplicationOAuthProvider.cs:
public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
{
private readonly string _publicClientId;
public ApplicationOAuthProvider(string publicClientId)
{
if (publicClientId == null)
{
throw new ArgumentNullException("publicClientId");
}
_publicClientId = publicClientId;
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager,
OAuthDefaults.AuthenticationType);
ClaimsIdentity cookiesIdentity = await user.GenerateUserIdentityAsync(userManager,
CookieAuthenticationDefaults.AuthenticationType);
AuthenticationProperties properties = CreateProperties(user.UserName);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(cookiesIdentity);
}
public override Task TokenEndpoint(OAuthTokenEndpointContext context)
{
foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
{
context.AdditionalResponseParameters.Add(property.Key, property.Value);
}
return Task.FromResult<object>(null);
}
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
// Resource owner password credentials does not provide a client ID.
if (context.ClientId == null)
{
context.Validated();
}
return Task.FromResult<object>(null);
}
public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
{
if (context.ClientId == _publicClientId)
{
Uri expectedRootUri = new Uri(context.Request.Uri, "/");
if (expectedRootUri.AbsoluteUri == context.RedirectUri)
{
context.Validated();
}
}
return Task.FromResult<object>(null);
}
public static AuthenticationProperties CreateProperties(string userName)
{
IDictionary<string, string> data = new Dictionary<string, string>
{
{ "userName", userName }
};
return new AuthenticationProperties(data);
}
}
解决方案
ASP.Net MVC 是一种服务器端 Web 应用程序技术,并且 [Authorize] 操作期望接收 cookie 而不是检查令牌。
不确定这是否可以覆盖,并且在这种类型的解决方案中,您通常最终不得不处理 cookie / 令牌 / 服务器端代码 / 客户端代码的丑陋组合。
更现代/更清洁的分离是开发单页应用程序 + REST API。代码往往更简单,感觉更适合您提到的要求。
要了解我的意思,可以快速浏览此示例中的 UI 代码: https ://github.com/gary-archer/oauth.websample1
如果你感兴趣的话,repo 会链接到一些博客文章。
推荐阅读
- apache-storm - Storm UI 数据 - 隐藏系统统计信息/显示系统统计信息
- java - 使用方法使用文件行输入制作字符串数组
- google-admin-sdk - 谷歌工作区 API 的访问被拒绝
- arrays - 为什么我无法在我的搜索函数中投射 void 指针?
- javascript - Three.js:Safari Mac 中没有出现的场景
- javascript - 如何清空由javascript填充的div标签
- json - 错误解析错误:Google 富测试结果中缺少“}”或对象成员名称
- react-native - 使用 firestore 时出现以下错误错误:FIRESTORE (8.1.2) INTERNAL ASSERTION FAILED: Unexpected state
- python - Python Data Frame Matplotlib - 绘制 x 轴时间格式问题
- python - 在python中将时间序列数据从一列重新采样为多列