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

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

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

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

废话不多说,正文开始:

学校官网

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

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

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

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

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

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

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

我们可以发现,其实两者的内容都是一样的,就是=编码的格式不同而已,因此我们只要向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进行信息的交流,比较有代表性)

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

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

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

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

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

我们可以发现的第二个数据,其实就是我们不同设备的局域网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的截图:

贴一下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

最新文章

  1. Deformity ASP/ASPX Webshell、Webshell Hidden Learning
  2. Material Design Button 样式
  3. javascript 图片延迟加载
  4. Thinkphp显示系统常量信息的方法(php的用法)
  5. 负margin新解
  6. Nagiosserver端安装部署具体解释(1)
  7. css去除ios文本框默认圆角
  8. 移动 ProgramData\Package Cache 文件夹
  9. 关于vue
  10. python 分片、截断序列
  11. ADO.NET基础学习 一(连接数据库)
  12. BZOJ.4513.[SDOI2016]储能表(数位DP)
  13. E0264 Unable to execute &#39;&quot;/usr/bin/codesign&quot; ...&#39;
  14. Java 基础 - 对象池
  15. io多路复用的精髓
  16. Weblogic CVE-2018-2894 漏洞复现
  17. 状态保持以及AJAX的初步学习
  18. schedule和scheduleAtFixedRate的区别
  19. java 后台封装json数据学习总结(二)
  20. vue Object.freeze() 优化

热门文章

  1. 表单提交checkbox的值
  2. centos 修改/etc/fstab后无法启动
  3. eclipse修改主题配色
  4. css3实战版的点击列表项产生水波纹动画
  5. Angular - - $cacheFactory
  6. Angular - - ngApp、ngBind、ngBindHtml、ngNonBindable
  7. ThinkPHP创建应用的一般开发流程
  8. Delphi中上指定进程(进程名)
  9. delphi DBgrid应用大全
  10. js原生设计模式——2面向对象编程之闭包1