前言
众所周知,许多大学的无线网都是没有密码的,取而代之的则是Web认证(portal)的方式,输入用户名和密码轻松实现网络访问的控制,如下图所示(图源网络):

那么这种认证方式有一个坏处就是经常掉线,每次我们手动去网页端使用用户名和密码登录很麻烦,于是就诞生了校园网自动登录的这个软件。
原理讲解
网页认证无非就是一个html页面,用于展现给用户输入用户名密码,把用户输入的信息通过POST方式发送给后端服务器进行验证,验证通过就允许上网,反之则无法上网,和大多数网站的登录机制类似。
校园网自动登录程序介绍
1、程序流程

2、所使用的技术
- GET请求——用于判断电脑是否可以访问互联网
- POST请求——用于发送数据给认证服务器
- Swing窗口——用于展示提示信息
- properties配置文件——用于存储用户信息
- Socket通信——测试与认证服务器的通断
- 多线程——保证窗口不会假死
- URL分割——提取认证网址的信息
一、GET请求测试是否正常联网
这个是Java自带的库,可以直接使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import java.net.HttpURLConnection; import java.net.URL;
public class HTTPGet { public static int testWsdlConnection(String address) throws Exception { int status = 404; try { URL urlObj = new URL(address); HttpURLConnection oc = (HttpURLConnection) urlObj.openConnection(); oc.setUseCaches(false); oc.setConnectTimeout(3000); status = oc.getResponseCode(); if (200 == status) { return status; } } catch (Exception e) { e.printStackTrace(); throw e; } return status;
} }
|
二、发送POST请求
这里需要用到httpclient
这个库,我们需要在Maven里添加这个库。
1 2 3 4 5
| <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.3.3</version> </dependency>
|
基本使用
定义一个sendPOST的函数,有需要可以直接调用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| import org.apache.http.HttpEntity; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils;
import java.util.ArrayList; import java.util.List; import java.util.Map;
public class HTTPPost {
public static String sendPost(String url, Map<String,String> headerMap, Map<String,String> bodyMap){ HttpPost post = new HttpPost(url); try { List<BasicNameValuePair> list = new ArrayList<BasicNameValuePair>(); if (bodyMap!=null){ for (String str:bodyMap.keySet()) { list.add(new BasicNameValuePair(str, bodyMap.get(str))); } } post.setEntity(new UrlEncodedFormEntity(list)); if (headerMap!=null){ for (String str:headerMap.keySet() ) { post.addHeader(str,headerMap.get(str)); } } CloseableHttpClient client = HttpClients.createDefault(); CloseableHttpResponse response = client.execute(post); HttpEntity entity = response.getEntity(); return EntityUtils.toString(entity, "UTF-8"); } catch (Exception e1) { e1.printStackTrace(); return ""; } } }
|
校园网登录
根据sendPOST函数,我们需要先准备url,header,body。这些参数都很容易在浏览器的开发人员工具
中找到。

如果浏览器的开发者工具不能满足你的话,建议使用专业的抓包工具。

都获取好之后,我们现把它存入一个字符串中,然后我们把它转化为Map<String,String>
的形式。这里我们需要用到马爸爸开发的fastjson
组件,同样在Maven里添加这个库。
1 2 3 4 5
| <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.66</version> </dependency>
|
整体代码如下
1 2 3 4 5 6 7 8 9
| String header = "{\"Cache-Control\": \"max-age=0\", \"Upgrade-Insecure-Requests\": \"1\", \"Origin\": \"\", \"Content-Type\": \"application/x-www-form-urlencoded\", \"User-Agent\": \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.66 Safari/537.36 Edg/103.0.1264.44\", \"Accept\": \"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\", \"Referer\": \"\", \"Accept-Encoding\": \"gzip, deflate\", \"Accept-Language\": \"zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6\", \"Connection\": \"close\"}"; Map<String, String> mapheader = JSONObject.parseObject(header, new TypeReference<Map<String, String>>(){}); Map<String, String> params = new HashMap<String, String>(){{ put("R1", "0"); }};
params.put("DDDDD", name); params.put("upass", b64.decode2(passwd)); String htmlStr = HTTPPost.sendPost(Path.trim(), mapheader, params);
|
Java爬虫
发送完POST请求之后我们需要判断是否登录成功,由于我们学校的登录返回值并不是一个JSON列表,直接就是一个HTML的文件,最显著的特征就是网页的标题:认证成功页
,我们需要通过这个标题来判断是否成功登录,因此这里需要用到Java的爬虫。
1 2 3
| Document doc = Jsoup.parse(htmlStr); String e_title = doc.title(); return Objects.equals(e_title, "认证成功页");
|
这里有一个大坑啊,字符串的比较用e_title=="认证成功页"
死活不好使,后来问了我们老师才知道,Java的字符串比较的方式。

三、读取配置文件
这里使用的是Java的properties
配置文件,用户名和密码均存储在配置文件里。

读取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| import java.io.*; import java.util.ArrayList; import java.util.List; import java.util.Properties;
public class ReadPropertise { private static Properties properties = new Properties(); private static BufferedInputStream in; public static void Readin() throws Exception{ try { in = new BufferedInputStream(new FileInputStream("c.properties")); } catch (FileNotFoundException e) { throw new RuntimeException(e); } try { properties.load(in); } catch (IOException e) { throw new RuntimeException(e); } } public static List getUser(){ String name = properties.getProperty("name"); String passwd = properties.getProperty("password"); List map = new ArrayList(); map.add(name); map.add(passwd); return map; } }
|
写入
从Swing窗口的输入框获取数据并写入到properties文件。

1 2 3 4 5 6 7 8
| Properties properties = new Properties(); properties.setProperty("name", name); properties.setProperty("password", passwd); try { properties.store(new FileWriter("c.properties"), null); } catch (IOException ex) { throw new RuntimeException(ex); }
|
Swing窗口框架
Swing是Java的一个窗口模块,也是自带的,下面将使用注释的方式来给大家讲解Swing。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
| import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.FileWriter; import java.io.IOException; import java.util.Map; import java.util.Objects; import java.util.Properties;
public class LoginSwing { public void placeComponents() { JFrame frame = new JFrame("Login"); frame.setSize(350, 200); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
JLabel userLabel = new JLabel("用户名:"); userLabel.setBounds(10, 20, 60, 25); panel.add(userLabel); JTextField userText = new JTextField(20); userText.setBounds(65, 20, 165, 25); panel.add(userText);
JLabel passwordLabel = new JLabel("密码:"); passwordLabel.setBounds(10, 50, 60, 25); panel.add(passwordLabel);
JPasswordField passwordText = new JPasswordField(20); passwordText.setBounds(65, 50, 165, 25); panel.add(passwordText);
JLabel addLabel = new JLabel("地址:"); addLabel.setBounds(10, 80, 60, 25); panel.add(addLabel);
JTextField addText = new JTextField(20); addText.setBounds(65, 80, 165, 25); panel.add(addText);
JLabel noticeLabel = new JLabel(); noticeLabel.setForeground(Color.red); noticeLabel.setBounds(95, 110, 90, 25); panel.add(noticeLabel);
JButton loginButton = new JButton("登录"); loginButton.setBounds(10, 110, 80, 25); panel.add(loginButton); loginButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { String name = userText.getText().trim(); String passwd = String.valueOf(passwordText.getPassword()); String host = addText.getText().trim(); if ("".equals(name)) { noticeLabel.setText("请输入用户名!"); } else if ("".equals(passwd)) { noticeLabel.setText("请输入密码!"); } else if ("".equals(host)) { noticeLabel.setText("请输入地址!"); } else { noticeLabel.setText("登录成功"); frame.dispose(); } } }); frame.add(panel); frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE); panel.setLayout(null); frame.setVisible(true); } }
|
Socket测试
在登录之前现检测一下学校的认证服务器是否好使,同样只是用了Java的内置库。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket;
public class SocketTest { public static boolean isHostConnectable(String host, int port) { Socket socket = new Socket(); try { socket.connect(new InetSocketAddress(host, port)); } catch (IOException e) { return false; } finally { try { socket.close(); } catch (IOException e) { } } return true; } }
|
Java的多线程使用
基本使用
1 2 3 4 5
| new Thread(new Runnable() { @Override public void run() { } }.start()
|
案例:睡眠排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class Main { public static void main(String[] args) { int[] nums = new int[]{235, 233, 110, 789, 5, 0, 1}; for (int item : nums) { new Thread(() -> { try { Thread.sleep(item); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(item); }).start(); } } }
|
生产环境千万不要使用这个啊,这串代码没个几十年脑血栓还真想不出来,睡眠排序暴露的问题也很明显:
- 没有办法睡眠负数。
- 相近的数值之间会有误差。
- 你要是给了个很大的数让它睡去吧,脑血栓治好了估计都睡不完。
下载
下载地址1:
链接: https://pan.baidu.com/s/1TnHwBci5RD_67hNRG8XSUQ
提取码: afi7
源码下载