系列目录:
DotNetOpenAuth实践系列(源码在这里)
DotNetOpenAuth是OAuth2的.net版本,利用DotNetOpenAuth我们可以轻松的搭建OAuth2验证服务器,不废话,下面我们来一步步搭建验证服务器
本次搭建环境:
.net4.5.1 ,DotNetOpenAuth v5.0.0-alpha3,MVC5
一、环境搭建
1、新建一个空的VS解决方案
2、添加验证服务器项目,项目选择MVC,不要自带的身份验证
3、使用Nuget添加DotNetOpenAuth v5.0.0-alpha3
输入DotNetOpenAuth 安装DotNetOpenAuth v5.0.0-alpha3
添加完成后
二、编写DotNetOpenAuth 验证服务器关键代码,实现功能
1、添加AuthorizationServerConfiguration.cs
这里的配置是为了添加方便管理,其实可以不用这个类
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Security.Cryptography.X509Certificates; 5 using System.Web; 6 7 namespace IdefavAuthorizationServer.Code 8 { 9 /// <summary> 10 /// 验证服务器配置 11 /// </summary> 12 public class AuthorizationServerConfiguration 13 { 14 /// <summary> 15 /// 构造函数 16 /// </summary> 17 public AuthorizationServerConfiguration() 18 { 19 TokenLifetime = TimeSpan.FromMinutes(5); 20 } 21 22 /// <summary> 23 /// 签名证书 24 /// </summary> 25 public X509Certificate2 SigningCertificate { get; set; } 26 27 /// <summary> 28 /// 加密证书 29 /// </summary> 30 public X509Certificate2 EncryptionCertificate { get; set; } 31 32 /// <summary> 33 /// Token有效时间 34 /// </summary> 35 public TimeSpan TokenLifetime { get; set; } 36 } 37 }
2、实现IClientDescription接口
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 using DotNetOpenAuth.Messaging; 6 using DotNetOpenAuth.OAuth2; 7 8 namespace IdefavAuthorizationServer.Code 9 { 10 public class Client : IClientDescription 11 { 12 /// <summary> 13 /// 客户端名称client_id 14 /// </summary> 15 public string Name { get; set; } 16 17 /// <summary> 18 /// 客户端类型 19 /// </summary> 20 public int ClientType { get; set; } 21 22 /// <summary> 23 /// 回调URL 24 /// </summary> 25 public string Callback { get; set; } 26 27 public string ClientSecret { get; set; } 28 29 30 Uri IClientDescription.DefaultCallback 31 { 32 get { return string.IsNullOrEmpty(this.Callback) ? null : new Uri(this.Callback); } 33 } 34 35 36 ClientType IClientDescription.ClientType 37 { 38 get { return (ClientType)this.ClientType; } 39 } 40 41 42 bool IClientDescription.HasNonEmptySecret 43 { 44 get { return !string.IsNullOrEmpty(this.ClientSecret); } 45 } 46 47 48 bool IClientDescription.IsCallbackAllowed(Uri callback) 49 { 50 if (string.IsNullOrEmpty(this.Callback)) 51 { 52 // No callback rules have been set up for this client. 53 return true; 54 } 55 56 // In this sample, it's enough of a callback URL match if the scheme and host match. 57 // In a production app, it is advisable to require a match on the path as well. 58 Uri acceptableCallbackPattern = new Uri(this.Callback); 59 if (string.Equals(acceptableCallbackPattern.GetLeftPart(UriPartial.Authority), callback.GetLeftPart(UriPartial.Authority), StringComparison.Ordinal)) 60 { 61 return true; 62 } 63 64 return false; 65 } 66 67 68 bool IClientDescription.IsValidClientSecret(string secret) 69 { 70 return MessagingUtilities.EqualsConstantTime(secret, this.ClientSecret); 71 } 72 73 74 } 75 }
3、实现IAuthorizationServerHost接口
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Security.Cryptography; 5 using System.Web; 6 using DotNetOpenAuth.Messaging.Bindings; 7 using DotNetOpenAuth.OAuth2; 8 using DotNetOpenAuth.OAuth2.ChannelElements; 9 using DotNetOpenAuth.OAuth2.Messages; 10 11 namespace IdefavAuthorizationServer.Code 12 { 13 public class IdefavAuthorizationServerHost : IAuthorizationServerHost 14 { 15 /// <summary> 16 /// 配置 17 /// </summary> 18 private readonly AuthorizationServerConfiguration _configuration; 19 20 /// <summary> 21 /// 构造函数 22 /// </summary> 23 /// <param name="config"></param> 24 public IdefavAuthorizationServerHost(AuthorizationServerConfiguration config) 25 { 26 if (config != null) 27 _configuration = config; 28 } 29 30 /// <summary> 31 /// Token创建 32 /// </summary> 33 /// <param name="accessTokenRequestMessage"></param> 34 /// <returns></returns> 35 public AccessTokenResult CreateAccessToken(IAccessTokenRequest accessTokenRequestMessage) 36 { 37 var accessToken = new AuthorizationServerAccessToken(); 38 accessToken.Lifetime = _configuration.TokenLifetime;//设置Token的有效时间 39 40 // 设置加密公钥 41 accessToken.ResourceServerEncryptionKey = 42 (RSACryptoServiceProvider)_configuration.EncryptionCertificate.PublicKey.Key; 43 // 设置签名私钥 44 accessToken.AccessTokenSigningKey = (RSACryptoServiceProvider)_configuration.SigningCertificate.PrivateKey; 45 46 var result = new AccessTokenResult(accessToken); 47 return result; 48 } 49 50 public IClientDescription GetClient(string clientIdentifier) 51 { 52 // 这里需要去验证客户端发送过来的client_id 53 if (string.Equals(clientIdentifier, "idefav", StringComparison.CurrentCulture))// 这里为了简明起见没有使用数据库 54 { 55 var client=new Client 56 { 57 Name = "idefav", 58 ClientSecret = "1", 59 ClientType = 1 60 }; 61 return client; 62 } 63 throw new ArgumentOutOfRangeException("clientIdentifier"); 64 } 65 66 public bool IsAuthorizationValid(IAuthorizationDescription authorization) 67 { 68 return true; 69 } 70 71 public AutomatedUserAuthorizationCheckResponse CheckAuthorizeResourceOwnerCredentialGrant(string userName, string password, 72 IAccessTokenRequest accessRequest) 73 { 74 throw new NotImplementedException(); 75 } 76 77 public AutomatedAuthorizationCheckResponse CheckAuthorizeClientCredentialsGrant(IAccessTokenRequest accessRequest) 78 { 79 AutomatedUserAuthorizationCheckResponse response = new AutomatedUserAuthorizationCheckResponse(accessRequest, true, "test"); 80 return response; 81 } 82 83 public ICryptoKeyStore CryptoKeyStore { get; } 84 public INonceStore NonceStore { get; } 85 86 87 } 88 }
4、实现OAuthController
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Threading.Tasks; 5 using System.Web; 6 using System.Web.Mvc; 7 using DotNetOpenAuth.Messaging; 8 using DotNetOpenAuth.OAuth2; 9 using IdefavAuthorizationServer.Code; 10 11 namespace IdefavAuthorizationServer.Controllers 12 { 13 public class OAuthController : Controller 14 { 15 private readonly AuthorizationServer authorizationServer = 16 new AuthorizationServer(new IdefavAuthorizationServerHost(Common.Configuration)); 17 18 public async Task<ActionResult> Token() 19 { 20 var response = await authorizationServer.HandleTokenRequestAsync(Request); 21 return response.AsActionResult(); 22 } 23 } 24 }
5、初始化AuthorizationServerConfiguration
这里采用Windows签名证书
放到项目中
制作证书事注意:要加上-a sha1 -sky exchange
到此,基本代码就写完了,现在说说要注意的地方,OAuth2默认设置的请求是要求SSL的也就是必须是https//localhost:1111/OAuth/Token,然后我们现在不需要使用SSL加密请求,更改一下WebConfig文件
在WebConfig里面设置成如图中那样,就可以不用https访问了
6、我们F5运行项目
使用Post工具发送Post请求访问 http://localhost:53022/OAuth/token
Body参数:
1 client_id:idefav 2 client_secret:1 3 grant_type:client_credentials
请求结果:
这样我们就拿到了access_token,通过这个access_token我们就可以访问资源服务器了
更新:
OAuthController代码添加内容类型
1 using System.Collections.Generic; 2 using System.Linq; 3 using System.Threading.Tasks; 4 using System.Web; 5 using System.Web.Mvc; 6 using System.Web.Script.Services; 7 using DotNetOpenAuth.Messaging; 8 using DotNetOpenAuth.OAuth2; 9 using IdefavAuthorizationServer.Code; 10 11 namespace IdefavAuthorizationServer.Controllers 12 { 13 public class OAuthController : Controller 14 { 15 private readonly AuthorizationServer authorizationServer = 16 new AuthorizationServer(new IdefavAuthorizationServerHost(Common.Configuration)); 17 18 public async Task<ActionResult> Token() 19 { 20 var response = await authorizationServer.HandleTokenRequestAsync(Request); 21 Response.ContentType = response.Content.Headers.ContentType.ToString(); 22 return response.AsActionResult(); 23 } 24 } 25 }
鉴于有人不知道Windows签名制作,下篇我们一起来看看如何制作一个认证服务器可以使用的签名证书