oauth2学习及dotnetopenauth部分源码研究docx.docx

上传人:b****6 文档编号:6901594 上传时间:2023-01-12 格式:DOCX 页数:27 大小:53.81KB
下载 相关 举报
oauth2学习及dotnetopenauth部分源码研究docx.docx_第1页
第1页 / 共27页
oauth2学习及dotnetopenauth部分源码研究docx.docx_第2页
第2页 / 共27页
oauth2学习及dotnetopenauth部分源码研究docx.docx_第3页
第3页 / 共27页
oauth2学习及dotnetopenauth部分源码研究docx.docx_第4页
第4页 / 共27页
oauth2学习及dotnetopenauth部分源码研究docx.docx_第5页
第5页 / 共27页
点击查看更多>>
下载资源
资源描述

oauth2学习及dotnetopenauth部分源码研究docx.docx

《oauth2学习及dotnetopenauth部分源码研究docx.docx》由会员分享,可在线阅读,更多相关《oauth2学习及dotnetopenauth部分源码研究docx.docx(27页珍藏版)》请在冰豆网上搜索。

oauth2学习及dotnetopenauth部分源码研究docx.docx

oauth2学习及dotnetopenauth部分源码研究docx

在上篇文章中我研究了OpenId及DotNetOpenAuth的相关应用,这一篇继续研究OAuth2.

     一.什么是OAuth2

     OAuth是一种开放认证协议,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用.数字2表示现在使用第2代协议.

 

     二.OAuth2中的角色

     OAuth2有四种角色

     resourceowner资源所有者:

比如twitter用户,他在twitter的数据就是资源,他自己就是这些资源的所有者。

     resourceserver资源服务器:

保存资源的服务器,别人要访问受限制的资源就要出示AccessToken(访问令牌)。

     client客户端:

一个经过授权后,可以代表资源所有者访问资源服务器上受限制资源的一方。

比如 开发者开发的应用。

     authorizationserver授权服务器:

对资源所有者进行认证,认证通过后,向客户端发放AccessToken(访问令牌)。

 

     三.认证过程

     用户访问客户端的网站,想操作自己存放在资源服务提供方的资源。

     客户端将用户引导至授权服务提供方的授权页面请求用户授权,在这个过程中将客户端的回调连接发送给授权服务提供方。

     用户在授权服务提供方的网页上输入用户名和密码,然后授权该客户端访问所请求的资源。

     授权成功后,授权服务提供方对客户端授予一个授权码,网站跳回至客户端。

     客户端获得授权码后,再次从授权服务提供方请求获取访问令牌。

     授权服务提供方根据授权码授予客户端访问令牌。

     客户端使用获取的访问令牌访问存放在资源服务提供方上的受保护的资源。

 

     四.获取访问令牌方式

     从上面可以看到,令牌是串起整个认证流程的核心.OAuth2有四种获取令牌的方式

     AuthorizationCode授权码方式:

这种是推荐使用的,也是最安全的.

     ImplicitGrant隐式授权:

相比授权码授权,隐式授权少了第一步的取AuthorizationCode的过程,而且不会返回refresh_token。

主要用于无服务器端的应用,比如 浏览器插件。

     ResourceOwnerPasswordCredentials资源所有者密码证书授权:

这种验证主要用于资源所有者对Client有极高的信任度的情况,比如操作系统或高权限程序。

只有在不能使用其它授权方式的情况下才使用这种方式。

     ClientCredentials客户端证书授权:

这种情况下Client使用自己的client证书(如client_id及client_secret组成的httpbasic验证码)来获取accesstoken,只能用于信任的client。

     本文主要讲解第一种获取方式.

     有能有些人有这样的疑问,为什么授权成功后不直接返回访问令牌,则是获取授权码,然后使用授权码去换访问令牌.这个问题的答案在官方的文档里,原因主要是保障数据安全性.当用户授权成功,浏览器从授权服务器返回客户端时,数据是通过QueryString传递的.如果直接返回访问令牌,则直接在地址栏可见,相关的日志系统也会记录,这会提高令牌被破解的风险.返回授权码,然后客户端通过直接通信使用授权码换取访问令牌,整个过程对用户是不可见的,这样大大提高了安全性.

 

     五.DotNetOpenAuth在OAuth2中的应用

     官方Sample内包含有OAuth的完整示例,其授权服务器使用Mvc编写,客户端与资源服务器使用WebForm编写,数据层使用了EF.为了更加贴进实际使用,减少无关杂音,本人模仿其重写了一个Sample,本文的讲解将围绕自行编写的Sample展开.Sample示例可于文后下载.

     1.客户端

     客户端编程主要围绕三个类展开

     AuthorizationServerDescription,顾名思义,用于对服务端的描述.如下所示

privatestaticAuthorizationServerDescriptionAuthServerDescription;privatestaticreadonlyWebServerClientClient;staticOAuth2Client(){AuthServerDescription=newAuthorizationServerDescription();AuthServerDescription.TokenEndpoint=newUri("http:

//localhost:

8301/OAuth/Token");AuthServerDescription.AuthorizationEndpoint=newUri("http:

//localhost:

8301/OAuth/Authorize");Client=newWebServerClient(AuthServerDescription,"sampleconsumer","samplesecret");}

     可以看到,主要设置其两个地址:

令牌获取地址与授权地址.然后将其作为参数来构建WebServerClient类.

     WebServerClient类,是OAuth2的客户端代理类,与授权服务器和资源服务器交互的方法都定义在上面.在实例化时需要传入AuthServerDescription对象,客户端名与客户端密码.这对名称与密码应该是事先向授权服务器申请的,用于标识每一个使用数据的客户端.各个客户端拥有各自的名称与密码.

     生成客户端代理后,第一件事就是应该访问授权服务器获取授权码.这主要由WebServerClient类的RequestUserAuthorization方法完成.

 

publicvoidRequestUserAuthorization(IEnumerablescope=null,UrireturnTo=null);

 

     在申请授权码时,还会向授权服务器发送申请权限的范围,参数名叫scope.一般都是一个Url地址.

     申请成功,授权服务器返回后,客户端需再次访问授权服务器申请访问令牌.这主要由WebServerClient类的ProcessUserAuthorization方法完成

publicIAuthorizationStateProcessUserAuthorization(HttpRequestBaserequest=null);

     成功申请后,会返回一个IAuthorizationState接口对象,其定义如下

stringAccessToken{get;set;}DateTime?

AccessTokenExpirationUtc{get;set;}DateTime?

AccessTokenIssueDateUtc{get;set;}UriCallback{get;set;}stringRefreshToken{get;set;}HashSetScope{get;}

     很好理解,AccessToken为访问令牌,RefreshToken为刷新令牌,AccessTokenIssueDateUtc为访问令牌生成时间,AccessTokenExpirationUtc为访问令牌过期时间,Callback为回调的Url,Scope为权限的范围,或者叫被授权可以访问的地址范围.

     在Sample中为了简化编程对框架作了二次封装,如下

 

1privatestaticAuthorizationServerDescriptionAuthServerDescription;23privatestaticreadonlyWebServerClientClient;45staticOAuth2Client()6{7AuthServerDescription=newAuthorizationServerDescription();8AuthServerDescription.TokenEndpoint=newUri("http:

//localhost:

8301/OAuth/Token");9AuthServerDescription.AuthorizationEndpoint=newUri("http:

//localhost:

8301/OAuth/Authorize");1011Client=newWebServerClient(AuthServerDescription,"sampleconsumer","samplesecret");12}1314privatestaticIAuthorizationStateAuthorization15{16get{return(AuthorizationState)HttpContext.Current.Session["Authorization"];}17set{HttpContext.Current.Session["Authorization"]=value;}18}1920publicstaticvoidGetUserAuthorization(stringscope)21{22GetUserAuthorization(newstring[]{scope});23}2425publicstaticvoidGetUserAuthorization(IEnumerablescopes)26{27if(Authorization!

=null)28{29return;30}3132IAuthorizationStateauthorization=Client.ProcessUserAuthorization();33if(authorization==null)34{35Client.RequestUserAuthorization(scopes);3637return;38}3940Authorization=authorization;41HttpContext.Current.Response.Redirect(HttpContext.Current.Request.Path);42}

 

     前12行为对象初始化,14到18行将获取的权限对象保存在Session中,属性名为Authorization.客户端使用GetUserAuthorization方法来获取对某地址访问授权.

     在页面中调用代码如下

if(!

IsPostBack){OAuth2Client.GetUserAuthorization("http:

//tempuri.org/IGetData/NameLength");}

     打开页面,首次调用GetUserAuthorization方法后,首先判断权限对象Authorization是否为空.不为空说明已获取到权限.为空则执行ProcessUserAuthorization方法获取访问令牌,由于此时没有授权码,则返回的权限对象为空.最后通过RequestUserAuthorization方法向授权服务器申请授权码.

     获取成功后,浏览器页面会刷新,在页面地址后追加了授权码.此时第二次执行GetUserAuthorization方法.权限对象Authorization仍然为空,但由于已有授权码,则ProcessUserAuthorization方法将向授权服务器申请访问令牌.获取成功后将返回的权限对象赋给Authorization属性,然后再次刷新本页面.注意,刷新地址使用的是HttpContext.Current.Request.Path,而此属性是不包括QueryString的.作用是将授权码从地址栏中去除.

     第三次执行GetUserAuthorization方法,由于权限对象Authorization已不为空,则直接返回.

     访问令牌默认是有时效的.当过期后,要么走上面三步重新申请一个令牌,不过更好的方法是使用刷新令牌刷新访问令牌.这主要由WebServerClient类的RefreshAuthorization方法完成

 

publicboolRefreshAuthorization(IAuthorizationStateauthorization,TimeSpan?

skipIfUsefulLifeExceeds=null);

 

     使用访问令牌的方式,是将令牌添加到访问资源服务器Http请求的头上,这主要由WebServerClient类的AuthorizeRequest方法完成

publicvoidAuthorizeRequest(HttpWebRequestrequest,IAuthorizationStateauthorization);publicvoidAuthorizeRequest(WebHeaderCollectionrequestHeaders,IAuthorizationStateauthorization);

     在Sample中针对Wcf请求作了二次封装,如下

1publicstaticTReturnUseService(Expression>operation)2{3if(Authorization.AccessTokenExpirationUtc.HasValue)4{5Client.RefreshAuthorization(Authorization,TimeSpan.FromMinutes

(2));6}78TServicechannel=newChannelFactory("*").CreateChannel();9IClientChannelclient=(IClientChannel)channel;1011HttpWebRequesthttpRequest=(HttpWebRequest)WebRequest.Create(client.RemoteAddress.Uri);12ClientBase.AuthorizeRequest(httpRequest,Authorization.AccessToken);13HttpRequestMessagePropertyhttpDetails=newHttpRequestMessageProperty();14httpDetails.Headers[HttpRequestHeader.Authorization]=httpRequest.Headers[HttpRequestHeader.Authorization];1516using(OperationContextScopescope=newOperationContextScope(client))17{18OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name]=httpDetails;1920client.Open();21TReturnresult=operation.Compile().Invoke(channel);22try23{24client.Close();25}26catch27{28client.Abort();29throw;30}3132returnresult;33}34}

     在请求一个Wcf前,首先判断有效期.如果少于2分钟则首先刷新访问令牌.之后构建一个HttpWebRequest对象,并使用AuthorizeRequest方法将访问令牌添加在请求头上.从第13行之后是Wcf的特定写法,其中13到18行表示将Http授权头赋给Wcf授权头.

 

     2.授权服务端

     服务端要做的事其实很好理解,就是记录某用户在某客户端的授权情况.其使用数据库来保存相关信息.Client表存储客户端,User表存储用户,ClientAuthorization表是张关系表,存储某用户在某客户端授予的权限.Nonce存储访问随机数,SymmertricCryptoKey表存储对称加密的密码.

     服务端主要围绕以下对象编程

     AuthorizationServer类,代表授权服务类.主要的功能都由它提供.IAuthorizationServerHost接口是编写验证逻辑的地方,由OAuth2AuthorizationServer类实现,ICryptoKeyStore是访问密码的接口,INonceStore是访问随机数的地方,这两个接口由DatabaseKeyNonceStore类实现,IClientDescription是描述客户端的接口,由Client实现.

     在本Sample中,OpenId与OAuth2是配合使用的.用户需要先去OpenId进行登录,然后去OAuth2进行授权.从这个意义上讲,OAuth2受OpenId的统一管理,是其一个客户端.

     AccountController是一个典型的OpenId客户端编程.上篇文章已有讲解,故不赘述.

     当客户端申请授权码时,首先执行OAuthController类的Authorize方法,如下,有删节

publicActionResultAuthorize(){varpendingRequest=this.authorizationServer.ReadAuthorizationRequest();if((this.authorizationServer.AuthorizationServerServicesasOAuth2AuthorizationServer).CanBeAutoApproved(pendingRequest)){varapproval=this.authorizationServer.PrepareApproveAuthorizationRequest(pendingRequest,HttpContext.User.Identity.Name);returnthis.authorizationServer.Channel.PrepareResponse(approval).AsActionResult();}database.AddParameter("@ClientIdentifier",pendingRequest.ClientIdentifier);ViewBag.Name=database.ExecuteScalar("selectnamefromClientwhereClientIdentifier=@ClientIdentifier").ToString();ViewBag.AuthorizationRequest=pendingRequest;returnView();}

     AuthorizationServer类的ReadAuthorizationRequest方法会获取用户请求并返回一个EndUserAuthorizationRequest对象,此对象定义如下

publicUriCallback{get;set;}publicstringClientIdentifier{get;set;}publicstringClientState{get;set;}publicvirtualEndUserAuthorizationResponseTypeResponseType{get;}publicHashSetScope{get;}

     可以看到包括了客户端的相关信息.然后将此对象传入OAuth2AuthorizationServer对像的CanBeAutoApproved方法,查看能否自动发放授权码.

publicboolCanBeAutoApproved(EndUserAuthorizationRequestauthorizationRequest){if(authorizationRequest.ResponseType==EndUserAuthorizationResponseType.AuthorizationCode){database.AddParameter("@ClientIdentifier",authorizationRequest.ClientIdentifier);objectresult=database.ExecuteScalar("selectClientSecretfromclientwhereClientIdentifier=@ClientIdentifier");if(result!

=null&&!

string.IsNullOrEmpty(result.ToString())){returnthis.IsAuthorizationValid(authorizationRequest.Scope,authorizationRequest.ClientIdentifier,DateTime.UtcNow,HttpContext.Current.User.Identity.Name);}}returnfalse;}

     此方法是查找数据库中有无此客户端记录且密码不为空,如果不为空且处于获取授权码阶段,则会调用了IsAuthorizationValid方法

privateboolIsAuthorizationValid(HashSetrequestedScopes,stringclientIdentifier,DateTimeissuedUtc,stringusername){issuedUtc+=TimeSpan.FromSeconds

(1);database.AddParameter("@ClientIdentifier",clientIdentifier);database.AddParameter("@CreatedOnUtc",issuedUtc);database.AddParameter("@Ex

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 外语学习 > 英语考试

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1