内容摘要
前言
设计思路
ASP.NET服务器端
Android客户端
运行结果
上篇《Android与IIS身份验证——基本验证》实现了Android客户端与IIS的简单验证。而这种验证方式虽然使用起来比较简单,但是也有很多缺点。如,安全性较差,账号密码设置不灵活。让我们回顾一下ASP.NET的身份验证方式:Windows、Form和Passport。这三种验证方式中,Form身份验证方式最经常使用,因为它有很多优越性,其优点我就不一一介绍了。那么,怎样实现Android客户端与IIS的Form身份验证呢?我整理了一下,需要以下几个步骤:
一、设计思路
Android客户端访问IIS服务器时,当没有登陆时,ASP.NET通过Form集成验证方法会返回一个登陆页面。然后输入正确的用户名和密码,就可以进入主程序了。流程如图1.1所示。
图1.1
在Android客户端访问以IIS为宿主的ASP.NET服务器时,返回JSON格式来传递数据。客户端中,根据返回数据的不同而判断当前状态是否已登陆。由于Form验证是保存了Cookie,所示每次调用Http请求之前要先添加上次请求的Cookie。
二、ASP.NET服务器端
创建一个ASP.NET MVC网站项目。
首先,由于每次请求都返回JSON格式的数据,我们编写一个Model类来承载数据。
{
/// <summary>
/// 是否已登录
/// </summary>
public bool IsAuthenticated { get ; set ; }
/// <summary>
/// 是否执行成功
/// </summary>
public bool IsSuccess { get ; set ; }
/// <summary>
/// 登录账号
/// </summary>
public string Identity { get ; set ; }
/// <summary>
/// 执行结果
/// </summary>
public object Result { get ; set ; }
}
接着,编写HomeController:
public class HomeController : Controller
{
[Authorize]
public ActionResult Index()
{
return Json( new ResponseResult
{
Result = " 登陆成功 " ,
IsAuthenticated = true ,
IsSuccess = true ,
Identity = this .User.Identity.Name
}, " text/html " , JsonRequestBehavior.AllowGet);
}
public ActionResult LogOn()
{
return Json( new ResponseResult
{
Result = " 您尚未登录 " ,
IsSuccess = true
}, " text/html " , JsonRequestBehavior.AllowGet);
}
public ActionResult Register( string account, string password)
{
if (account == " liudong " && password == " 123 " )
{
FormsAuthentication.SetAuthCookie(account, false );
return Json( new ResponseResult
{
Result = " 登录成功 " ,
IsSuccess = true ,
IsAuthenticated = true ,
Identity = account
}, " text/html " , JsonRequestBehavior.AllowGet);
}
else
{
return Json( new ResponseResult
{
Result = " 用户名或密码错误 " ,
}, " text/html " , JsonRequestBehavior.AllowGet);
}
}
Index Action为登陆认证后才能访问的Action。
LogOn Action为未登陆时跳转的Action。
Register Action为登陆Action,用户名和密码正确后就保存Form验证的Cookie。
然后,在Web.config中配置Form验证配置
< forms loginUrl ="~/Home/LogOn" timeout ="2880" />
</ authentication >
最后,配置IIS,部署ASP.NET MV网站,如图2.1所示。
图2.1
三、Android客户端
首先,编写一个调用Http请求的类。
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.List;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.CookieStore;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.AbstractHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import android.util.Log;
public abstract class HttpHelper {
private final static String TAG = " HttpHelper " ;
private final static String SERVER_URL = " http://192.168.1.104:180/Home/ " ;
private static CookieStore cookieStore;
public static String invoke(String actionName, List < NameValuePair > params) {
String result = null ;
try {
String url = SERVER_URL + actionName + " / " ;
Log.d(TAG, " url is " + url);
HttpPost httpPost = new HttpPost(url);
if (params != null && params.size() > 0 ) {
HttpEntity entity = new UrlEncodedFormEntity(params, " UTF-8 " );
httpPost.setEntity(entity);
}
DefaultHttpClient httpClient = new DefaultHttpClient();
// 添加Cookie
if (cookieStore != null ) {
httpClient.setCookieStore(cookieStore);
}
HttpResponse httpResponse = httpClient.execute(httpPost);
StringBuilder builder = new StringBuilder();
BufferedReader reader = new BufferedReader( new InputStreamReader(
httpResponse.getEntity().getContent()));
for (String s = reader.readLine(); s != null ; s = reader.readLine()) {
builder.append(s);
}
result = builder.toString();
Log.d(TAG, " result is ( " + result + " ) " );
// 保存Cookie
cookieStore = ((AbstractHttpClient) httpClient).getCookieStore();
} catch (Exception e) {
Log.e(TAG, e.toString());
}
Log.d(TAG, " over " );
return result;
}
public static String invoke(String actionName) {
return invoke(actionName, null );
}
}
注意的是,需要存储一个全局的Cookie,在每次调用前添加上去,再在调用j结束后保存它。
接着,添加两个布局文件:
< LinearLayout xmlns:android ="http://schemas.android.com/apk/res/android"
android:orientation ="vertical" android:layout_width ="fill_parent"
android:layout_height ="fill_parent" >
< TextView android:text ="账号" android:layout_width ="fill_parent"
android:layout_height ="20dip" />
< EditText android:layout_width ="fill_parent" android:hint ="请输入账号"
android:layout_height ="40dip" android:id ="@+id/edtAccount" />
< TextView android:text ="密码" android:layout_width ="fill_parent"
android:layout_height ="20dip" />
< EditText android:layout_width ="fill_parent" android:hint ="请输入密码"
android:password ="true" android:layout_height ="40dip" android:id ="@+id/edtPassword" />
< Button android:text ="登录" android:layout_width ="fill_parent"
android:layout_height ="40dip" android:id ="@+id/btnLogon" />
</ LinearLayout >
< LinearLayout xmlns:android ="http://schemas.android.com/apk/res/android"
android:orientation ="vertical" android:layout_width ="fill_parent"
android:layout_height ="fill_parent" >
< Button android:text ="调用" android:id ="@+id/btnInvoke"
android:layout_width ="fill_parent" android:layout_height ="wrap_content" />
</ LinearLayout >
然后、编写登陆和主Activity
import java.util.ArrayList;
import java.util.List;
import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class LogonActivity extends Activity {
private final String TAG = this .getClass().getSimpleName();
private EditText edtAccount;
private EditText edtPassword;
private Button btnLogon;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
setContentView(R.layout.logon);
edtAccount = (EditText) this .findViewById(R.id.edtAccount);
edtPassword = (EditText) this .findViewById(R.id.edtPassword);
btnLogon = (Button) this .findViewById(R.id.btnLogon);
setButtonOnClick();
}
private void setButtonOnClick() {
this .btnLogon.setOnClickListener( new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
AsyncTask < Void, Void, String > task = new AsyncTask < Void, Void, String > () {
private ProgressDialog progressDialog;
@Override
protected void onPostExecute(String result) {
// TODO Auto-generated method stub
// super.onPostExecute(result);
progressDialog.cancel();
Log.d(TAG, result);
try {
JSONObject json = new JSONObject(result);
String msg = json.getString( " Result " );
// 是否登录
if (json.getBoolean( " IsSuccess " )) {
Toast.makeText(LogonActivity. this , msg,
Toast.LENGTH_SHORT).show();
// 跳转登录Activity
Intent intent = new Intent();
intent.setClass(LogonActivity. this ,
MainActivity. class );
LogonActivity. this .startActivity(intent);
} else {
Toast.makeText(LogonActivity. this , msg,
Toast.LENGTH_SHORT).show();
}
} catch (JSONException e) {
// TODO Auto-generated catch block
Log.e(TAG, e.toString());
}
}
@Override
protected void onPreExecute() {
// TODO Auto-generated method stub
super .onPreExecute();
progressDialog = new ProgressDialog(LogonActivity. this );
progressDialog.setCancelable( false );
progressDialog
.setProgressStyle(ProgressDialog.STYLE_SPINNER);
progressDialog.setTitle( " 登录中,请稍后... " );
progressDialog.show();
}
@Override
protected String doInBackground(Void... arg0) {
// TODO Auto-generated method stub
String account = edtAccount.getText().toString();
String password = edtPassword.getText().toString();
List < NameValuePair > params = new ArrayList < NameValuePair > ();
params.add( new BasicNameValuePair( " account " , account));
params.add( new BasicNameValuePair( " password " , password));
try {
return HttpHelper.invoke( " Register " , params);
} catch (Exception e) {
Log.e(TAG, e.toString());
return null ;
}
}
};
task.execute();
}
});
}
}
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends Activity {
private final String TAG = this .getClass().getSimpleName();
private Button btnInvoke;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
setContentView(R.layout.main);
btnInvoke = (Button) this .findViewById(R.id.btnInvoke);
setInvokeOnClick();
}
private void setInvokeOnClick() {
btnInvoke.setOnClickListener( new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
AsyncTask < Void, Void, String > task = new AsyncTask < Void, Void, String > () {
private ProgressDialog progressDialog;
@Override
protected void onPostExecute(String result) {
// TODO Auto-generated method stub
// super.onPostExecute(result);
progressDialog.cancel();
Log.d(TAG, result);
try {
JSONObject json = new JSONObject(result);
// 是否登录
if (json.getBoolean( " IsAuthenticated " )) {
String msg = json.getString( " Identity " ) + " : "
+ json.getString( " Result " );
Toast.makeText(MainActivity. this , msg,
Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(MainActivity. this ,
json.getString( " Result " ),
Toast.LENGTH_SHORT).show();
// 跳转登录Activity
Intent intent = new Intent();
intent.setClass(MainActivity. this ,
LogonActivity. class );
MainActivity. this .startActivity(intent);
}
} catch (JSONException e) {
// TODO Auto-generated catch block
Log.e(TAG, e.toString());
}
}
@Override
protected void onPreExecute() {
// TODO Auto-generated method stub
super .onPreExecute();
progressDialog = new ProgressDialog(MainActivity. this );
progressDialog.setCancelable( false );
progressDialog
.setProgressStyle(ProgressDialog.STYLE_SPINNER);
progressDialog.setTitle( " 调用中,请稍后... " );
progressDialog.show();
}
@Override
protected String doInBackground(Void... arg0) {
// TODO Auto-generated method stub
try {
return HttpHelper.invoke( " Index " );
} catch (Exception e) {
Log.e(TAG, e.toString());
return null ;
}
}
};
task.execute();
}
});
}
}
最后,设置访问权限:<uses-permission android:name="android.permission.INTERNET" />
四、运行结果
1.进入主MainActivity,图4.1.1所示。
图4.1.1
2.调用Http请求,图4.2.1所示。
图4.2.1
3.由于没有登陆,所以跳转到登陆Activity,图4.3.1所示。
图4.3.1
4.故意输入错误密码使其登陆失败,图4.4.1所示。
图4.4.1
5.输入正确的密码后跳转到主Activity,如图4.5.1所示。
图4.5.1
6.再一次调用Http请求,这次是已登陆状态,图4.6.1所示。
图4.6.1