2014-02-27 226 views
12

即使存在來自LinkedIn .Setting向上LinkedIn授權使用OAuth 1.0沒有這樣的機器人特定SDK(如Facebook和的Android Twitter的SDK)仍然容易使用:的Oauth 2.0授權LinkedIn Android中

但它與Oauth2.0的授權不是一回事。沒有太多有用的庫或android特定的例子。我試着用這些:

我讀過Oauth 2.0比1.0更容易實現。我仍然無法這樣做。

任何在Android中爲LinkedIn實現Oauth2.0的指針?

回答

38

LinkedIN的Oauth2.0認證。

第1步:

  • 按照this document.註冊與LinkedIn您的應用程序,讓你的API_KEY和api_secret。

第2步:

MainActivity:

public class MainActivity extends Activity { 

/*CONSTANT FOR THE AUTHORIZATION PROCESS*/ 

/****FILL THIS WITH YOUR INFORMATION*********/ 
//This is the public api key of our application 
private static final String API_KEY = "YOUR_API_KEY"; 
//This is the private api key of our application 
private static final String SECRET_KEY = "YOUR_API_SECRET"; 
//This is any string we want to use. This will be used for avoiding CSRF attacks. You can generate one here: http://strongpasswordgenerator.com/ 
private static final String STATE = "E3ZYKC1T6H2yP4z"; 
//This is the url that LinkedIn Auth process will redirect to. We can put whatever we want that starts with http:// or https:// . 
//We use a made up url that we will intercept when redirecting. Avoid Uppercases. 
private static final String REDIRECT_URI = "http://com.amalbit.redirecturl"; 
/*********************************************/ 

//These are constants used for build the urls 
private static final String AUTHORIZATION_URL = "https://www.linkedin.com/uas/oauth2/authorization"; 
private static final String ACCESS_TOKEN_URL = "https://www.linkedin.com/uas/oauth2/accessToken"; 
private static final String SECRET_KEY_PARAM = "client_secret"; 
private static final String RESPONSE_TYPE_PARAM = "response_type"; 
private static final String GRANT_TYPE_PARAM = "grant_type"; 
private static final String GRANT_TYPE = "authorization_code"; 
private static final String RESPONSE_TYPE_VALUE ="code"; 
private static final String CLIENT_ID_PARAM = "client_id"; 
private static final String STATE_PARAM = "state"; 
private static final String REDIRECT_URI_PARAM = "redirect_uri"; 
/*---------------------------------------*/ 
private static final String QUESTION_MARK = "?"; 
private static final String AMPERSAND = "&"; 
private static final String EQUALS = "="; 

private WebView webView; 
private ProgressDialog pd; 

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 

    //get the webView from the layout 
    webView = (WebView) findViewById(R.id.main_activity_web_view); 

    //Request focus for the webview 
    webView.requestFocus(View.FOCUS_DOWN); 

    //Show a progress dialog to the user 
    pd = ProgressDialog.show(this, "", this.getString(R.string.loading),true); 

    //Set a custom web view client 
    webView.setWebViewClient(new WebViewClient(){ 
      @Override 
      public void onPageFinished(WebView view, String url) { 
       //This method will be executed each time a page finished loading. 
       //The only we do is dismiss the progressDialog, in case we are showing any. 
       if(pd!=null && pd.isShowing()){ 
        pd.dismiss(); 
       } 
      } 
     @Override 
     public boolean shouldOverrideUrlLoading(WebView view, String authorizationUrl) { 
      //This method will be called when the Auth proccess redirect to our RedirectUri. 
      //We will check the url looking for our RedirectUri. 
      if(authorizationUrl.startsWith(REDIRECT_URI)){ 
       Log.i("Authorize", ""); 
       Uri uri = Uri.parse(authorizationUrl); 
       //We take from the url the authorizationToken and the state token. We have to check that the state token returned by the Service is the same we sent. 
       //If not, that means the request may be a result of CSRF and must be rejected. 
       String stateToken = uri.getQueryParameter(STATE_PARAM); 
       if(stateToken==null || !stateToken.equals(STATE)){ 
        Log.e("Authorize", "State token doesn't match"); 
        return true; 
       } 

       //If the user doesn't allow authorization to our application, the authorizationToken Will be null. 
       String authorizationToken = uri.getQueryParameter(RESPONSE_TYPE_VALUE); 
       if(authorizationToken==null){ 
        Log.i("Authorize", "The user doesn't allow authorization."); 
        return true; 
       } 
       Log.i("Authorize", "Auth token received: "+authorizationToken); 

       //Generate URL for requesting Access Token 
       String accessTokenUrl = getAccessTokenUrl(authorizationToken); 
       //We make the request in a AsyncTask 
       new PostRequestAsyncTask().execute(accessTokenUrl); 

      }else{ 
       //Default behaviour 
       Log.i("Authorize","Redirecting to: "+authorizationUrl); 
       webView.loadUrl(authorizationUrl); 
      } 
      return true; 
     } 
    }); 

    //Get the authorization Url 
    String authUrl = getAuthorizationUrl(); 
    Log.i("Authorize","Loading Auth Url: "+authUrl); 
    //Load the authorization URL into the webView 
    webView.loadUrl(authUrl); 
} 

/** 
* Method that generates the url for get the access token from the Service 
* @return Url 
*/ 
private static String getAccessTokenUrl(String authorizationToken){ 
    return ACCESS_TOKEN_URL 
      +QUESTION_MARK 
      +GRANT_TYPE_PARAM+EQUALS+GRANT_TYPE 
      +AMPERSAND 
      +RESPONSE_TYPE_VALUE+EQUALS+authorizationToken 
      +AMPERSAND 
      +CLIENT_ID_PARAM+EQUALS+API_KEY 
      +AMPERSAND 
      +REDIRECT_URI_PARAM+EQUALS+REDIRECT_URI 
      +AMPERSAND 
      +SECRET_KEY_PARAM+EQUALS+SECRET_KEY; 
} 
/** 
* Method that generates the url for get the authorization token from the Service 
* @return Url 
*/ 
private static String getAuthorizationUrl(){ 
    return AUTHORIZATION_URL 
      +QUESTION_MARK+RESPONSE_TYPE_PARAM+EQUALS+RESPONSE_TYPE_VALUE 
      +AMPERSAND+CLIENT_ID_PARAM+EQUALS+API_KEY 
      +AMPERSAND+STATE_PARAM+EQUALS+STATE 
      +AMPERSAND+REDIRECT_URI_PARAM+EQUALS+REDIRECT_URI; 
} 

@Override 
public boolean onCreateOptionsMenu(Menu menu) { 
    // Inflate the menu; this adds items to the action bar if it is present. 
    getMenuInflater().inflate(R.menu.main, menu); 
    return true; 
} 

private class PostRequestAsyncTask extends AsyncTask<String, Void, Boolean>{ 

    @Override 
    protected void onPreExecute(){ 
     pd = ProgressDialog.show(MainActivity.this, "", MainActivity.this.getString(R.string.loading),true); 
    } 

    @Override 
    protected Boolean doInBackground(String... urls) { 
     if(urls.length>0){ 
      String url = urls[0]; 
      HttpClient httpClient = new DefaultHttpClient(); 
      HttpPost httpost = new HttpPost(url); 
      try{ 
       HttpResponse response = httpClient.execute(httpost); 
       if(response!=null){ 
        //If status is OK 200 
        if(response.getStatusLine().getStatusCode()==200){ 
         String result = EntityUtils.toString(response.getEntity()); 
         //Convert the string result to a JSON Object 
         JSONObject resultJson = new JSONObject(result); 
         //Extract data from JSON Response 
         int expiresIn = resultJson.has("expires_in") ? resultJson.getInt("expires_in") : 0; 

         String accessToken = resultJson.has("access_token") ? resultJson.getString("access_token") : null; 
         Log.e("Tokenm", ""+accessToken); 
         if(expiresIn>0 && accessToken!=null){ 
          Log.i("Authorize", "This is the access Token: "+accessToken+". It will expires in "+expiresIn+" secs"); 

          //Calculate date of expiration 
          Calendar calendar = Calendar.getInstance(); 
          calendar.add(Calendar.SECOND, expiresIn); 
          long expireDate = calendar.getTimeInMillis(); 

          ////Store both expires in and access token in shared preferences 
          SharedPreferences preferences = MainActivity.this.getSharedPreferences("user_info", 0); 
          SharedPreferences.Editor editor = preferences.edit(); 
          editor.putLong("expires", expireDate); 
          editor.putString("accessToken", accessToken); 
          editor.commit(); 

          return true; 
         } 
        } 
       } 
      }catch(IOException e){ 
       Log.e("Authorize","Error Http response "+e.getLocalizedMessage()); 
      } 
      catch (ParseException e) { 
       Log.e("Authorize","Error Parsing Http response "+e.getLocalizedMessage()); 
      } catch (JSONException e) { 
       Log.e("Authorize","Error Parsing Http response "+e.getLocalizedMessage()); 
      } 
     } 
     return false; 
    } 

    @Override 
    protected void onPostExecute(Boolean status){ 
     if(pd!=null && pd.isShowing()){ 
      pd.dismiss(); 
     } 
     if(status){ 
      //If everything went Ok, change to another activity. 
      Intent startProfileActivity = new Intent(MainActivity.this, ProfileActivity.class); 
      MainActivity.this.startActivity(startProfileActivity); 
     } 
    } 

}; 
} 

而xmlLayout:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:tools="http://schemas.android.com/tools" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:paddingBottom="@dimen/activity_vertical_margin" 
    android:paddingLeft="@dimen/activity_horizontal_margin" 
    android:paddingRight="@dimen/activity_horizontal_margin" 
    android:paddingTop="@dimen/activity_vertical_margin" 
    tools:context=".MainActivity" > 

    <WebView 
     android:id="@+id/main_activity_web_view" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent" /> 

</LinearLayout> 

令牌保存在sharedpreference文件。

簡單的android項目回購here in github

+0

重定向網址是否必須相同。我可以指出其他一些網址嗎? – Akshat

+0

我有一個奇怪的問題,彈出後顯示android鍵盤出現,但單擊字母時不起作用。跆拳道?我不能輸入登錄信息。 –

+1

只是想提一提,您不能再使用隨機redirect_uri:您必須使用您在填寫的「添加應用程序」表單中指定的那個。 – ujvl

2

我得到了它的工作,但它花了我一些時間。我跟着LinkedIn Authentication來管理。
我仍然強烈建議,仍讀這個環節,因爲我不包括在我的例子所有的情況下(錯誤,錯誤處理,最好pratices,參數的使用,精確的文檔......)

  • 首先,你需要有你的LinkedIn API密鑰和密鑰。如果沒有,請在here.上註冊一個應用程序

  • 其次,您需要一個可以接收授權碼的應用程序中的活動。爲此,它需要在AndroidManifest.xml文件中設置爲可瀏覽(可發射從瀏覽器):雖然不建議

    <activity 
         android:name=".ResultActivity" 
         android:label="" > 
         <intent-filter> 
         <action android:name="android.intent.action.VIEW"/> 
    
         <category android:name="android.intent.category.DEFAULT"/> 
         <category android:name="android.intent.category.BROWSABLE"/> 
         </intent-filter> 
    

    ,它可以使用數據標籤使用自定義方案來檢索的URI:

    <data android:scheme="oauth"/> 
    
  • 之後,您需要將用戶重定向到LinkedIn的授權對話框,使用特定的URL:

    https://www.linkedin.com/uas/oauth2/authorization?response_type=code 
                &client_id=YOUR_API_KEY 
                &scope=SCOPE 
                &state=STATE 
                &redirect_uri=YOUR_REDIRECT_URI 
    

    您可以使用Web查看直接顯示在你的應用程序,或者讓系統通過它像一個意圖:

    Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(/* FULL URL */)); 
    startActivity(intent); 
    

    這裏唯一的問題是,API不接受其他方案比HTTP或HTTPS,這意味着你不能只需將意圖URI作爲redirect_uri參數傳遞即可。

    所以我在我的服務器上創建了一個登陸頁面,唯一的目的是重定向到應用程序。我們可以想象,像(醜陋的縮短PHP)(Intent ref.)

    header('Location: ' . "intent:#Intent;component=your.package/.ResultActivity;S.code=" . $_GET['code'] . ";S.state=" . $_GET['state'] . ";end"); 
    die(); 
    

    所以,一切的一套!現在ResultActivity的onCreate(Bundle)

    Intent intent = getIntent(); 
    String authorizationCode = intent.getStringExtra("code"); 
    

    還有另一種方式在這裏傳遞參數,如果該數據標籤較早使用。

  • 幾乎在那裏!現在,你只需要在該網址上執行一個簡單的POST請求:

    https://www.linkedin.com/uas/oauth2/accessToken?grant_type=authorization_code 
                &code=AUTHORIZATION_CODE 
                &redirect_uri=YOUR_REDIRECT_URI 
                &client_id=YOUR_API_KEY 
                &client_secret=YOUR_SECRET_KEY 
    

    在成功時返回一個JSON對象:

    {"expires_in":5184000,"access_token":"AQXdSP_W41_UPs5ioT_t8HESyODB4FqbkJ8LrV_5mff4gPODzOYR"}

的Et瞧!您現在可以使用access_token進行API調用。不要忘記store it某處,所以你沒有再次通過這些步驟。

我希望這篇文章不會太長,並且可以幫助一些人。 :)

+0

是否有可能沒有在PHP的登陸頁面呢? – amalBit

+0

@amalBit有一些方法可以解決這個問題,但這是修改Oauth2 nature imho。至於解決方案,您可以使用WebView和'@Override shouldOverrideUrlLoading',如@ huy.nguyen的示例所示,甚至使用'http:// localhost'作爲redirect_uri並監聽正在進行的連接。 – Bhullnatik

+0

你能舉一個例子嗎?我無法解決它.. – amalBit

3

OAuth 2.0比1.0簡單很多,可以在沒有任何外部庫的幫助下完成。但是,如果你已經在使用scribe-java,它會更容易。

執行很簡單。您需要創建一個WebView,它具有自定義WebViewClient,它捕獲並覆蓋回調URL的加載行爲。因此,當WebView嘗試加載該URL時,您可以攔截該進程並提取驗證者。驗證者可以傳遞給scribe-java來交換訪問令牌。

要開始整個過程​​,只需告知WebView加載授權URL。

我有託管示例代碼here。該應用程序使用Buffer的API進行身份驗證,但大部分代碼都可以重用。您可能對fragment感興趣,該主機託管我的自定義WebView和獲取訪問令牌的背景job

隨意問我任何後續問題。

+0

嘿,我檢查了你的代碼,但無法理解oauth機制。你能更好地解釋代碼中驗證者的提取嗎? – amalBit

+0

你的意思是[line](https://github.com/nguyenhuy/buffer/blob/master/buffer/src/main/java/org/nguyenhuy/buffer/fragment/OAuthFragment.java#L98)? 回調看起來像這樣:'callback_url?code =「verifier」'(即:http://nguyenhuy.me/callback?code="verifier「)。爲了提取驗證者,你只需要調用'Uri.getQueryParameter(String)'來獲得「代碼」的值。 –

+1

一個可行的例子會很棒。 – amalBit

0
  @Override 

     public boolean shouldOverrideUrlLoading(WebView view, String authorizationUrl) { 
      //This method will be called when the Auth proccess redirect to our RedirectUri. 
      //We will check the url looking for our RedirectUri. 
      if(authorizationUrl.startsWith(REDIRECT_URI)){ 
       Log.i("Authorize", ""); 
       Uri uri = Uri.parse(authorizationUrl); 
       //We take from the url the authorizationToken and the state token. We have to check that the state token returned by the Service is the same we sent. 
       //If not, that means the request may be a result of CSRF and must be rejected. 
       String stateToken = uri.getQueryParameter(STATE_PARAM); 
       if(stateToken==null || !stateToken.equals(STATE)){ 
        Log.e("Authorize", "State token doesn't match"); 
        return true; 
       } 

       //If the user doesn't allow authorization to our application, the authorizationToken Will be null. 
       String authorizationToken = uri.getQueryParameter(RESPONSE_TYPE_VALUE); 
       if(authorizationToken==null){ 
        Log.i("Authorize", "The user doesn't allow authorization."); 
        return true; 
       } 
       Log.i("Authorize", "Auth token received: "+authorizationToken); 

       //Generate URL for requesting Access Token 
       String accessTokenUrl = getAccessTokenUrl(authorizationToken); 
       //We make the request in a AsyncTask 
       new PostRequestAsyncTask().execute(accessTokenUrl); 

      }else{ 
       //Default behaviour 
       Log.i("Authorize","Redirecting to: "+authorizationUrl); 
       webView.loadUrl(authorizationUrl); 
      } 
      return true; 
     } 

而在你的AsyncTask:

私有類PostRequestAsyncTask擴展的AsyncTask {

@Override 
    protected void onPreExecute(){ 
     pd = ProgressDialog.show(MainActivity.this, "", MainActivity.this.getString(R.string.loading),true); 
    } 

    @Override 
    protected Boolean doInBackground(String... urls) { 
     if(urls.length>0){ 
      String url = urls[0]; 
      HttpClient httpClient = new DefaultHttpClient(); 
      HttpPost httpost = new HttpPost(url); 
      try{ 
       HttpResponse response = httpClient.execute(httpost); 
       if(response!=null){ 
        //If status is OK 200 
        if(response.getStatusLine().getStatusCode()==200){ 
         String result = EntityUtils.toString(response.getEntity()); 
         //Convert the string result to a JSON Object 
         JSONObject resultJson = new JSONObject(result); 
         //Extract data from JSON Response 
         int expiresIn = resultJson.has("expires_in") ? resultJson.getInt("expires_in") : 0; 

         String accessToken = resultJson.has("access_token") ? resultJson.getString("access_token") : null; 
         Log.e("Tokenm", ""+accessToken); 
         if(expiresIn>0 && accessToken!=null){ 
          Log.i("Authorize", "This is the access Token: "+accessToken+". It will expires in "+expiresIn+" secs"); 

          //Calculate date of expiration 
          Calendar calendar = Calendar.getInstance(); 
          calendar.add(Calendar.SECOND, expiresIn); 
          long expireDate = calendar.getTimeInMillis(); 

          ////Store both expires in and access token in shared preferences 
          SharedPreferences preferences = MainActivity.this.getSharedPreferences("user_info", 0); 
          SharedPreferences.Editor editor = preferences.edit(); 
          editor.putLong("expires", expireDate); 
          editor.putString("accessToken", accessToken); 
          editor.commit(); 

          return true; 
         } 
        } 
       } 
      }catch(IOException e){ 
       Log.e("Authorize","Error Http response "+e.getLocalizedMessage()); 
      } 
      catch (ParseException e) { 
       Log.e("Authorize","Error Parsing Http response "+e.getLocalizedMessage()); 
      } catch (JSONException e) { 
       Log.e("Authorize","Error Parsing Http response "+e.getLocalizedMessage()); 
      } 
     } 
     return false; 
    } 

    @Override 
    protected void onPostExecute(Boolean status){ 
     if(pd!=null && pd.isShowing()){ 
      pd.dismiss(); 
     } 
     if(status){ 
      //If everything went Ok, change to another activity. 
      Intent startProfileActivity = new Intent(MainActivity.this, ProfileActivity.class); 
      MainActivity.this.startActivity(startProfileActivity); 
     } 
    } 

};