之前在网上看过好多人用java写登录网站,这次正好学习android,自己写实习一个,就拿经常登录的javaeye(现在叫iteye,已经是csdn的了)试一下。
先来分析javaeye登录界面
查看源文件
<form action="/login" id="login_form" method="post"> <table border="0" cellspacing="0" cellpadding="0" class="table_1"> <colgroup><col width="60" /><col /></colgroup> <tr> <th>账号</th> <td> <input class="input_1 required" id="user_name" name="name" placeholder="用户名或邮箱" tabindex="1" type="text" value="" /> </td> </tr> <tr> <th>密码</th> <td> <input class="input_1 required" id="password" name="password" tabindex="2" type="password" value="" /></td> </tr> <tr> <th> </th> <td> <input id="auto" name="remember_me" tabindex="3" type="checkbox" value="1" /> <label for="auto">下次自动登录</label> <a href="/users/forget">忘记密码?</a> </td> </tr> <tr> <th> </th> <td><input type="submit" name="button" id="button" value="登 录" class="btn_1 submit" tabindex="4" /></td> </tr> </table> </form>
登录以后,使用chrome的开发人员工具(其他浏览器也有相关工具,我就不一一说了)拦截获取响应消息
可以看出返回的是302地址重定向
还需要注意的是每次提交时,不能遗漏的cookie即seesion_id ,javaeye用的是_javaeye3_session
接下来是我做的android的例子,使用webview控件,它只负责内容展现,请求响应我分别使用了HttpURLConnection和HttpClient。
不说了,上代码:
主布局main.xml代码如下:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/RelativeLayout1" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <TextView android:id="@+id/textViewInfo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentTop="true"/> <WebView android:id="@+id/webViewInfo" android:layout_width="match_parent" android:layout_height="260dp" android:layout_below="@+id/textViewInfo" android:layout_marginTop="30dp" android:layout_alignParentLeft="true" /> <LinearLayout android:id="@+id/linearLayout2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_above="@+id/linearLayout1" android:layout_alignParentLeft="true" > <TextView android:id="@+id/textView2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="使用Java" /> <Button android:id="@+id/buttonJavaLogin" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_toRightOf="@+id/textView2" android:text="登录ITeye" /> <Button android:id="@+id/buttonJavaMyMessage" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_toRightOf="@+id/buttonJavaLogin" android:text="ITeye收件箱" /> </LinearLayout> <LinearLayout android:id="@+id/linearLayout1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentLeft="true" > <TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="使用Apache" /> <Button android:id="@+id/buttonApacheLogin" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_toRightOf="@+id/textView1" android:text="登录ITeye" /> <Button android:id="@+id/buttonApacheMyMessage" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_toRightOf="@+id/buttonApacheLogin" android:text="ITeye收件箱" /> </LinearLayout> </RelativeLayout>
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <LinearLayout android:id="@+id/linearLayout1" android:layout_width="match_parent" android:layout_height="wrap_content" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="账号: " /> <EditText android:id="@+id/editTextUsername" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" /> </LinearLayout> <LinearLayout android:id="@+id/linearLayout2" android:layout_width="match_parent" android:layout_height="wrap_content" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="密码: " /> <EditText android:id="@+id/exitTextPassword" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:inputType="textPassword" /> </LinearLayout> </LinearLayout>
<uses-permission android:name="android.permission.INTERNET"/>
package com.zhang.test08_16; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.io.Writer; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.ProtocolException; import java.net.URL; import java.util.ArrayList; import java.util.List; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.NameValuePair; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.message.BasicNameValuePair; import org.apache.http.params.CoreProtocolPNames; import org.apache.http.params.HttpParams; import org.apache.http.protocol.HTTP; import org.apache.http.util.EntityUtils; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.webkit.WebView; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; public class Test08_16Activity extends Activity { private TextView textViewInfo; private WebView webViewInfo; private Button buttonJavaLogin; private Button buttonJavaMyMessage; private Button buttonApaceLogin; private Button buttonApacheMyMessage; private View loginView; private AlertDialog loginDialog; private EditText editTextUserName; private EditText editTextPassword; private String clientType; private DefaultHttpClient client; private String cookie; private static final String LOGIN_URL = "http://www.iteye.com/login"; private static final String MESSAGE_URL = "http://app.iteye.com/messages"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); textViewInfo = (TextView)findViewById(R.id.textViewInfo); webViewInfo = (WebView)findViewById(R.id.webViewInfo); buttonJavaLogin = (Button)findViewById(R.id.buttonJavaLogin); buttonJavaMyMessage = (Button)findViewById(R.id.buttonJavaMyMessage); buttonApaceLogin = (Button)findViewById(R.id.buttonApacheLogin); buttonApacheMyMessage = (Button)findViewById(R.id.buttonApacheMyMessage); LayoutInflater inflater = LayoutInflater.from(this); loginView = inflater.inflate(R.layout.login, null); editTextUserName = (EditText) loginView.findViewById(R.id.editTextUsername); editTextPassword = (EditText) loginView.findViewById(R.id.exitTextPassword); loginDialog = getLoginDialog(); buttonJavaLogin.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { clientType = "java"; loginDialog.show(); } }); buttonJavaMyMessage.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { showMyMessageJava(); } }); buttonApaceLogin.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { clientType = "apache"; loginDialog.show(); } }); buttonApacheMyMessage.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { showMyMessageApache(); } }); } @Override protected void onResume() { client = new DefaultHttpClient();//client.getCookieStore().getCookies() session_id HttpParams httpParams = client.getParams(); httpParams.setParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE, false); super.onResume(); } @Override protected void onPause() { client.getConnectionManager().shutdown(); super.onPause(); } private AlertDialog getLoginDialog() { return new AlertDialog.Builder(Test08_16Activity.this) .setTitle("登录") .setView(loginView) .setPositiveButton("登录", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { if (clientType.equals("apache")) { loginApache(); } else if(clientType.equals("java")) { loginJava(); } } }) .setNegativeButton("取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }) .create(); } private void loginJava() { String username = editTextUserName.getText().toString(); String password = editTextPassword.getText().toString(); URL url = null; try { url = new URL(LOGIN_URL); } catch (MalformedURLException e) { } HttpURLConnection urlConnection = null; try { urlConnection = (HttpURLConnection) url.openConnection(); } catch (IOException e) { textViewInfo.setText(e.getMessage()); return; } try { urlConnection.setRequestMethod("POST"); } catch (ProtocolException e) { } urlConnection.setDoOutput(true); urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); urlConnection.setRequestProperty("Connection", "keep-alive"); urlConnection.setInstanceFollowRedirects(false);// OutputStream out = null; try { out = new BufferedOutputStream(urlConnection.getOutputStream());//请求 } catch (IOException e) { urlConnection.disconnect(); textViewInfo.setText(e.getMessage()); return; } Writer writer = null; try { writer = new OutputStreamWriter(out,"UTF-8"); } catch (UnsupportedEncodingException e1) { } try { writer.write("name="+username +"&password="+password); } catch (IOException e) { urlConnection.disconnect(); textViewInfo.setText(e.getMessage()); return; } finally{ try { writer.flush(); writer.close(); } catch (IOException e) { } } getResponseJava(urlConnection); } private void showMyMessageJava() { URL url = null; try { url = new URL(MESSAGE_URL); } catch (MalformedURLException e) { } HttpURLConnection urlConnection = null; try { urlConnection = (HttpURLConnection) url.openConnection(); } catch (IOException e) { textViewInfo.setText(e.getMessage()); return; } //method The default value is "GET" getResponseJava(urlConnection); } //共用HttpClient private void loginApache() { String username = editTextUserName.getText().toString(); String password = editTextPassword.getText().toString(); List<NameValuePair> params = new ArrayList<NameValuePair>(1); params.add(new BasicNameValuePair("name", username)); params.add(new BasicNameValuePair("password", password)); HttpEntity formEntity = null; try { formEntity = new UrlEncodedFormEntity(params,HTTP.UTF_8); } catch (UnsupportedEncodingException e) { } HttpPost request = new HttpPost(LOGIN_URL); request.setEntity(formEntity); getResponseApache(request); } private void showMyMessageApache() { HttpGet request = new HttpGet(MESSAGE_URL); getResponseApache(request); } private void getResponseJava(HttpURLConnection urlConnection) { if(cookie != null) {//session_id urlConnection.setRequestProperty("Cookie", cookie); } InputStream in = null; try { in = new BufferedInputStream(urlConnection.getInputStream());//响应 } catch (IOException e) { urlConnection.disconnect(); textViewInfo.setText(e.getMessage()); return; } cookie = urlConnection.getHeaderField("Set-Cookie");//session_id int responseCode = -1; try { responseCode = urlConnection.getResponseCode(); } catch (IOException e) { urlConnection.disconnect(); textViewInfo.setText(e.getMessage()); return; } if(responseCode == 301 || responseCode == 302 || responseCode == 307) {//重定向 String location = urlConnection.getHeaderField("location"); URL url = null; try { url = new URL(location); } catch (MalformedURLException e) { } HttpURLConnection urlConnectionRedirect = null; try { urlConnectionRedirect = (HttpURLConnection) url.openConnection(); } catch (IOException e) { textViewInfo.setText(e.getMessage()); return; } getResponseJava(urlConnectionRedirect); return; } BufferedReader reader = null; try { reader = new BufferedReader(new InputStreamReader(in,"UTF-8")); } catch (UnsupportedEncodingException e1) { } StringBuilder result = new StringBuilder(); String tmp = null; try { while((tmp = reader.readLine()) != null){ result.append(tmp); } } catch (IOException e) { textViewInfo.setText(e.getMessage()); return; } finally { try { reader.close(); urlConnection.disconnect(); } catch (IOException e) { } } webViewInfo.loadDataWithBaseURL(null, result.toString(), "text/html", "UTF-8", null); } private void getResponseApache(HttpUriRequest request) { HttpResponse response = null; try { response = client.execute(request);//重定向 RedirectStrategy execute while (!done) } catch (ClientProtocolException e) { textViewInfo.setText(e.getMessage()); } catch (IOException e) { textViewInfo.setText(e.getMessage()); } if (response == null) { return; } if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { textViewInfo.setText("error response" + response.getStatusLine().toString()); return; } String result = null; try { result = EntityUtils.toString(response.getEntity(),"UTF-8"); } catch (Exception e) { textViewInfo.setText("error response" + response.getStatusLine().toString()); return; } webViewInfo.loadDataWithBaseURL(null, result, "text/html", "UTF-8", null); } }
主界面:
点击 使用java登录iteye,弹出登录对话框:
输入javaeye的账号和密码,点击登录(有点慢,耐心等待),结果如下:
登录成功了,点击 iteye收件箱(相当于在浏览器里点击收件箱),验证是同一session
成功,代码没有问题。
使用Apache的方式结果一样,我就不截图了。
做这个例子,我是想了解http协议,httpclient内部实现原理。写的过程中参照如下:
seesion问题:http://hi.baidu.com/%CE%A4%D7%D4%C9%FD/blog/item/e2ce0004f52f2c61030881fa.html
http://helin.iteye.com/blog/257115
302重定向:http://blog.csdn.net/hegch/article/details/1891146
http 417 :http://blog.yes2.me/archives/915
webview乱码:http://hongyang321.iteye.com/blog/1021564
接下来分析一下代码:
先分析java部分,代码和我上篇博客node.js+android http请求响应基本很像,关键的不同
1.解决session的代码:
private String cookie; if(cookie != null) {//session_id urlConnection.setRequestProperty("Cookie", cookie); } cookie = urlConnection.getHeaderField("Set-Cookie");//session_id
2.解决重定向的代码:
if(responseCode == 301 || responseCode == 302 || responseCode == 307) {//重定向 String location = urlConnection.getHeaderField("location"); URL url = null; try { url = new URL(location); } catch (MalformedURLException e) { } HttpURLConnection urlConnectionRedirect = null; try { urlConnectionRedirect = (HttpURLConnection) url.openConnection(); } catch (IOException e) { textViewInfo.setText(e.getMessage()); return; } getResponseJava(urlConnectionRedirect); return; }查看响应代码是否需要重定向,如果是读出响应头上的location,发出get请求,递归调用。
再来分析一下httpclient部分:
1.解决407 error
HttpParams httpParams = client.getParams(); httpParams.setParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE, false);
private DefaultHttpClient client;
3.解决重定向
没有附加任何代码,httpclient内部已经实现,源码主要部分如下:
class AbstractHttpClient public final HttpResponse execute(HttpUriRequest request) throws IOException, ClientProtocolException { return execute(request, (HttpContext) null); } public final HttpResponse execute(HttpHost target, HttpRequest request, HttpContext context) RequestDirector director = null; director = createClientRequestDirector return new DefaultRequestDirector return director.execute(target, request, execContext); class DefaultRequestDirector public HttpResponse execute(HttpHost target, HttpRequest request, HttpContext context) while (!done) { RoutedRequest followup = handleResponse(roureq, response, context); if (followup == null) { done = true; } else { protected RoutedRequest handleResponse(RoutedRequest roureq, HttpResponse response, HttpContext context) if (HttpClientParams.isRedirecting(params) && this.redirectStrategy.isRedirected(request, response, context)) { class DefaultRedirectStrategy public boolean isRedirected( final HttpRequest request, final HttpResponse response, final HttpContext context) int statusCode = response.getStatusLine().getStatusCode(); String method = request.getRequestLine().getMethod(); Header locationHeader = response.getFirstHeader("location"); switch (statusCode) { case HttpStatus.SC_MOVED_TEMPORARILY: return (method.equalsIgnoreCase(HttpGet.METHOD_NAME) || method.equalsIgnoreCase(HttpHead.METHOD_NAME)) && locationHeader != null; case HttpStatus.SC_MOVED_PERMANENTLY: case HttpStatus.SC_TEMPORARY_REDIRECT: return method.equalsIgnoreCase(HttpGet.METHOD_NAME) || method.equalsIgnoreCase(HttpHead.METHOD_NAME); case HttpStatus.SC_SEE_OTHER: return true; default: return false; } //end of switch
ps:使用windows7以后,根据内容找文件很蛋疼,推荐大家使用 UltraFileSearch 官网 http://www.ultrafilesearch.com/,不是广告,只是用了这么多搜索替代工具以后,觉得最好的一个,和大家分享一下
敲了半天怪累的,就说到这了。