Android网络开发实例(基于抓包实现的网络模拟登录,登出和强制登出)

时间:2023-03-09 06:04:33
Android网络开发实例(基于抓包实现的网络模拟登录,登出和强制登出)

学习Android有几个月了,最近喜欢上了网络编程,于是想通过Android写一些一个小程序用于连接外网.在这里非常感谢雪夜圣诞的支持,非常感谢,给我打开新的一扇门.

1.声明,本程序只能用于西南大学连接外网登录,其他网站需要自己进行抓包测试.

2.声明,本文更多的是关注网络抓包已经,本地构造,如果有什么错误,请尽情指教,非常感谢.

3.声明,最后源代码,以全部上传github,需要的同志可以自行下载,文章结尾会附带链接.

废话不多说,正文开始:

学校官网

Android网络开发实例(基于抓包实现的网络模拟登录,登出和强制登出)

第一步,首先需要实现的是登录操作:

当我们点击登录外网会出现以下页面:

Android网络开发实例(基于抓包实现的网络模拟登录,登出和强制登出)

这个页面时关键,我们就要在这个页面进行抓包处理.我使用的是chorme浏览器,我打开chrome浏览器的开发者工具,从中选择network进行信息监控以下界面:

Android网络开发实例(基于抓包实现的网络模拟登录,登出和强制登出)

这里需要关注的是,我们需要勾选上Preserve log,这样页面跳转时,发送的信息就不会消失了.然后,我们点击连接按钮,我们我可以发现以下情况:

Android网络开发实例(基于抓包实现的网络模拟登录,登出和强制登出)

其实我们可以发现我们要实现登录按钮,我们需要使用的url就是第一个,我们点击第一个url查看数据包详情,这样子我们就可以知道这个url需要哪些数据:

Android网络开发实例(基于抓包实现的网络模拟登录,登出和强制登出)

这里我们可以发现,其实浏览器是向这个url发送了一个post请求,在post中放置了如下数据(userId,password,service,queryString,operatroPwd,operatorUserId,validcode).

我们很容易就发现userId和passwordId(就是账号和密码),service经过我多次测试并不会改变,应该是固定值,除了queryString之外的属性都是空的.难点就在这个queryString,我们点击view source查看原来编码(这里需要特别注意,浏览器会进行一次编码显示给我们,我们使用的应该是source原来的value值)

Android网络开发实例(基于抓包实现的网络模拟登录,登出和强制登出)

我们可以发现,其实两者的内容都是一样的,就是=编码的格式不同而已,因此我们只要向http://222.198.127.170/发送一个get请求,然后把对应的内容截取出来就可以了.

因此登录很简单了,网址有了,填充的数据也知道了,我只要发送一个post请求就可以实现登录功能了.这里贴一下登录函数的代码

    //进行登录操作
private boolean loginValidate(String username,String passwd) throws Exception
{
final String html = HttpUtil.sendGetRequest("http://222.198.127.170/", false, null, "gbk");
//使用正则表达式获取对应的填充数据
String p = "jsp\\?(.+?)'</script>";
Pattern reg = Pattern.compile(p);
Matcher m= reg.matcher(html);
String FillingStr = "";
if(m.find())
{
FillingStr = m.group(1);
}
//这里需要注意,需要使用utf-8格式进行编码
FillingStr = URLEncoder.encode(FillingStr,"utf-8");
final String url = "http://222.198.127.170/eportal/InterFace.do?method=login";
final String data="userId="+username+"&password="+passwd+"&service=%25E9%25BB%2598%25E8%25AE%25A4&queryString="+FillingStr+"&operatorPwd=&operatorUserId=&validcode=";
//发送登录请求
String html2=HttpUtil.sendPostRequest(url, data, false, null, "gbk");
if(html2.contains("success"))
return true;
return false;
}

HttpUtil是我自己写的一个发送Http请求的工具类,我把工具类列出来,github中有源码,需要的可以进行查阅.

package com.network.cjyong.networklogin.util;

/**
* Created by cjyong on 2017/3/5.
*/ import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask; public class HttpUtil
{
/**
* 向对应的网址发送get请求,以String的形式返回服务器的相应
*
* @author cjyong at 2017/3/5
* @param url 发送请求的网址
* @param usecookie 是否使用cookie
* @param cookie 需要携带的cookie
* @param encoding 编码格式
* @return 以string的形式返回服务器的响应
* @throws Exception
*/
public static String sendGetRequest(final String url,final boolean usecookie,final String cookie,final String encoding) throws Exception
{
FutureTask<String> task = new FutureTask<String>(
new Callable<String>()
{
@Override
public String call() throws Exception
{
URL turl = new URL(url);
HttpURLConnection conn = (HttpURLConnection) turl.openConnection();
//设置时间限制,抛出异常
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
conn.setRequestProperty("User-Agent", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
if(usecookie)
conn.setRequestProperty("Cookie", cookie);
InputStream is = conn.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is,encoding));
StringBuilder sb = new StringBuilder();
String line = null;
while((line = reader.readLine())!= null)
sb.append(line+"\n");
return sb.toString();
}
});
//格外进行一个线程进行网络操作,防止堵塞
new Thread(task).start();
return task.get();
} /**
* 向对应的网址发送post请求,以String的形式返回服务器的相应
*
* @author cjyong at 2017/3/5
* @param url 发送请求的网址
* @param data 发送post请求携带的数据
* @param usecookie 是否使用cookie
* @param cookie 需要携带的cookie
* @param encoding 编码格式
* @return 以string的形式返回服务器的响应
* @throws Exception
*/
public static String sendPostRequest(final String url,final String data,final boolean usecookie,final String cookie,final String encoding) throws Exception
{
FutureTask<String> task = new FutureTask<String>(
new Callable<String>()
{
@Override
public String call() throws Exception
{
URL turl = new URL(url);
HttpURLConnection conn = (HttpURLConnection) turl.openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
//设置时间限制,抛出异常
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
conn.setRequestProperty("User-Agent", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
if(usecookie)
conn.setRequestProperty("Cookie", cookie);
OutputStream outStream = conn.getOutputStream();
outStream.write(data.getBytes());
outStream.flush();
outStream.close();
InputStream is = conn.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is,encoding));
StringBuilder sb = new StringBuilder();
String line = null;
while((line = reader.readLine())!= null)
sb.append(line+"\n");
return sb.toString();
}
});
//格外进行一个线程进行网络操作,防止堵塞
new Thread(task).start();
return task.get();
} /**
* 向对应的网址发送post请求,获取对应的cookie,以备后用
*
* @author cjyong at 2017/3/5
* @param url 发送请求的网址
* @param data 发送post请求携带的数据
* @return 以string的形式返回服务器的响应
* @throws Exception
*/ public static String getCookie(final String url,final String data)throws Exception
{
FutureTask<String> task = new FutureTask<String>(
new Callable<String>()
{ @Override
public String call() throws Exception
{
byte[] Data = data.getBytes();
URL turl=new URL(url);
HttpURLConnection conn = (HttpURLConnection)turl.openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
//设置连接与读取时间过期返回异常
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
conn.setRequestProperty("User-Agent", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
OutputStream outStream = conn.getOutputStream();
outStream.write(Data);
outStream.flush();
outStream.close();
String Cookie=conn.getHeaderField("Set-Cookie");
return Cookie;
} }
);
//格外进行一个线程进行网络操作,防止堵塞
new Thread(task).start();
return task.get();
} }

登出功能类似,我这里就不在赘述了,贴一下登出函数的代码:

    //进行登出操作
private boolean logoutValidate() throws Exception
{
String html = HttpUtil.sendGetRequest("http://222.198.127.170/eportal/InterFace.do?method=logout",false,null,"utf-8");
if(html.contains("success"))
return true;
return false;
}

最后来讲一下,强制下线功能的实现(这个需要用到cookie进行信息的交流,比较有代表性)

Android网络开发实例(基于抓包实现的网络模拟登录,登出和强制登出)

在学校网络管理中心,有校园网推出选项,当我们点击时,会出现如下情况:

Android网络开发实例(基于抓包实现的网络模拟登录,登出和强制登出)

这说明,强制下线并不是单纯向一个url发送一个链接就可以完成的,在这里我们就需要进行抓包处理:

Android网络开发实例(基于抓包实现的网络模拟登录,登出和强制登出)

这里我们可以发现,这里发送的请求也很简单,就是向login_judge.jsf发送一个post请求,post中数据也很简单,就两个内容(name,password)

然后我们点击我的设备,就可以进行退出网络操作了.我们截取一下包:

Android网络开发实例(基于抓包实现的网络模拟登录,登出和强制登出)

Android网络开发实例(基于抓包实现的网络模拟登录,登出和强制登出)

我们可以发现,这里需要向userself_ajax.jsf?methodName=xxxxx,发送一个post请求,其中数据特别简单一个key和一串数字,并没有用户名和密码,而Cookie中出现了,很明显2个页面之间的交流是通过cookie来是实现的,所以我们需要在登录的页面获取对应的cookie进行编辑,向这个url发送post请求.难点在于,封装的第二个数据是什么?这里就要进行苦逼的网页代码查询了,我们点开onlinedevice_list.jsf进行代码查询: (由于网页代码太长了,我截取一部分有用的进行分享)

Android网络开发实例(基于抓包实现的网络模拟登录,登出和强制登出)

我们可以发现的第二个数据,其实就是我们不同设备的局域网ip地址,这样子,数据也获取到了,cookie也得到了,我们只要向指定url发送post请求就可以了.

这里贴一下强制下线函数的代码:

 //进行强制下线操作
private boolean forceLogoutValidate(String username,String passwd) throws Exception
{
//构造填充参数
String data ="name="+username+"&password="+passwd;
String url= "http://service2.swu.edu.cn/selfservice/module/scgroup/web/login_judge.jsf";
//构造cookie
String Cookie=HttpUtil.getCookie(url,data);
Cookie=String.format(Cookie+" rmbUser=true; userName=%s; passWord=%s; oldpassWord=%s;", username,passwd,passwd);
String listurl= "http://service2.swu.edu.cn/selfservice/module/webcontent/web/onlinedevice_list.jsf";
String html= HttpUtil.sendGetRequest(listurl, true, Cookie, "gbk");
//账号密码错误
if(html.contains("您还未登录或会话过期"))
return false;
//获取设备的IP地址构造填充数据
String p = "<span id=\"a1\">IP : (.+?)</span >";
Pattern reg = Pattern.compile(p);
Matcher m=reg.matcher(html);
//将所有的设备进行下线
while(m.find())
{
//执行下线操作
String myurl = "http://service2.swu.edu.cn/selfservice/module/userself/web/userself_ajax.jsf?methodName=indexBean.kickUserBySelfForAjax";
String mydata = "key= "+username+":" +m.group(1);
HttpUtil.sendPostRequest(myurl, mydata, true, Cookie, "utf-8");
}
return true;
}

到这里,所有的重要的函数和抓包方法都已经讲解完毕,最后贴一下手机APP的截图:

Android网络开发实例(基于抓包实现的网络模拟登录,登出和强制登出)

贴一下MainActivity的代码:

package com.network.cjyong.networklogin;

import android.app.AlertDialog;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import java.net.URLEncoder;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.network.cjyong.networklogin.util.HttpUtil; public class MainActivity extends AppCompatActivity
{
EditText etUsername,etUserpass;
Button login,cancel,logout,forceout,help;
SharedPreferences preferences;
SharedPreferences.Editor editor;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//绑定组件
bindCompoent();
//程序初始化
init();
} //绑定各种主键并设置好监听器
private void bindCompoent()
{
//绑定各类主键
etUsername = (EditText) findViewById(R.id.userEditText);
etUserpass = (EditText) findViewById(R.id.pwdEditText);
login = (Button) findViewById(R.id.bnLogin);
cancel = (Button) findViewById(R.id.bnCancel);
logout = (Button) findViewById(R.id.bnLogout);
forceout = (Button) findViewById(R.id.bnForceLogout);
help = (Button) findViewById(R.id.bnHelp); //给取消按钮绑定监听器
cancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
etUsername.setText(null);
etUserpass.setText(null);
}
}); //给登录按钮监听器
login.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//验证是否连接网络和输入是否合理
if(validate())
{
//发送登录请求
String username = etUsername.getText().toString();
String userpasswd = etUserpass.getText().toString();
try {
if(loginValidate(username,userpasswd))
{
Toast.makeText(getApplicationContext(),
"登录成功",Toast.LENGTH_LONG).
show();
login.setEnabled(false);
logout.setEnabled(true);
//将正确的账号和密码存储到本地中去
save();
}
else
{
Toast.makeText(getApplicationContext(),
"登录失败,请检查你的用户名和密码是否正确",
Toast.LENGTH_SHORT).
show();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}); //给登出按钮设置监听器
logout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//验证是否连接网络和输入是否合理
if(validate())
{
//发送登录请求
String username = etUsername.getText().toString();
String userpasswd = etUserpass.getText().toString();
try {
if(logoutValidate())
{
Toast.makeText(getApplicationContext(),
"登出成功",
Toast.LENGTH_SHORT).
show();
logout.setEnabled(false);
login.setEnabled(true);
}
else
{
Toast.makeText(getApplicationContext(),
"登出失败,请检查你的用户名和密码是否正确",
Toast.LENGTH_SHORT).
show();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}); //给强制登出按钮设置监听器
forceout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//验证是否连接网络和输入是否合理
if(validate())
{
//发送登录请求
String username = etUsername.getText().toString();
String userpasswd = etUserpass.getText().toString();
try {
if(forceLogoutValidate(username,userpasswd))
{
Toast.makeText(getApplicationContext(),
"下线成功",
Toast.LENGTH_SHORT).
show();
logout.setEnabled(false);
login.setEnabled(true);
}
else
{
Toast.makeText(getApplicationContext(),
"下线失败,请检查你的用户名和密码是否正确",
Toast.LENGTH_SHORT).
show();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}); help.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new AlertDialog.Builder(MainActivity.this).
setTitle("帮助界面").
setMessage("本软件适合在西南大学一键登录外网 \n--------by cjyong\n" +
"强制退出按钮会强制退出所有当前账号登录的设备,请谨慎使用\n"+
"如果软件有问题,请联系QQ2686600303\n").
setNegativeButton("取消",null).
show();
}
});
} private void init()
{
//判断以前是否登录过,通过存储在本地的记录进行判断
isOldUser(); //判断网络是否连接正确,是否可以联网
wifiIsGood(); } //检查网络是否可以正确连接
private void wifiIsGood()
{
//判断wifi是否连接
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
NetworkInfo.State wifi = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).getState();
if(wifi== NetworkInfo.State.DISCONNECTED)
{
Toast.makeText(this,"请连接好WiFi再进行登录",Toast.LENGTH_SHORT).show();
return ;
} //判断是否可以上网
try
{
//前往bibili网站
String html = HttpUtil.sendGetRequest("http://www.bilibili.com", false, null, "utf-8");
if(html.contains("http://222.198.127.170/"))
{
return;
}
else
{
Toast.makeText(this,"你的WiFi已经可以上网,不用登陆",Toast.LENGTH_LONG).show();
return;
}
}
catch (Exception e)
{
e.printStackTrace();
}
} //通过遍历本地记录,进行判断是否之前登录过,如果登录自动填充两个对话框
private void isOldUser()
{
//这里通过preference进行储存相应数据
preferences = getSharedPreferences("userpass",0);
editor = preferences.edit();
String username = preferences.getString("username", null);
String userpass = preferences.getString("userpass",null); if(username != null && userpass!= null)
{
etUsername.setText(username);
etUserpass.setText(userpass);
}
return;
} //检查wifi状况和输入情况
private boolean validate()
{
String username = etUsername.getText().toString();
String userpass = etUserpass.getText().toString();
if(username.equals("") || userpass.equals(""))
{
Toast.makeText(this,"用户名或者密码不可以为空,请重新输入",Toast.LENGTH_SHORT).show();
return false;
}
return true;
} //将正确的账号和密码存储到本地中去
private void save()
{
String username = etUsername.getText().toString();
String userpass = etUserpass.getText().toString();
editor.putString("username",username);
editor.putString("userpass",userpass);
editor.commit();
} //进行登录操作
private boolean loginValidate(String username,String passwd) throws Exception
{
final String html = HttpUtil.sendGetRequest("http://222.198.127.170/", false, null, "gbk");
//使用正则表达式获取对应的填充数据
String p = "jsp\\?(.+?)'</script>";
Pattern reg = Pattern.compile(p);
Matcher m= reg.matcher(html);
String FillingStr = "";
if(m.find())
{
FillingStr = m.group(1);
}
//这里需要注意,需要使用utf-8格式进行编码
FillingStr = URLEncoder.encode(FillingStr,"utf-8");
final String url = "http://222.198.127.170/eportal/InterFace.do?method=login";
final String data="userId="+username+"&password="+passwd+"&service=%25E9%25BB%2598%25E8%25AE%25A4&queryString="+FillingStr+"&operatorPwd=&operatorUserId=&validcode=";
//发送登录请求
String html2=HttpUtil.sendPostRequest(url, data, false, null, "gbk");
if(html2.contains("success"))
return true;
return false;
} //进行登出操作
private boolean logoutValidate() throws Exception
{
String html = HttpUtil.sendGetRequest("http://222.198.127.170/eportal/InterFace.do?method=logout",false,null,"utf-8");
if(html.contains("success"))
return true;
return false;
} //进行强制下线操作
private boolean forceLogoutValidate(String username,String passwd) throws Exception
{
//构造填充参数
String data ="name="+username+"&password="+passwd;
String url= "http://service2.swu.edu.cn/selfservice/module/scgroup/web/login_judge.jsf";
//构造cookie
String Cookie=HttpUtil.getCookie(url,data);
Cookie=String.format(Cookie+" rmbUser=true; userName=%s; passWord=%s; oldpassWord=%s;", username,passwd,passwd);
String listurl= "http://service2.swu.edu.cn/selfservice/module/webcontent/web/onlinedevice_list.jsf";
String html= HttpUtil.sendGetRequest(listurl, true, Cookie, "gbk");
//账号密码错误
if(html.contains("您还未登录或会话过期"))
return false;
//获取设备的IP地址构造填充数据
String p = "<span id=\"a1\">IP : (.+?)</span >";
Pattern reg = Pattern.compile(p);
Matcher m=reg.matcher(html);
//将所有的设备进行下线
while(m.find())
{
//执行下线操作
String myurl = "http://service2.swu.edu.cn/selfservice/module/userself/web/userself_ajax.jsf?methodName=indexBean.kickUserBySelfForAjax";
String mydata = "key= "+username+":" +m.group(1);
HttpUtil.sendPostRequest(myurl, mydata, true, Cookie, "utf-8");
}
return true;
} }

贴一下github地址(欢迎补充):

https://github.com/cai123nb/NetworkLogin/tree/master/main

讲点废话,其实我们可以看出,编码并不困难,困难的使我们怎么抓取准确的网址和数据包,怎么填充正确的数据包.

只要我们这一点学习的好的话,,我们可以拓展开来,抓手机号码的归属地,邮件/快递的送达地址等,都是可以的.

第二,其实我的HttpUtil有点过时了,现在大多数的人都是使用HttpClient,因为HttpClient支持https,我用老版的用顺手,也就没有换了,如果

有人有不同的思路欢迎补充.在这里,抛砖引玉了,你我共勉.

非常感谢,阅读.

17:30:14