65.9K
CodeProject 正在变化。 阅读更多。
Home

通过社交网络进行 OWIN OAuth2 身份验证

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.85/5 (17投票s)

2015年2月8日

CPOL

5分钟阅读

viewsIcon

51661

downloadIcon

1971

Web API通过Facebook、Google、Microsoft支持的OWIN OAuth2中间件进行基于令牌的身份验证。从社交网络提取额外的用户信息。

引言

在本文中,您可以找到通过社交网络(Facebook,Google和Microsoft)验证用户的代码示例、演示和解释。如何提取有关用户的其他信息,例如头像、电子邮件和全名。

您可以在我的网站上查看演示并下载最新版本的代码:SupperSlonic.com

基本概念

Owin 中间件

Owin中间件模块负责处理与外部身份验证提供程序(如Facebook、Google等)的身份验证,并通过cookie建立应用程序会话。在所有后续调用中,应用程序cookie中间件提取传入应用程序cookie的内容并设置当前上下文的声明标识。

什么是令牌?

令牌是一个字符串值,代表一个加密的System.Security.Claims列表。您可以使用任何您喜欢的声明。我的应用程序有自己的在整个项目中使用的声明列表。我们称之为应用程序声明

应用程序声明由OWIN加密到颁发令牌中的定义良好的声明列表。每次服务收到令牌时,它都会尝试使用其IIS机器密钥解密它,将其还原为声明列表并填充User.Identity对象。

要确定它是外部持有者令牌还是本地令牌,它会检查声明的Issuer字段。对于本地令牌,它必须始终是ClaimsIdentity.DefaultIssuer
请注意,OWIN使用IIS机器密钥进行加密,因此您必须应用一些自定义解决方案才能在多个WEB服务之间使用相同的令牌。

我是如何构建我的应用程序声明列表的

private static ClaimsIdentity CreateIdentity(ClaimsMapper claimsMapper, string authenticationType)
{
	IList claims = new List();

	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 GetExternalLogin(string provider, string error = null)
{
	if (error != null)
	{
		return Redirect(Url.Content("~/") + "#error=" + Uri.EscapeDataString(error));
	}

	ExternalLoginProvider loginProvider;
	if (!Enum.TryParse(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();
}

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步有详细视图)

  1. 新用户通过外部提供程序授权: 在颁发外部持有者令牌后,用户可以使用此令牌在应用程序中注册,然后再次重新授权,以获取新令牌(本地持有者)。
    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;
    	}
    }
  2. 现有用户通过外部提供程序授权:
    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();
    	}
    }
  3. 现有用户通过登录/密码授权:
    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 配置

  1. 导航到Facebook开发者页面并输入您的Facebook凭据登录;
  2. 如果您尚未注册为Facebook开发者,请点击“注册为开发者”并按照说明进行注册;
  3. 我的应用选项卡下,点击+ 添加新应用按钮:Facebook new app
  4. 选择网站作为应用程序平台:Facebook app type
  5. 输入应用名称类别,然后点击创建应用
    这在Facebook上必须是唯一的。应用命名空间是您的应用将用于访问Facebook应用程序进行身份验证的URL的一部分(例如,https://apps.facebook.com/{应用命名空间})。如果您不指定应用命名空间,则将使用应用ID作为URL。应用ID是一个长系统生成的数字,您将在下一步中看到。
  6. 在页面的基本设置部分
    • 输入联系电子邮件
    • 输入将向Facebook发送请求的站点URL
    Facebook settings 请注意,只有您才能使用您注册的电子邮件别名进行身份验证。其他用户和测试帐户将无法注册。
    您可以在角色菜单下授予测试用户访问应用程序的权限。
    对于所有其他Facebook帐户,您的应用程序必须获得Facebook的批准。有关进一步说明,请查看状态与审核菜单。
  7. 要禁用应用的沙盒模式,请转到左侧的状态与审核菜单并选择Facebook settings

Google 配置

  1. 导航到Google开发者控制台
  2. 点击创建项目按钮并输入项目名称和ID(您可以使用默认值)。几秒钟后,新项目将创建成功,您的浏览器将显示新项目页面;
  3. 在左侧选项卡中,点击API和身份验证,然后点击同意屏幕
    • 输入电子邮件地址
    • 输入产品名称
    Google Consent screen
  4. 在左侧选项卡中,点击API和身份验证,然后点击API
    • 启用Google+ API以支持用户头像访问
    Google+ API enabled
  5. 在左侧选项卡中,点击API和身份验证,然后点击凭据
  6. 在OAuth下点击创建新的客户端ID
    • 创建客户端ID对话框中,将应用程序类型保持默认Web应用程序
    • 将授权JavaScript来源设置为服务的SSL URL,例如:https://supperslonic.com/
    • 将授权重定向URI设置为:https://supperslonic.com/signin-google
    Google Client Id
  7. AppIdApp Secret复制并粘贴到Google的Credentials.resx文件中。

Microsoft 配置

  1. 导航到Microsoft开发者帐户
  2. 点击创建应用程序引用;
  3. 基本信息中输入有效的应用程序名称服务URLMicrosoft Basic Info
  4. API设置中选择它是移动应用程序并输入有效的重定向URL
    • 请注意在您的重定向URL中添加signin-microsoft
    Microsoft API Settings
  5. 应用设置中,将AppIdApp Secret复制并粘贴到Microsoft的Credentials.resx文件中。
© . All rights reserved.