OAuth认证

来源:互联网 发布:淘宝网手机版 编辑:程序博客网 时间:2024/05/01 23:24

OAuth认证:

1.      Oauth是一种安全认证的协议;

2.      Oauth协议为用户资源的授权提供了一个安全的,开放而又简易的标准;

3.      Oauth的授权不会使第三方触及到用户的账信息

OAuth当中的角色

1.      Service Provider

2.      User

3.      Comsumer

4.      Protected Resources

OAuth演示图

 

     第一组:(App Key和App Secret),创建新应用时,新浪开发平台分配给应用App KeyApp Secret

      第二组:(RequestToken和Request Secret)

      第三组:(oauth_verifier)

      第四组:(user_id、Access Token和Access Secret) 

     新浪微博的OAuth认证过程,当用户第一次使用本客户端软件时,客户端程序用第一组作为参数向新浪微博发起请求,然后新浪微博经过验证后返回第二组参数给客户端软件同时表示新浪微博信任本客户端软件;

(获取服务端信任的过程)

当客户端软件获取第二组参数时作为参数引导用户浏览器跳至新浪微博的授权页面 然后用户在新浪的这个授权页面里输入自己的微博账号和密码进行授权,完成授权后根据客户端设定的回调地址把第三组参数返回给客户端软件并表示用户也信任本客户端软件

(获取用户授权的过程)

接下来客户端软件把第二组参数和第三组参数作为参数再次向新浪微博发起请求,然后新浪微博返回第四组参数给客户端软件,第四组参数需要好好的保存起来这个就是用来代替用户的新浪账号和密码用的,在后面调用api时都需要。

(获取通行证)

从这个过程来看用户只是在新浪微博的认证网页输入过账户和密码并没有在客户端软件里输入过账户和密码,客户端软件只保存了第四组数据并没有保存用户的账户和密码,这样有效的避免了账户和密码透露给新浪微博之外的第三方应用程序, 保证 了安全性。

      本项目用为了方便开发采用了oauth-signpost开源项目进行OAuth认证开发,新建OAuth.java 类文件对OA进行简单的封装,OAuth类主要有RequestAccessToken、GetAccessToken、SignRequest三个方 法,第一个方法RequestAccessToken就是上面过程中用来获取第三组参数用的,GetAccessToken方法是用来获取第四组参数用,SignRequest方法是用来调用api用。由于采用了oauth-signpost开源项目简单了很多。具体代码如下:

代码

public class OAuth {
    
private CommonsHttpOAuthConsumer httpOauthConsumer;
    
private OAuthProvider httpOauthprovider;
    
public String consumerKey;//AppKey
    
public String consumerSecret;//AppSecret
   
    //
客户端请求服务端信任,获取Request_token及相应密钥
    public static String URL_OAUTH_TOKEN ="http://api.t.sina.com.cn/oauth/request_token";
    //获取用户授权,同意授权后返回oauth_verifier
    public static String URL_AUTHORIZE= "http://api.t.sina.com.cn/oauth/authorize";
   //
获取用户信息及通行证(user_id、Access Token和Access Secret
    public static String URL_ACCESS_TOKEN ="http://api.t.sina.com.cn/oauth/access_token";

    
public OAuth()
    {    
        
// 第一组:(App Key和App Secret)
        this("3315495489","e2731e7grf592c0fd7fea32406f86e1b");
    }
    
public OAuth(String consumerKey,String consumerSecret)
    {
        
this.consumerKey=consumerKey;
        
this.consumerSecret=consumerSecret;
    
  //构造函数中初始化httpOauthConsumer、httpOauthprovider 对象
       httpOauthConsumer = new CommonsHttpOAuthConsumer(consumerKey,consumerSecret);
        httpOauthprovider = newCommonsHttpOAuthProvider(URL_OAUTH_TOKEN,URL_ACCESS_TOKEN,
               URL_AUTHORIZE);
    }
    
    
public Boolean RequestAccessToken(Activity activity,String callBackUrl){
        Boolean ret=
false;
        
try{
       //   httpOauthConsumer = 
new CommonsHttpOAuthConsumer(consumerKey,consumerSecret);
       //  
httpOauthprovider = new DefaultOAuthProvider(URL_OAUTH_TOKEN,URL_ACCESS_TOKEN,
URL_AUTHORIZE);
(注意1,这里我改用CommonsHttpOAuthProvider)

            String authUrl = httpOauthprovider.retrieveRequestToken(httpOauthConsumer, callBackUrl);
            activity.startActivity(
new Intent(Intent.ACTION_VIEW, Uri.parse(authUrl)));

            ret=
true;

        }
catch(Exception e){
        }
        
return ret;
    }
    
    
public UserInfo GetAccessToken(Intent intent){
        UserInfo user=
null;
        Uri uri = intent.getData();
        String verifier = uri.getQueryParameter(oauth.signpost.OAuth.OAUTH_VERIFIER);
        
try {
            httpOauthprovider.setOAuth10a(
true); 
            httpOauthprovider.retrieveAccessToken(httpOauthConsumer,verifier);
        } 
catch (OAuthMessageSignerException ex) {
            ex.printStackTrace();
        } 
catch (OAuthNotAuthorizedException ex) {
            ex.printStackTrace();
        } 
catch (OAuthExpectationFailedException ex) {
            ex.printStackTrace();
        } 
catch (OAuthCommunicationException ex) {
            ex.printStackTrace();
        }
        SortedSet<String> user_id= httpOauthprovider.getResponseParameters().get("user_id");
        String userId=user_id.first();
        String userKey = httpOauthConsumer.getToken();
        String userSecret = httpOauthConsumer.getTokenSecret();
        user=
new UserInfo();
        user.setUserId(userId);
        user.setToken(userKey);
        user.setTokenSecret(userSecret);
        
return user;
    }
    
    
public HttpResponse SignRequest(String token,String tokenSecret,String url,List params)
    {
        HttpPost post = 
new HttpPost(url);
        
//HttpClient httpClient = null;
        try{
            post.setEntity(
new UrlEncodedFormEntity(params,HTTP.UTF_8));
        } 
catch (UnsupportedEncodingException e) {
             e.printStackTrace();
        }
      
  注意2
       
 //关闭Expect:100-Continue握手,100-Continue握手需谨慎使用,因为遇到不支持HTTP/1.1协议的服务器或者代理时会引起问题
        post.getParams().setBooleanParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE, false);
        
return SignRequest(token,tokenSecret,post);
    }
    
    
public HttpResponse SignRequest(String token,String tokenSecret,HttpPost post){
       // httpOauthConsumer = 
new CommonsHttpOAuthConsumer(consumerKey,consumerSecret);
        httpOauthConsumer.setTokenWithSecret(token,tokenSecret);
        HttpResponse response = 
null;
        
try {
            httpOauthConsumer.sign(post);
        } 
catch (OAuthMessageSignerException e) {
            e.printStackTrace();
        } 
catch (OAuthExpectationFailedException e) {
            e.printStackTrace();
        } 
catch (OAuthCommunicationException e) {
            e.printStackTrace();
        }
        
//取得HTTP response
        try {
            response = 
new DefaultHttpClient().execute(post);
        } 
catch (ClientProtocolException e) {
            e.printStackTrace();
        } 
catch (IOException e) {
            e.printStackTrace();
        }
        
return response;
    }
}
CommonsHttpOAuthConsumer类继承于AbstractOAuthConsumer,支持signing HTTP requests of type。
AbstractOAuthConsumer是抽象类,可以理解为一个存储认证用户信息及相关参数的基类,它有三个子类:


其中
DefaultOAuthConsumer类,默认的OAuth consumer的实现类. Only supports signing
 * {@link java.net.HttpURLConnection} type requests.
CommonsHttpOAuthConsumer类位于signpost-commonshttp4-1.2.1.1.jar包,主要Supports signing HTTP requests of type。{@linkorg.apache.http.HttpRequest}.
JettyOauthConsumer类位于signpost-jetty6-1.2.1.1.jar包,未找到相关源码说明。

构造函数:
AbstractOAuthConsumer(String consumerKey,String consumerSecret)

常用方法:
String getConsumerKey()
String getConsumerSecret()
String getToken()
String getTokenSecret()

public void setTokenWithSecret(String token, String tokenSecret)

public HttpRequest sign(Object request)

                throws OAuthMessageSignerException,

                       OAuthExpectationFailedException,

                       OAuthCommunicationException

Signs the given HTTP request by writing an OAuthsignature (and other required OAuth parameters) to it. Where these parametersare written depends on the currentSigningStrategy.

CommonsHttpOAuthProvider类继承于AbstractOAuthProvider,使用 the Apache Commons {@link HttpClient} 4.x HTTP方式来获取Oauth Token.
DefaultOAuthProvider使用HttpURLConnection type GET requeststo receive tokens from a service provider.
AbstractOAuthProvider是抽象类,它有两个子类:


构造函数:
AbstractOAuthProvider(String requestTokenEndpointUrl,String accessTokenEndpointUrl,String authorizationWebsiteUrl)

公共方法:

public String retrieveRequestToken(OAuthConsumer consumer,

                                   String callbackUrl)

                            throws OAuthMessageSignerException,

                                   OAuthNotAuthorizedException,

                                   OAuthExpectationFailedException,

                                   OAuthCommunicationException

注意:必须确保OAuthConsumer中已经设置了有效的consumer key and consumer secret,同时含有 未经过授权的request token and token secret 。

返回值:用户重定向到授权页的URL地址,其中包含未经授权的Request Token。
The URL to which the user must be sent in order to authorize the consumer. Itincludes the unauthorized request token 。

public void setOAuth10a(boolean isOAuth10aProvider)设置为true,表示支持OAuth1.0版本。注意你必须在调用retrieveRequestToken() 和        retrieveAccessToken()

 之间重建一个provider object对象时,使用此方法。
public void retrieveAccessToken(OAuthConsumer consumer,

                               String oauthVerifier)

                        throws OAuthMessageSignerException,

                                OAuthNotAuthorizedException,

                                OAuthExpectationFailedException,

                                OAuthCommunicationException
说明:Queries the service provider for anaccess token.
注意: 必须确保OAuthConsumer中已经设置了有效的consumerkey and consumer secret,同时含有 未经过授权的 request token andtoken secret 。
参数:consumer - the OAuthConsumer that should be used tosign the request
     oauthVerifier - NOTE: Only applies to serviceproviders implementing OAuth

        1.0a. Setto null if the service provider is still using OAuth

        1.0. The verification code issued by the service provider

        after thethe user has granted the consumer authorization. If the

        callbackmethod provided in the previous step was

        OAuth.OUT_OF_BAND,then you must ask the user for this

        value. Ifyour app has received a callback, the verfication code

        was passedas part of that request instead

protected String getResponseParameter(String key)  Returns asingle query parameter as served by the service provider in a

 token reply. Youmust call setResponseParameters(oauth.signpost.http.HttpParameters)with the set of ameters before using this method.

注意1:在Android中不要使用DefaultOAuth* 类,建议使用CommonHttpOAuth*,因为
since there's a bug in Android's java.net.HttpURLConnection that

keeps it from working with some serviceproviders.Instead, use the CommonsHttpOAuth* classes, since they are meant

to be used with Apache Commons HTTP (that'swhat Android uses for

HTTP anyway).
第一步,初始化httpOauthConsumer、httpOauthprovider对象。其中
httpOauthConsumer对象封装了请求用的AppKey和AppSecret值;httpOauthprovider对象提供相应操作。
第二步,执行httpOauthprovider.retrieveRequestToken(httpOauthConsumer,callBackUrl);执行这句,httpOauthConsumer将获取token和token_secret参数,并跳转到授权页面,并返回请求验证的URL
   for(String s:httpOauthConsumer.getRequestParameters().keySet()){
        Log.d("log",s+":"+httpOauthConsumer.getRequestParameters().getFirst(s));
   }
输出的HttpOauthConsumer请求参数:
01-07 01:20:33.016: DEBUG/log(9592): oauth_callback:myapp://AuthorizeActivity
01-07 01:20:33.016: DEBUG/log(9592): oauth_consumer_key:30632531
01-07 01:20:33.016: DEBUG/log(9592): oauth_nonce:-8174893267510893022
01-07 01:20:33.026: DEBUG/log(9592): oauth_signature_method:HMAC-SHA1
01-07 01:20:33.026: DEBUG/log(9592): oauth_timestamp:1325899232
01-07 01:20:33.026: DEBUG/log(9592): oauth_version:1.0
此时输出
    for(Strings:httpOauthprovider.getResponseParameters().keySet()){
                   Log.d("log",s+":"+httpOauthprovider.getResponseParameters().getFirst(s));
     }
显示为空。
可以这样理解,第一步只是向服务器发出来请求。
authorize_url:http://api.t.sina.com.cn/oauth/authorize?oauth_token=09e02bfbe399986bca8053a47be01082&oauth_callback=myapp://AuthorizeActivity

第三步,如果用户没有登录新浪微博,则会要求用户登录。否则将会出现一个页面,用户可以在此页面上一键同意或者拒绝对此应用授权。用户授权后,web应用页面将会重定向至你指定的oauth_callback,如果使用了callback,那么oauth_callback应该已经接到返回的信息,其中包含oauth_token和oauth_verifier。
此时保存oauth_verifier参数,作为下次请求的参数。
Oauth_CallBack:myapp://AuthorizeActivity?oauth_token=8844ab4499c29d153508f837730b839f&oauth_verifier=226065
第四步,执行retrieveAccessToken(httpOauthConsumer,oauth_verifier)方法,获取授权的Token和Secret。
此时输出:
         for(Strings:httpOauthprovider.getResponseParameters().keySet()){

               Log.d("log",s+":"+httpOauthprovider.getResponseParameters().getFirst(s));
返回参数:
01-07 01:30:58.876: DEBUG/log(11367): user_id:1571372175

0 0