服务端API的OAuth认证实现

http://stackoverflow.com/questions/12499602/body-joints-angle-using-kinect?rq=1

新浪微博跟update相关的api已经挂了很多天了一直没有恢复正常,返回错误:40070 Error limited application access api!,新浪开放平台的论坛里n多的人都在等这个恢复,新浪官方也相当的恶心出问题了连个公告都没有,既不说什么原因又不说什么时候能恢复,。还是有版主说是api正在升级礼拜1恢复正常今天都礼拜2了还是不行。基于这个原因我的android版的新浪微博客户端已经停工好几天了,刚好是跟update相关的一些功能。

     客户端开发不成了,就自己做做服务端程序,提供类似新浪微博rest api服务, api其实说简单也很简单了,无法是通过链接对外提供json或者xml格式的数据和接收外部提供的数据进去相应的存储、删除、更新等操作。过程中碰到的最麻烦的问题就是OAuth认证功能了,在做android版的新浪微博客户端时候也花了蛮长的时间对OAuth认证进行研究,在客户端原先是采用oauth-signpost开源项目,后来由于某些原因就放弃了这个开源类库,自己重新写了OAuth认证部分的实现, 现在做服务端的OAuth认证,其实有过做客户端的经验做服务端也差不多,简单的说无非是客户端对参数字符串进行签名然后把签名值传输到服务端,服务端也对同样对参数字符串进行签名,把从客户端传过来的签名值进去比较,简单的说就这么个过程,具体实现肯定比这个要复杂多了,不明真相的同学可以google一下OAuth进行深入的学习研究了。

      服务端程序用asp.net和C#编写了而非java,理由很简单本人对.net更加熟悉。由于想快速的实现效果采用了oauth-dot-net开源项目并没有全部自己写。

      一、首先新建名为Rest Api的ASP.NET Web应用程序,然后添加 oauth-dot-net开源项目相关的几个dll(Castle.Core.dll、Castle.MicroKernel.dll、Castle.Windsor.dll、CommonServiceLocator.WindsorAdapter.dll、Microsoft.Practices.ServiceLocation.dll、OAuth.Net.Common.dll、OAuth.Net.Components.dll、OAuth.Net.ServiceProvider.dll)。

     二、在Web.config文件里添加相应的配置,具体可以参考OAuth.Net.Examples.EchoServiceProvider项目,然后在Global.asax.cs添加如下代码:

复制代码
public   override   void  Init()
        {
            IServiceLocator injector 
=
                
new  WindsorServiceLocator(
                    
new  WindsorContainer(
                        
new  XmlInterpreter(
                            
new  ConfigResource( " oauth.net.components " ))));

            ServiceLocator.SetLocatorProvider(() 
=>  injector);
复制代码

        }

      接下来是比较重要,就是request_token、authorize、access_token的实现,OAuth认证实现的几个过程,不理解可以看android开发我的新浪微博客户端-OAuth篇(2.1) ,具体代码实现很多是参考OAuth.Net.Examples.EchoServiceProvider示例项目。

      三、 首先新建ConsumerStore.cs类,用来存储Consumer信息,由于测试项目所以存储在内存中并没有考虑保存到数据库,真实项目的时候请把相应的Consumer信息保存到数据库中。Consumer信息对应新浪微博其实就是应用的App Key和App Secret,当开发者在新浪微博建一个新的应用获取App Key和App Secret,所以完整的应该还需要一个开发一个提供给第三方开发者申请获取App Key和App Secret的功能页面,这里就不具体实现,直接在代码里写死了一个名为测试应用的Consumer,App Key:2433927322,App Secret:87f042c9e8183cbde0f005a00db1529f,这个提供给客户端测试用。 具体代码如下:

复制代码
public   sealed   class  ConsumerStore : InMemoryConsumerStore, IConsumerStore
    {
        
internal   static   readonly  IConsumer FixedConsumer  =   new  OAuthConsumer( " 2433927322 " " 87f042c9e8183cbde0f005a00db1529f " " 测试应用 " , ConsumerStatus.Valid);

        
public  ConsumerStore()
        {
            
this .ConsumerDictionary.Add(
                ConsumerStore.FixedConsumer.Key, 
                ConsumerStore.FixedConsumer);
        }

        
public   override   bool  Add(IConsumer consumer)
        {
            
throw   new  NotSupportedException( " Consumers cannot be added to this store--it is fixed. " );
        }

        
public   override   bool  Contains( string  consumerKey)
        {
            
return  ConsumerStore.FixedConsumer.Key.Equals(consumerKey);
        }

        
public   override   bool  Update(IConsumer consumer)
        {
            
throw   new  NotSupportedException( " Consumers cannot be updated in this store--it is fixed. " );
        }

        
public   override   bool  Remove(IConsumer consumer)
        {
            
throw   new  NotSupportedException( " Consumers cannot be removed from this store--it is fixed. " );
        }
复制代码

    } 

      四、接下来就是request_token功能,新建RequestTokenHandler.cs ,这个是OAuth.Net.ServiceProvider.RequestTokenHandler子类,并且是httpHandlers所以需要在Web.config中添加httpHandlers配置,这个用来接收客户端程序的请求,返回给客户端程序Request Token和Request Secret用,具体代码如下:

复制代码
public   sealed   class  RequestTokenHandler : OAuth.Net.ServiceProvider.RequestTokenHandler
    {   
        
protected   override   void  IssueRequestToken(HttpContext httpContext, OAuthRequestContext requestContext)
        {
            
// 产生RequestToken
            IRequestToken token  =   this .GenerateRequestToken(httpContext, requestContext);

            requestContext.RequestToken 
=  token;
            Uri callbackUri;
            
if  (Uri.TryCreate(requestContext.Parameters.Callback, UriKind.Absolute,  out  callbackUri))
            {
                
if  ( ! ServiceProviderContext.CallbackStore.ContainsCallback(token))
                {
                    
// 保存Callback地址了
                    ServiceProviderContext.CallbackStore.AddCallback(token, callbackUri);
                }
            }
            
else
                OAuthRequestException.ThrowParametersRejected(
new   string [] { Constants.CallbackParameter },  " Not a valid Uri. " );


            
// 把token.Token和token.Secret输出到客户端,
            requestContext.ResponseParameters[Constants.TokenParameter]  =  token.Token;
            requestContext.ResponseParameters[Constants.TokenSecretParameter] 
=  token.Secret;
        }

        
protected   override  IRequestToken GenerateRequestToken(HttpContext httpContext, OAuthRequestContext requestContext)
        {
            
            
return  ServiceProviderContext.TokenGenerator.CreateRequestToken(requestContext.Consumer, requestContext.Parameters);
        }
复制代码

    } 

      五、 接着是authorize功能,新建名为authorize.aspx的页面,用来给用户输入账号和密码进行授权的页面,这个页面很简单具体如下图,在这个页面中获取用户输入的账户和密码跟数据库中存储的用户账号和密码进行验证,如果验证通过返回之前客户端提供的callback地址,并且给这个地址添加一个校验码,具体代码如下:

复制代码
public   partial   class  authorize : System.Web.UI.Page
    {
        
protected   void  Page_Load( object  sender, EventArgs e)
        {

        }

        
protected   void  Button1_Click( object  sender, EventArgs e)
        {
            
if  (loginName.Text  ==   " test "   &&  password.Text  ==   " 123 " )
            {
                
string  toke  =  Request.Params[ " oauth_token " ];
                IRequestToken tk 
=  ServiceProviderContext.TokenStore.GetRequestToken(toke);
                Uri callback 
=  ServiceProviderContext.CallbackStore.GetCalback(tk);
                
string  oauth_verifier  =  ServiceProviderContext.VerificationProvider.Generate(tk);
                Response.Redirect(callback.ToString() 
+   " ?oauth_verifier= "   +  oauth_verifier);
            }
            
        }
复制代码

    } 

     六、接下来就是access_token功能,新建AccessTokenHandler.cs , 这个是OAuth.Net.ServiceProvider.AccessTokenHandler子类,并且是httpHandlers所以需要在Web.config中添加httpHandlers配置,这个用来接收客户端程序的请求,返回给客户端程序Access Token和Access Secret用,具体代码如下:

复制代码
public   sealed   class  AccessTokenHandler : OAuth.Net.ServiceProvider.AccessTokenHandler
    {
        
protected   override   void  IssueAccessToken(HttpContext httpContext, OAuthRequestContext requestContext)
        {
            
// 产生access token
            IAccessToken accessToken  =   this .GenerateAccessToken(httpContext, requestContext);

            accessToken.Status 
=  TokenStatus.Authorized;

            
//  把accessToken和accessSecret输出到客户端,
            requestContext.ResponseParameters[Constants.TokenParameter]  =  accessToken.Token;
            requestContext.ResponseParameters[Constants.TokenSecretParameter] 
=  accessToken.Secret;
        }

        
protected   override  IAccessToken GenerateAccessToken(HttpContext httpContext,  OAuthRequestContext requestContext)
        {
            
return  ServiceProviderContext.TokenGenerator.CreateAccessToken(requestContext.Consumer, requestContext.RequestToken);
        }
    }

public   class  TokenGenerator : ITokenGenerator
    {
        
internal   static   readonly  IRequestToken FixedRequestToken  =   new  OAuthRequestToken( " requestkey " ,
            
" requestsecret " ,
            ConsumerStore.FixedConsumer,
            TokenStatus.Authorized,
            
null ,
            ServiceProviderContext.DummyIdentity,
            
new   string [] { });

        
internal   static   readonly  IAccessToken FixedAccessToken  =   new  OAuthAccessToken(
            
" accesskey " ,
            
" accesssecret " ,
            ConsumerStore.FixedConsumer,
            TokenStatus.Authorized,
            TokenGenerator.FixedRequestToken);

        
public  IRequestToken CreateRequestToken(IConsumer consumer, OAuthParameters parameters)
        {
            
return  TokenGenerator.FixedRequestToken;
        }

        
public  IAccessToken CreateAccessToken(IConsumer consumer, IRequestToken requestToken)
        {
            
return  TokenGenerator.FixedAccessToken;
        }
    }

public   class  TokenStore : InMemoryTokenStore, ITokenStore
    {
        
public  TokenStore()
        {
            
this .RequestTokenDictionary.Add(
                TokenGenerator.FixedRequestToken.Token,
                TokenGenerator.FixedRequestToken);

            
this .AccessTokenDictionary.Add(
                TokenGenerator.FixedAccessToken.Token,
                TokenGenerator.FixedAccessToken);
        }

        
public   override   bool  Add(IRequestToken token)
        {
            
throw   new  NotSupportedException( " Tokens cannot be added to the token store--it is fixed. " );
        }

        
public   override   bool  Add(IAccessToken token)
        {
            
throw   new  NotSupportedException( " Tokens cannot be added to the token store--it is fixed. " );
        }

        
public   override   bool  Update(IRequestToken token)
        {
            
throw   new  NotSupportedException( " Tokens cannot be updated in the token store--it is fixed. " );
        }

        
public   override   bool  Update(IAccessToken token)
        {
            
throw   new  NotSupportedException( " Tokens cannot be updated in the token store--it is fixed. " );
        }

        
public   override   bool  Remove(IRequestToken token)
        {
            
throw   new  NotSupportedException( " Tokens cannot be removed from the token store--it is fixed. " );
        }

        
public   override   bool  Remove(IAccessToken token)
        {
            
throw   new  NotSupportedException( " Tokens cannot be removed from the token store--it is fixed. " );
        }
复制代码

    } 

 这样就完成了一个最最简单小型的服务端OAuth认证,然后用android客户端进行测试ok通过。

     注意点:

     一、android模拟器访问本地服务地址为10.0.2.2,比如http://localhost:3423/authorize.aspx在模拟器中用http://10.0.2.2:3423/authorize.aspx。

     二、OAuth.Net类库的OAuth.Net.Common项目中的interface ICallbackStore 添加了一个Uri GetCalback(IRequestToken token);并且在具体的实现类InMemoryCallbackStore添加了实习代码:

          public Uri GetCalback(IRequestToken token)

        {
            lock (this.callbackStore)
            {
                if (this.callbackStore.ContainsKey(token))
                {
                    return this.callbackStore[token];
                }
                else
                {
                    return null;
                }
            }
        }
 

       三、为了能用我前面做的给新浪用的android客户端,对于类库源代码AccessTokenHandler的ParseParameters方法做了如下修改,因为新浪请求api的时候都会加一个source的参数:

            protected virtual void ParseParameters(HttpContext httpContext, OAuthRequestContext requestContext)

        {
            .......
            parameters.AllowOnly(
                    Constants.ConsumerKeyParameter,
                    Constants.TokenParameter,
                    Constants.SignatureMethodParameter,
                    Constants.SignatureParameter,
                    Constants.TimestampParameter,
                    Constants.NonceParameter,
                    Constants.VerifierParameter,
                    Constants.VersionParameter, // (optional)
                    Constants.RealmParameter, // (optional)
                    "source");
 
            ......
        }
 

 

你可能感兴趣的:(OAuth)