前面的文章的OAuth認證過程在擷取oauth_verifier碼是是通過調用android系統帶的瀏覽器進行使用者授權認證的, 具體見:android開發我的新浪微部落格戶端-使用者授權頁面功能篇(3.2)。
當初的實現是這樣:
1、首先在AndroidManifest.xml中配置給AuthorizeActivity添加如下配置<data android:scheme="myapp" android:host="AuthorizeActivity" /> ,這樣在瀏覽器中通過地址myapp://AuthorizeActivity啟動AuthorizeActivity這個Activity.
2、在用android系統帶的瀏覽器顯示使用者授權頁面的時候,把"myapp://AuthorizeActivity"作為url參數CallBackUrl=myapp://AuthorizeActivity,這樣當使用者輸入完帳號和密碼會請求myapp://AuthorizeActivity?oauth_verifier=123456這個地址,這個時候就自然啟動了AuthorizeActivity這個Activity.
3、當AuthorizeActivity被啟動的時候,在onNewIntent方法中通過Uri uri = intent.getData();擷取oauth_verifier值123456。
這樣的實現有個弊端:
就是當使用者認證是選擇用uc等第三方的瀏覽器進行使用者認證時,當使用者輸入帳號密碼後點擊授權按鈕後不會跳轉到AuthorizeActivity這個Activity,只有用android內建的瀏覽器才沒有問題。其實這個是個比較嚴重的問題了大多數的使用者都會用uc等第三方的瀏覽器了,這樣導致認證不能正常進行。
最近在嘗試iphone下的oauth認證的時候發現一個很好的思路,那就是不要用系統帶的瀏覽器而且在自己的程式中嵌入一個WebView在這個WebView控制項中完成使用者授權認證而不再使用額外的瀏覽器。今天抽了空把在iphone下的實現思路搬到了android下,做了如下的嘗試。
現在的實現是這樣:
1、首先上面的那3點都可以不需要了,該刪除的刪除。
2、建立一個名為WebViewActivity的ViewActivity,在這個ViewActivity就只有一個WebView控制項
Layout檔案如下:
<?xml version="1.0" encoding="utf-8"?><ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <WebView android:layout_height="wrap_content" android:layout_width="wrap_content" android:id="@+id/web"> </WebView> </ScrollView>
3、修改OAuth過程在擷取(Request Token和Request Secret)後,啟動WebViewActivity,在這個WebViewActivity的WebView控制項顯示使用者認證的網頁,代碼實現如下:
//Url類似:http://api.t.sina.com.cn/oauth/authenticate?oauth_token=71c6df7fd0b4e1e5c491faa90b654fdf&from=xweiboString Url=getAuthenticationURL();Intent intent = new Intent(MainActivity.this, WebViewActivity.class);Bundle b=new Bundle();b.putString("url", url);intent.putExtras(b);startActivity(intent);
4、在WebViewActivity中onCreate方法中擷取上面傳過來的url進行認證網頁顯示,代碼如下:
super.onCreate(savedInstanceState); setContentView(R.layout.webview); WebView wv =(WebView) findViewById(R.id.web);Intent i=this.getIntent();if(!i.equals(null)){ Bundle b=i.getExtras(); if(b!=null){ if(b.containsKey("url")){ String url = b.getString("url"); //這行很重要一點要有,不然網頁的認證按鈕會無效 wv.getSettings().setJavaScriptEnabled(true); wv.getSettings().setSupportZoom(true); //wv.getSettings().setDefaultZoom(WebSettings.ZoomDensity.FAR); wv.getSettings().setBuiltInZoomControls(true); wv.loadUrl(url); } } }
5、上面Url參數中我們並沒有提供CallBackUrl參數,這個時候當使用者輸入帳號和密碼點擊授權後會顯示pin碼(oauth_verifier值)頁面,而不在會跳轉,頁面如下:
6、上一步中已經在WebView顯示出了授權碼,我們接下來要做的就是擷取這個授權碼176048,這個既然是顯示在WebView中的網頁,那麼我們擷取當前的這個WebView顯示的html代碼然後從中提取出授權碼不就可以了。擷取html代碼是通過調用javascript實現的,不難就解釋了看具體代碼,在onCreate方法添加如下代碼:
wv.addJavascriptInterface(new JavaScriptInterface(), "Methods"); WebViewClient wvc=new WebViewClient() { @Override public void onPageFinished(WebView view,String url) { view.loadUrl("javascript:window.Methods.getHTML('<head>'+document.getElementsByTagName('html')[0].innerHTML+'</head>');"); super.onPageFinished(view, url); } }; wv.setWebViewClient(wvc);
添加類JavaScriptInterface
class JavaScriptInterface { public void getHTML(String html) { String pin= getPin(html); //這裡就擷取到了我們想要的pin碼 //這個pin碼就是oauth_verifier值,用來進一步擷取Access Token和Access Secret用 Log.e("pin", pin); } }
7、這樣我們就擷取到了當前WebView的html代碼,接下來就是提取html中的授權碼,html代碼如下:
<head><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><meta name="viewport" content="width=device-width"><title>應用授權 - 新浪微博</title><link href="http://timg.sjs.sinajs.cn/t35/style/css/open/open_third.css" rel="stylesheet" type="text/css"></head><body><div class="opthBg"> <div class="opthTopBg"><div class="opthHead"><a href="http://weibo.com" class="newlogo"></a></div></div> <div class="opthBox"> <div class="opthCen"> <br><br> <font size="4"><b>擷取到授權碼:176048</b></font> <br><br> <div class="opthBottom"> <div class="lf"></div> <div class="rt"> </div> </div> </div> </div></div><script type="text/javascript">function canchange(){ document.forms['authZForm'].from.value='turnuser'; document.forms['authZForm'].action.value=''; document.forms['authZForm'].submit();}</script></body></head>
提取授權碼用正則實現的,java方法代碼如下:
public String getPin(String html) { String ret=""; String regEx="[0-9]{6}"; Pattern p=Pattern.compile(regEx); Matcher m=p.matcher(html); boolean result=m.find(); if(result) { ret= m.group(0); } return ret; }
這樣就完成了所有的實現了,這做雖然有點麻煩,但是徹底解決了以前存在的問題。