在云那方

首页 新随笔 联系 订阅 管理
本篇介绍如何开放OAuth授权,并控制服务端数据访问。
先看一下图:



这两天事太多,文章整理的断断续续委屈

OK,步入正题,这里还是要借力: DevDefined.OAuth 框架。它提供了客户端访问,服务端管理Token的基础功能。

1. OAuthChannel
定义了服务端用户模型,OAuth的拦截器,OAuthWebServiceHostFactory(继承于WebServiceHostFactory,用于添加拦截器),以及 RequestToken 和 AccessToken 保持在内存里的容器及存取类 (InMemoryTokenRepository,InMemoryTokenStore)

OAuthWebServiceHostFactory 添加拦截器,使用了 WebServiceHost2 (Microsoft.ServiceModel.Web.dll 里,是 Microsoft 发布的WCF REST Starter Kit的一部分)
WebServiceHost2 重写了 ServiceHost 里 OnOpening 方法添加拦截器。WebServiceHost2的源代码猛击这里
OAuthWebServiceHostFactory:

  1. <pre name="code" class="csharp">using System;  
  2. using System.ServiceModel.Activation;  
  3. using System.ServiceModel.Web;  
  4. using Microsoft.ServiceModel.Web;  
  5. using DevDefined.OAuth.Provider;  
  6. using OAuthChannel.Repositories;  
  7.   
  8. namespace OAuthChannel  
  9. {  
  10.     public class OAuthWebServiceHostFactory : WebServiceHostFactory  
  11.     {  
  12.         public IOAuthProvider OAuthProvider { getset; }  
  13.         public ITokenRepository<OAuthChannel.Models.AccessToken> AccessTokenRepository { getset; }  
  14.   
  15.         protected override System.ServiceModel.ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)  
  16.         {  
  17.             var serviceHost = new WebServiceHost2(serviceType, true, baseAddresses);  
  18.             var interceptor = new OAuthChannel.OAuthInterceptor(OAuthProvider, AccessTokenRepository);  
  19.             serviceHost.Interceptors.Add(interceptor);  
  20.             return serviceHost;  
  21.         }  
  22.     }  
  23. }  

 

 

拦截器(OAuthInterceptor.cs)将请求的 OAuth (Request Header中) 转换成 OAuthChannel.Models.AccessToken

  1. public class AccessToken : TokenBase  
  2. {  
  3.     public string UserName { getset; }  
  4.     public string[] Roles { getset; }  
  5.     public DateTime ExpireyDate { getset; }  
  6. }  

 



2. OAuth WCF Rest Service
首先创建一个 WCF Rest Service:

定义一个基础数据模型,供Sample访问:
  1. namespace OAuthWcfRestService  
  2. {  
  3.     public class Contact  
  4.     {  
  5.         public int Id { getset; }  
  6.         public string Name { getset; }  
  7.         public string Email { getset; }  
  8.         public string Owner { getset; }  
  9.     }  
  10.   
  11.     public class DataModel  
  12.     {  
  13.         public static List<Contact> Contacts;  
  14.   
  15.         static DataModel()  
  16.         {  
  17.             Contacts = new List<Contact> {  
  18.               new Contact(){ Id=0, Name="Felix", Email="Felix@test.com", Owner = "jane" },  
  19.               new Contact(){ Id=1, Name="Wendy", Email="Wendy@test.com", Owner = "jane"},  
  20.               new Contact(){ Id=2, Name="John", Email="John@test.com", Owner = "john"},  
  21.               new Contact(){ Id=3, Name="Philip", Email="Philip@mail.com", Owner = "john"}  
  22.             };  
  23.         }  
  24.     }  
  25. }  
Contacts 中的数据只有属于 Owner 的“用户”才可以访问,因此 OAuthService 中实现如下:
  1. namespace OAuthWcfRestService  
  2. {  
  3.     [ServiceContract]  
  4.     [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]  
  5.     [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]  
  6.     public class OAuthService  
  7.     {  
  8.         [WebGet(UriTemplate = "Contacts")]  
  9.         public List<Contact> Contacts()  
  10.         {  
  11.             var name = Thread.CurrentPrincipal.Identity.Name;  
  12.             return DataModel.Contacts.Where(c => c.Owner == name).ToList();  
  13.         }          
  14.     }  
  15. }  
上面的 name 从 Thread.CurrentPrincipal.Identity.Name 而来,即访问当前服务的客户端ID。这个ID是由OAuth服务的拦截器(Interceptor)实现由 AccessToken(String) 转换成服务端用户模型。

在 web.config 中,利用 WCF 对 ASP.NET 的兼容机制,使用 Form 认证:定义了两个用户:john 和 jane
  1. <system.web>  
  2.   <compilation debug="true" targetFramework="4.0" />  
  3.   <authentication mode="Forms">  
  4.     <forms loginUrl="Pages/Login.aspx" cookieless="UseUri">  
  5.       <credentials passwordFormat="Clear">  
  6.         <user name="john" password="password"/>  
  7.         <user name="jane" password="password"/>  
  8.       </credentials>  
  9.     </forms>  
  10.   </authentication>  
  11.   <authorization>  
  12.     <allow users="*"/>  
  13.   </authorization>  
  14. </system.web>  

并修改 Global.asax 的 WebServiceHostFactory,改为 OAuthWebServiceHostFactory 
  1. public class Global : HttpApplication  
  2. {  
  3.     void Application_Start(object sender, EventArgs e)  
  4.     {  
  5.         RegisterRoutes();  
  6.     }  
  7.   
  8.     private void RegisterRoutes()  
  9.     {  
  10.         var oauthWebServiceHostFactory = new OAuthChannel.OAuthWebServiceHostFactory   
  11.         {   
  12.               AccessTokenRepository = OAuthServicesLocator.AccessTokenRepository,  
  13.               OAuthProvider = OAuthServicesLocator.Provider   
  14.         };  
  15.         RouteTable.Routes.Add(new ServiceRoute("OAuthService", oauthWebServiceHostFactory, typeof(OAuthService)));  
  16.     }  
  17. }  
作为一个基本的OAuth授权服务,我们还需要提供:
1. 获取 RequestToken 的服务
2. 获取 AccessToken 的服务
RequestToken.ashx :返回 RequestToken
  1. <pre name="code" class="csharp">using System;  
  2. using System.Web.UI;  
  3. using DevDefined.OAuth.Framework;  
  4. using DevDefined.OAuth.Provider;  
  5.   
  6. namespace OAuthWcfRestService  
  7. {  
  8.     public partial class RequestToken : System.Web.IHttpHandler  
  9.     {  
  10.         public bool IsReusable  
  11.         {  
  12.             get { return true; }  
  13.         }  
  14.   
  15.         public void ProcessRequest(System.Web.HttpContext context)  
  16.         {  
  17.             IOAuthContext oauthContext = new OAuthContextBuilder().FromHttpRequest(context.Request);  
  18.             IOAuthProvider provider = OAuthManager.Provider;  
  19.             IToken token = provider.GrantRequestToken(oauthContext);  
  20.             context.Response.Write(token);  
  21.             context.Response.End();  
  22.         }  
  23.     }  
  24. }  
AccessToken.ashx :交换 RequestToken 返回 AccessToken 
  1. using System;  
  2. using System.Web.UI;  
  3. using DevDefined.OAuth.Framework;  
  4. using DevDefined.OAuth.Provider;  
  5.   
  6. namespace OAuthWcfRestService  
  7. {  
  8.     public partial class AccessToken : System.Web.IHttpHandler  
  9.     {  
  10.         public bool IsReusable  
  11.         {  
  12.             get { return true; }  
  13.         }  
  14.   
  15.         public void ProcessRequest(System.Web.HttpContext context)  
  16.         {  
  17.             IOAuthContext oauthContext = new OAuthContextBuilder().FromHttpRequest(context.Request);  
  18.             IOAuthProvider provider = OAuthManager.Provider;  
  19.             IToken accessToken = provider.ExchangeRequestTokenForAccessToken(oauthContext);  
  20.             context.Response.Write(accessToken);  
  21.             context.Response.End();  
  22.         }  
  23.     }  
  24. }  
当然我们还需要提供用户登录和授权的页面:Login.aspx 和 UserAuthorize.aspx Form登录就不累述了, UserAuthorize.aspx 中实现授权的方法如下:
  1. private void ApproveRequestForAccess(string tokenString)  
  2. {             
  3.     OAuthChannel.Models.RequestToken requestToken = RequestTokenRepository.GetToken(tokenString);  
  4.     var accessToken = new OAuthChannel.Models.AccessToken  
  5.                           {  
  6.                               ConsumerKey = requestToken.ConsumerKey,  
  7.                               Realm = requestToken.Realm,  
  8.                               Token = Guid.NewGuid().ToString(),  
  9.                               TokenSecret = Guid.NewGuid().ToString(),  
  10.                               UserName = HttpContext.Current.User.Identity.Name,  
  11.                               ExpireyDate = DateTime.Now.AddMinutes(1),  
  12.                               Roles = new string[] { }  
  13.                           };  
  14.     AccessTokenRepository.SaveToken(accessToken);  
  15.     requestToken.AccessToken = accessToken;  
  16.     RequestTokenRepository.SaveToken(requestToken);  
  17. }  

3. 应用
Default.aspx 发起请求获取RequestToken,授权成功后回调 Callback.ashx
  1. namespace OAuthConsumerSample  
  2. {  
  3.     public partial class _Default : Page  
  4.     {  
  5.         protected void oauthRequest_Click(object sender, EventArgs e)  
  6.         {  
  7.         OAuthSession session = OAuthSessionFactory.CreateSession();  
  8.             IToken requestToken = session.GetRequestToken();  
  9.             if (string.IsNullOrEmpty(requestToken.Token))  
  10.             {  
  11.                 throw new Exception("The request token was null or empty");  
  12.             }  
  13.             Session[requestToken.Token] = requestToken;  
  14.             string callBackUrl = "http://localhost:" + HttpContext.Current.Request.Url.Port + "/Callback.ashx";  
  15.             string authorizationUrl = session.GetUserAuthorizationUrlForToken(requestToken, callBackUrl);  
  16.             Response.Redirect(authorizationUrl, true);  
  17.         }  
  18.     }  
  19. }  
Callback.ashx
  1. namespace OAuthConsumerSample  
  2. {  
  3.     public partial class Callback : System.Web.IHttpHandler, System.Web.SessionState.IRequiresSessionState  
  4.     {  
  5.         public void ProcessRequest(System.Web.HttpContext context)  
  6.         {  
  7.             var session = OAuthSessionFactory.CreateSession();  
  8.             string requestTokenString = context.Request["oauth_token"];  
  9.             var requestToken = (IToken)context.Session[requestTokenString];  
  10.             IToken accessToken = session.ExchangeRequestTokenForAccessToken(requestToken);  
  11.             context.Session[requestTokenString] = null;  
  12.             context.Session[accessToken.Token] = accessToken;  
  13.             context.Response.Redirect("ViewData.ashx?oauth_token=" + accessToken.Token);  
  14.         }  
  15.   
  16.         public bool IsReusable  
  17.         {  
  18.             get { return true; }  
  19.         }  
  20.     }  
  21. }  
posted on 2011-11-25 12:47  Rich.T  阅读(1028)  评论(3编辑  收藏  举报