• 下载OwinAuthenticationExample.zip - 420.6 KB
介绍
在本文中,您可以找到有关如何通过社交网络(Facebook,Google和Microsoft)对用户进行身份验证的代码,演示和说明示例。如何提取有关用户的其他信息,如头像,电子邮件和全名。
基本概念
Owin中间件
该Owin中间件模块负责处理与外部供应商的身份验证(如Facebook,谷歌等)的认证(authentication),并通过一个cookie建立应用会话。在所有后续调用中,应用程序cookie中间件提取传入应用程序cookie的内容并设置当前上下文的声明标识(claims identity)。
什么是令牌(token)?
令牌是表示System.Security.Claims加密列表的字符串值。您可以使用任何您喜欢的声明。我的应用程序在项目中使用了自己定义的声明列表。我们称之为应用程序声明(Application Claims)。
应用程序声明(Application Claims)是由OWIN加密到已颁发令牌中的明确定义的声明列表。每次服务收到令牌时,它都会尝试使用其IIS机器密钥解密其为声明列表并填充User.Identity对象。
要确定它是外部承载令牌(External Bearer token)还是本地令牌,它会检查声明的颁发者(Issuer)字段。对于本地令牌来说,它必须始终是ClaimsIdentity.DefaultIssuer。
注意,对于使用IIS机器密钥加密OWIN,这就是为什么必须应用一些自定义解决方案来跨多个WEB服务使用相同的令牌。
以下是我构建应用程序声明列表的方法:
private static ClaimsIdentity CreateIdentity(ClaimsMapper claimsMapper, string authenticationType)
{
IList<claim> claims = new List<claim>();
claims.Add(new Claim(ClaimTypes.NameIdentifier, claimsMapper.Id, null, claimsMapper.Issuer, claimsMapper.OriginalIssuer));
claims.Add(new Claim(ClaimTypes.Email, claimsMapper.Email, null, claimsMapper.Issuer, claimsMapper.OriginalIssuer));
claims.Add(new Claim(ClaimTypes.GivenName, claimsMapper.FullName, null, claimsMapper.Issuer, claimsMapper.OriginalIssuer));
claims.Add(new Claim(ClaimTypes.Sid, claimsMapper.Sid, null, claimsMapper.Issuer, claimsMapper.OriginalIssuer));
claims.Add(new Claim(ClaimTypes.Version, claimsMapper.Version, null, claimsMapper.Issuer, claimsMapper.OriginalIssuer));
claims.Add(new Claim(ClaimTypeIsVerified, claimsMapper.IsVerified, null, claimsMapper.Issuer, claimsMapper.OriginalIssuer));
claims.Add(new Claim(ClaimTypeAvatarUrl, claimsMapper.AvatarUrl, null, claimsMapper.Issuer, claimsMapper.OriginalIssuer));
return new ClaimsIdentity(claims, authenticationType);
}
所以,我总是可以从我的WEB服务发出的任何令牌中获取所有这些信息。ClaimsMapper是一种抽象策略,其知道如何将不同的数据模型映射到我的声明列表。
认证流程
OAuth2身份认证
应用程序如何从Facebook,Google等获取用户信息?
从外部提供商(Facebook,Google等)收到的有关用户的所有信息都在外部cookie中加密。
以下是应用程序与外部提供程序之间的通信流程的说明:
// GET api/Account/ExternalLogin
[AllowAnonymous]
[HostAuthentication(DefaultAuthenticationTypes.ExternalCookie)] //authenticated by external provider
[HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)] //refresh token support
[Route("externalLogin", Name = "externalLogin")]
public async Task<ihttpactionresult> GetExternalLogin(string provider, string error = null)
{
if (error != null)
{
return Redirect(Url.Content("~/") + "#error=" + Uri.EscapeDataString(error));
}
ExternalLoginProvider loginProvider;
if (!Enum.TryParse<externalloginprovider>(provider, ignoreCase: true, result: out loginProvider) ||
loginProvider == ExternalLoginProvider.None)
{
//Unsupported login provider
return InternalServerError();
}
if (!User.Identity.IsAuthenticated)
{
return new ChallengeResult(loginProvider, this);
}
ExternalLoginModel externalLogin = ExternalLoginModel.FromIdentity(User.Identity as ClaimsIdentity);
if (externalLogin == null)
{
return InternalServerError();
}
if (externalLogin.Provider != loginProvider)
{
Request.GetOwinContext().Authentication.SignOut(
DefaultAuthenticationTypes.ExternalCookie,
OAuthDefaults.AuthenticationType,
CookieAuthenticationDefaults.AuthenticationType);
return new ChallengeResult(loginProvider, this);
}
User user = await this.UserProvider.FindAsync(externalLogin.Provider, externalLogin.ProviderKey);
if (user != null)
{
OwinHelper.SingIn(Request.GetOwinContext(), user, externalLogin);
}
else
{
OwinHelper.SingIn(Request.GetOwinContext(), externalLogin);
}
return Ok();
}
</externalloginprovider></ihttpactionresult>
Facebook用户的数据提取:
public class FacebookOAuthProvider : FacebookAuthenticationProvider
{
private const string ApiBaseUrl = "https://graph.facebook.com";
public override Task Authenticated(FacebookAuthenticatedContext context)
{
string avatarUrl = GetAvatarUrl(context.User.GetValue("id").ToString(), 240);
context.Identity.AddClaim(
new Claim(OwinHelper.ClaimTypeAvatarUrl, avatarUrl));
return base.Authenticated(context);
}
public static string GetAvatarUrl(string facebookUserId, int size)
{
return string.Format("{0}/{1}/picture?width={2}&height={2}",
ApiBaseUrl,
facebookUserId,
size);
}
}
Google用户的数据提取:
public class GoogleOAuthProvider : GoogleOAuth2AuthenticationProvider
{
public override Task Authenticated(GoogleOAuth2AuthenticatedContext context)
{
string avatarUrl = context.User
.SelectToken("image.url")
.ToString()
.Replace("sz=50", "sz=240");
context.Identity.AddClaim(
new Claim(OwinHelper.ClaimTypeAvatarUrl, avatarUrl));
return base.Authenticated(context);
}
}
Microsoft用户的数据提取:
public class MicrosoftOAuthProvider : MicrosoftAccountAuthenticationProvider
{
public override void ApplyRedirect(MicrosoftAccountApplyRedirectContext context)
{
context = new MicrosoftAccountApplyRedirectContext(
context.OwinContext,
context.Options,
context.Properties,
context.RedirectUri + "&display=touch"); //Mobile devices support
base.ApplyRedirect(context);
}
public override Task Authenticated(MicrosoftAccountAuthenticatedContext context)
{
string avatarUrl = string.Format("https://apis.live.net/v5.0/{0}/picture",
context.User.GetValue("id").ToString());
context.Identity.AddClaim(
new Claim(OwinHelper.ClaimTypeAvatarUrl, avatarUrl));
return base.Authenticated(context);
}
}
令牌发行
一旦用户通过身份认证,应用程序就会有三个不同的流程来发布令牌(步骤10中的详细视图):
- 一个新的用户是通过认证的外部供应商:
一个外部承载令牌发出后,用户可以使用此令牌的应用程序注册,然后再重新认证,获取新的令牌(本地承载)。
public class NotRegisteredExternal : ClaimsMapper
{
public NotRegisteredExternal(ExternalLoginModel extLogin)
{
this.Id = string.Empty;
this.Email = extLogin.Email ?? string.Empty;
this.FullName = extLogin.FullName ?? string.Empty;
this.AvatarUrl = extLogin.AvatarUrl ?? string.Empty;
this.Sid = extLogin.ProviderKey;
this.Version = string.Empty;
this.IsVerified = false.ToString();
this.Issuer = extLogin.Provider.ToString();
this.OriginalIssuer = this.Issuer;
}
}
- 一个现有的用户是通过认证的外部供应商:
public class RegisteredExternal : ClaimsMapper
{
public RegisteredExternal(User user, ExternalLoginModel extLogin)
{
this.Id = user.Id.ToString();
this.Email = user.Email;
this.FullName = user.FullName ?? string.Empty;
this.AvatarUrl = UserProvider.GetAvatarUrl(user);
this.Sid = extLogin.ProviderKey;
this.Version = this.GetVersion(user.TimeStamp);
this.IsVerified = user.IsVerified.ToString();
this.Issuer = ClaimsIdentity.DefaultIssuer;
this.OriginalIssuer = extLogin.Provider.ToString();
}
}
- 一个现有用户通过认证登录/密码:
public class RegisteredLocal : ClaimsMapper
{
public RegisteredLocal(User user)
{
this.Id = user.Id.ToString();
this.Email = user.Email;
this.FullName = user.FullName ?? string.Empty;
this.AvatarUrl = UserProvider.GetAvatarUrl(user);
this.Sid = string.Empty;
this.Version = this.GetVersion(user.TimeStamp);
this.IsVerified = user.IsVerified.ToString();
this.Issuer = ClaimsIdentity.DefaultIssuer;
this.OriginalIssuer = ClaimsIdentity.DefaultIssuer;
}
}
向外部提供商注册您的应用程序
Facebook配置
- 导航到Facebook开发者页面并输入您的Facebook凭据登录;
- 如果您尚未注册为Facebook开发人员,请单击“注册为开发人员”并按照说明进行注册;
- 在我的应用程序选项卡下,单击+添加新应用程序按钮:
- 选择一个网站作为应用程序平台:
- 输入应用程序名称和类别,然后单击“创建应用程序”。
这在Facebook上必须是独一无二的。该应用程序命名空间(App Namespace)为您的应用程序将用于访问进行身份认证的Facebook应用程序的URL的一部分(例如,https://apps .facebook .com/{App命名空间})。如果未指定应用程序命名空间(App Namespace),则应用程序ID将用于URL。该应用程序ID(App ID)是一个长期的系统生成的编号,你将在下一步看到。 - 在页面的基本设置部分:
- 输入联系电邮 ;
- 输入将向Facebook发送请求的站点URL。
请注意,只有您才能使用已注册的电子邮件别名进行身份认证。其他用户和测试帐户将无法注册。
您可以授予测试用户对“角色”菜单下的应用程序的访问权限。
对于所有其他Facebook帐户,您的申请必须由Facebook 批准。如需进一步说明,请查看状态和复查(Status & Review )菜单。
- 要为您的应用禁用沙盒模式,请转到左侧的状态和复查(Status & Review )菜单,然后选择是:
Google配置
- 导航到Google Developers Console ;
- 单击“创建项目”按钮并输入项目名称和ID(您可以使用默认值)。几秒钟后,将创建新项目,浏览器将显示新项目页面;
- 在左侧选项卡中,单击“API和身份认证”,然后单击同意屏幕(Consent screen):
- 输入电邮地址 ;
- 输入产品名称:
- 在左侧选项卡中,单击“API和身份认证”,然后单击“API”:
- 启用Google+ API以支持用户的头像访问权限:
- 在左侧选项卡中,单击“API和身份认证”,然后单击“凭据”。
- 点击OAuth下的新建客户ID:
- 在“创建客户端ID”对话框中,保留应用程序类型的默认Web应用程序;
- 将授权的JavaScript源设置为服务的SSL URL,例如:https://supperslonic.com/ ;
- 将授权重定向URI设置为:https://supperslonic.com/ signin-google。
- 将AppId和App Secret复制并粘贴到Google 的Credentials.resx文件中。
Microsoft配置
- 导航到Microsoft Developer Account ;
- 按创建应用程序引用;
- 在基本信息中输入有效的应用程序名称服务的网址:
- 在API设置中,选择它是移动应用程序并输入有效的重定向URL:
- 请注意将signin-microsoft添加到重定向URL。
- 在应用程序设置中,将AppId和App Secret复制并粘贴到Microsoft 的Credentials.resx文件中。
原文地址:https://www.codeproject.com/Articles/873598/OWIN-OAuth-authentication-via-Social-networks