一、路由器刷入openwrt系统
1.开Telnet
双击运行 1.V2版开Telnet.bat程序
第一个为路由器ip
第二个为当前设备的ip
密码为路由器管理员密码
2.连接路由器,备份固件
直接打开刷机工具包里的putty工具
双击R4A进入即可
账户密码均为root
备份编程器固件
cat /proc/mtd&&dd if=/dev/mtd0 of=/tmp/ALL_backup.bin&&dd if=/dev/mtd4 of=/tmp/eeprom.bin
使用FlashFXP 下载备份固件
保存tmp目录里的 ALL_backup.bin 和 eeprom.bin 到电脑.
同时将breed.bin上传到tmp目录
3.刷入breed
验证breed.bin的MD5数值
md5sum /tmp/breed.bin
MD5值应当为 24e62762809c15ba3872e610a37451a3
使用命令
mtd write /tmp/breed.bin Bootloader
刷入breed固件
使用命令 reboot 重启
路由器重启完成后浏览器192.168.1.1进入管理界面
4.刷入openwrt
点击 固件更新-常规固件
选择好固件(.bin文件)和EEPROM(之前备份的eeprom.bin) 点击上传即可
确认一遍是否正确 点击更新
进度条跑完即可等待路由器重启啦!
如果路由器一直闪黄灯进入安全模式,可以拔掉电源硬重启一次
5.进入系统
192.168.31.1进入openwrt管理界面
二、无网线接口配置方法
网络->无线->扫描
选择需要连接的网络
默认提交
确认为客户端模式,且网络接口为之前创建的
三、获取认证接口 构建认证请求
通过抓包测试,可知登录 注销等接口
1.登录接口
http://210.28.39.251:801/eportal/?user_account=<用户名>&wlan_user_ip=<当前设备IP>&login_method=1&user_password=<密码>&c=Portal&a=login
Host: 210.28.39.251:801
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 Edg/139.0.0.0
Referer: http://210.28.39.251/
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
2.注销接口
http://210.28.39.251:801/eportal/?c=Portal&a=logout&ac_logout=0®ister_mode=1&wlan_user_ip=<当前设备IP>&user_account=drcom
Host: 210.28.39.251:801
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 Edg/139.0.0.0
Accept: */*
Referer: http://210.28.39.251/
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
3.构造认证请求文件
根据个人需求编写auto_login.sh脚本
#!/bin/sh
# 校园网认证脚本
# 首先获取当前WAN口IP,然后使用该IP进行认证
# 设置日志文件路径
LOG_FILE="/tmp/auto_login.log"
# 获取WWAN接口的IP地址
# 假设你的无线客户端接口是wwan(根据第一步配置)
CURRENT_IP=$(ifconfig wwan 2>/dev/null | awk '/inet addr/{print substr($2,6)}')
USERNAME=""
PASSWORD=""
# 如果没有获取到IP,尝试使用其他方法
if [ -z "$CURRENT_IP" ]; then
CURRENT_IP=$(ubus call network.interface.wwan status 2>/dev/null | jsonfilter -e '@["ipv4-address"][0].address')
fi
# 如果仍然没有IP,记录错误并退出
if [ -z "$CURRENT_IP" ]; then
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 错误: 无法获取WAN口IP地址" >> $LOG_FILE
exit 1
fi
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 获取到IP: $CURRENT_IP" >> $LOG_FILE
# 构造认证URL
AUTH_URL="http://210.28.39.251:801/eportal/?user_account=${USERNAME}&wlan_user_ip=${CURRENT_IP}&login_method=1&user_password=${PASSWORD}&c=Portal&a=login"
# 发送第一次认证请求
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 发送第一次认证请求..." >> $LOG_FILE
RESPONSE=$(curl -s -X GET \
"$AUTH_URL" \
-H "Host: 210.28.39.251:801" \
-H "Connection: keep-alive" \
-H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 Edg/139.0.0.0" \
-H "Referer: http://210.28.39.251/" \
-H "Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6")
# 记录第一次响应
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 第一次认证响应: $RESPONSE" >> $LOG_FILE
# 解析第一次响应结果
if echo "$RESPONSE" | grep -q '"result":"1"'; then
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 第一次认证成功" >> $LOG_FILE
exit 0
elif echo "$RESPONSE" | grep -q '"result":"0"'; then
if echo "$RESPONSE" | grep -q '"ret_code":2'; then
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 已经在线,无需重复认证" >> $LOG_FILE
exit 0
else
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 第一次认证失败,等待2秒后重试..." >> $LOG_FILE
sleep 2
# 发送第二次认证请求
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 发送第二次认证请求..." >> $LOG_FILE
RESPONSE=$(curl -s -X GET \
"$AUTH_URL" \
-H "Host: 210.28.39.251:801" \
-H "Connection: keep-alive" \
-H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 Edg/139.0.0.0" \
-H "Referer: http://210.28.39.251/" \
-H "Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6")
# 记录第二次响应
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 第二次认证响应: $RESPONSE" >> $LOG_FILE
# 解析第二次响应结果
if echo "$RESPONSE" | grep -q '"result":"1"'; then
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 第二次认证成功" >> $LOG_FILE
exit 0
elif echo "$RESPONSE" | grep -q '"result":"0"' && echo "$RESPONSE" | grep -q '"ret_code":2'; then
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 第二次尝试时已经在线" >> $LOG_FILE
exit 0
else
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 第二次认证失败: $RESPONSE" >> $LOG_FILE
exit 1
fi
fi
else
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 未知响应,等待2秒后重试..." >> $LOG_FILE
sleep 2
# 发送第二次认证请求
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 发送第二次认证请求..." >> $LOG_FILE
RESPONSE=$(curl -s -X GET \
"$AUTH_URL" \
-H "Host: 210.28.39.251:801" \
-H "Connection: keep-alive" \
-H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 Edg/139.0.0.0" \
-H "Referer: http://210.28.39.251/" \
-H "Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6")
# 记录第二次响应
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 第二次认证响应: $RESPONSE" >> $LOG_FILE
# 解析第二次响应结果
if echo "$RESPONSE" | grep -q '"result":"1"'; then
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 第二次认证成功" >> $LOG_FILE
exit 0
elif echo "$RESPONSE" | grep -q '"result":"0"' && echo "$RESPONSE" | grep -q '"ret_code":2'; then
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 第二次尝试时已经在线" >> $LOG_FILE
exit 0
else
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 第二次认证失败: $RESPONSE" >> $LOG_FILE
exit 1
fi
fi
{/collapse-item}
{collapse-item label="9月11更新auto_login.sh"}
#!/bin/sh
# 校园网认证脚本
# 首先获取当前WAN口IP,然后使用该IP进行认证
# 设置日志文件路径
LOG_FILE="/tmp/auto_login.log"
# 获取WWAN接口的IP地址
# 假设你的无线客户端接口是wwan(根据第一步配置)
CURRENT_IP=$(ifconfig wwan 2>/dev/null | awk '/inet addr/{print substr($2,6)}')
USERNAME=""
PASSWORD=""
# 如果没有获取到IP,尝试使用其他方法
if [ -z "$CURRENT_IP" ]; then
CURRENT_IP=$(ubus call network.interface.wwan status 2>/dev/null | jsonfilter -e '@["ipv4-address"][0].address')
fi
# 如果仍然没有IP,记录错误并退出
if [ -z "$CURRENT_IP" ]; then
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 错误: 无法获取WAN口IP地址" >> $LOG_FILE
exit 1
fi
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 获取到IP: $CURRENT_IP" >> $LOG_FILE
# 构造认证URL
AUTH_URL="http://210.28.39.251:801/eportal/?user_account=${USERNAME}&wlan_user_ip=${CURRENT_IP}&login_method=1&user_password=${PASSWORD}&c=Portal&a=login"
# 发送第一次认证请求
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 发送第一次认证请求..." >> $LOG_FILE
RESPONSE=$(curl -s -X GET \
"$AUTH_URL" \
-H "Host: 210.28.39.251:801" \
-H "Connection: keep-alive" \
-H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 Edg/139.0.0.0" \
-H "Referer: http://210.28.39.251/" \
-H "Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6")
# 记录第一次响应
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 第一次认证响应: $RESPONSE" >> $LOG_FILE
# 解析第一次响应结果
if echo "$RESPONSE" | grep -q '"result":"1"'; then
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 第一次认证成功" >> $LOG_FILE
exit 0
elif echo "$RESPONSE" | grep -q '"result":"0"'; then
if echo "$RESPONSE" | grep -q '"ret_code":2'; then
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 已经在线,无需重复认证" >> $LOG_FILE
exit 0
else
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 第一次认证失败,等待5秒后重试..." >> $LOG_FILE
sleep 5
# 发送第二次认证请求
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 发送第二次认证请求..." >> $LOG_FILE
RESPONSE=$(curl -s -X GET \
"$AUTH_URL" \
-H "Host: 210.28.39.251:801" \
-H "Connection: keep-alive" \
-H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 Edg/139.0.0.0" \
-H "Referer: http://210.28.39.251/" \
-H "Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6")
# 记录第二次响应
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 第二次认证响应: $RESPONSE" >> $LOG_FILE
# 解析第二次响应结果
if echo "$RESPONSE" | grep -q '"result":"1"'; then
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 第二次认证成功" >> $LOG_FILE
exit 0
elif echo "$RESPONSE" | grep -q '"result":"0"' && echo "$RESPONSE" | grep -q '"ret_code":2'; then
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 第二次尝试时已经在线" >> $LOG_FILE
exit 0
else
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 第二次认证失败: $RESPONSE;当前IP为:$CURRENT_IP" >> $LOG_FILE
exit 1
fi
fi
else
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 未知响应,等待5秒后重试...;当前IP为:$CURRENT_IP;URL为:$AUTH_URL" >> $LOG_FILE
sleep 5
# 发送第二次认证请求
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 发送第二次认证请求..." >> $LOG_FILE
RESPONSE=$(curl -s -X GET \
"$AUTH_URL" \
-H "Host: 210.28.39.251:801" \
-H "Connection: keep-alive" \
-H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 Edg/139.0.0.0" \
-H "Referer: http://210.28.39.251/" \
-H "Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6")
# 记录第二次响应
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 第二次认证响应: $RESPONSE" >> $LOG_FILE
# 解析第二次响应结果
if echo "$RESPONSE" | grep -q '"result":"1"'; then
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 第二次认证成功" >> $LOG_FILE
exit 0
elif echo "$RESPONSE" | grep -q '"result":"0"' && echo "$RESPONSE" | grep -q '"ret_code":2'; then
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 第二次尝试时已经在线" >> $LOG_FILE
exit 0
else
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 第二次认证失败: $RESPONSE;当前IP为:$CURRENT_IP" >> $LOG_FILE
exit 1
fi
fi
{/collapse-item}
{collapse-item label="9月14更新auto_login.sh
IP地址变化导致,构建的URL不正确,更新每次认证前,重新获取一次IP"}
#!/bin/sh
# 校园网认证脚本
# 首先获取当前WAN口IP,然后使用该IP进行认证
USER_ACCOUNT=""
USER_PASSWORD=""
AUTH_SERVER=""
# HTTP请求头部定义
HOST_HEADER=""
REFERER_HEADER=""
USER_AGENT="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 Edg/139.0.0.0"
CONNECTION="keep-alive"
ACCEPT_LANGUAGE="zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6"
# 设置日志文件路径
LOG_FILE="/tmp/auto_login.log"
# 函数:获取当前WWAN接口的IP地址
get_current_ip() {
# 尝试多种方法获取IP地址
local ip=$(ifconfig wwan 2>/dev/null | awk '/inet addr/{print substr($2,6)}')
# 如果没有获取到IP,尝试使用其他方法
if [ -z "$ip" ]; then
ip=$(ubus call network.interface.wwan status 2>/dev/null | jsonfilter -e '@["ipv4-address"][0].address')
fi
# 如果依然获取不到,尝试使用ip命令
if [ -z "$ip" ]; then
ip=$(ip -4 addr show dev wwan 2>/dev/null | grep -oE 'inet [0-9.]+' | cut -d' ' -f2)
fi
# 如果仍然没有IP,记录错误
if [ -z "$ip" ]; then
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 错误: 无法获取WAN口IP地址" >> $LOG_FILE
echo "" # 返回空字符串
else
echo "$ip"
fi
}
# 函数:发送认证请求
send_auth_request() {
local ip=$1
local attempt=$2
local auth_url="http://${AUTH_SERVER}/eportal/?user_account=${USER_ACCOUNT}&wlan_user_ip=${ip}&login_method=1&user_password=${USER_PASSWORD}&c=Portal&a=login"
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 发送第${attempt}次认证请求..." >> $LOG_FILE
# echo "[$(date +'%Y-%m-%d %H:%M:%S')] 认证URL: $auth_url" >> $LOG_FILE
RESPONSE=$(curl -s -X GET \
"$auth_url" \
-H "Host: $HOST_HEADER" \
-H "Connection: $CONNECTION" \
-H "User-Agent: $USER_AGENT" \
-H "Referer: $REFERER_HEADER" \
-H "Accept-Language: $ACCEPT_LANGUAGE")
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 第${attempt}次认证响应: $RESPONSE" >> $LOG_FILE
# 解析响应结果
if echo "$RESPONSE" | grep -q '"result":"1"'; then
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 第${attempt}次认证成功" >> $LOG_FILE
return 0
elif echo "$RESPONSE" | grep -q '"result":"0"' && echo "$RESPONSE" | grep -q '"ret_code":2'; then
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 第${attempt}次尝试时已经在线,无需重复认证" >> $LOG_FILE
return 0
else
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 第${attempt}次认证失败;响应:$RESPONSE;认证URL:$auth_url" >> $LOG_FILE
return 1
fi
}
# 获取当前IP
CURRENT_IP=$(get_current_ip)
if [ -z "$CURRENT_IP" ]; then
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 错误: 无法获取WAN口IP地址,退出脚本" >> $LOG_FILE
exit 1
fi
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 获取到IP: $CURRENT_IP" >> $LOG_FILE
# 第一次认证尝试
if send_auth_request "$CURRENT_IP" "一"; then
exit 0
fi
# 第一次尝试失败,等待5秒后重试
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 第一次认证失败,等待5秒后重试..." >> $LOG_FILE
sleep 5
# 第二次尝试前重新获取IP
NEW_IP=$(get_current_ip)
if [ -z "$NEW_IP" ]; then
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 错误: 重试时无法获取WAN口IP地址,退出脚本" >> $LOG_FILE
exit 1
fi
# 检查IP是否变化
if [ "$NEW_IP" != "$CURRENT_IP" ]; then
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 检测到IP地址变化:${CURRENT_IP} -> ${NEW_IP}" >> $LOG_FILE
CURRENT_IP="$NEW_IP"
fi
# 第二次认证尝试
if send_auth_request "$CURRENT_IP" "二"; then
exit 0
else
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 认证最终失败; 当前IP为: $CURRENT_IP" >> $LOG_FILE
exit 1
fi
{/collapse-item}
4.上传到路由器
使用putty软件连接到路由器
后面使用vim进行操作
1.创建auto_login.sh文件
vi /etc/auto_login.sh
2.在vi编辑器下,按 i 进入编辑模式,按 Shift+Insert 将准备好的脚本粘贴上去,然后按 esc 退出编辑模式,然后输入 :wq 并回车即可保存。
若输入错误,在退出编辑模式后,输入 :q! 不保存退出,如何再重新打开编辑
若要全部删除:按 esc 键后,先按 gg (到达顶部),然后 dG 即可。
3.设置权限
chmod +x /etc/auto_login.sh
4.手动运行一次测试一下
/etc/auto_login.sh
5.查看日志记录
cat /tmp/auto_login.log
6.设置计划任务 每30分钟运行一次
*/30 * * * * /etc/auto_login.sh
5.IP监测
1.创建 check_ip_change.sh 文件
vi /etc/check_ip_change.sh
#!/bin/sh
# 接口名称
INTERFACE="wwan"
# 用于存储上次IP的文件
IP_FILE="/tmp/last_ip_${INTERFACE}.txt"
# 日志文件
LOG_FILE="/tmp/auto_login.log"
# 循环次数
LOOP_COUNT=29
# 循环间隔(秒)
LOOP_INTERVAL=60
# 获取当前IP地址
get_current_ip() {
# 尝试多种方法获取IP地址
local ip=""
ip=$(ifconfig ${INTERFACE} 2>/dev/null | awk '/inet addr/{print substr($2,6)}')
# 如果没有获取到IP,尝试使用其他方法
if [ -z "$ip" ]; then
ip=$(ubus call network.interface.${INTERFACE} status 2>/dev/null | jsonfilter -e '@["ipv4-address"][0].address')
fi
# 如果依然获取不到,尝试使用ip命令
if [ -z "$ip" ]; then
ip=$(ip -4 addr show dev ${INTERFACE} 2>/dev/null | grep -oE 'inet [0-9.]+' | cut -d' ' -f2)
fi
echo "$ip"
}
# 记录日志函数
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') check_ip_change.sh-> $1" >> ${LOG_FILE}
# logger -t ip-change-detector "$1"
}
# 主循环函数
main_loop() {
for i in $(seq 1 $LOOP_COUNT); do
# log "循环第 $i/$LOOP_COUNT 次"
# 获取当前IP
current_ip=$(get_current_ip)
# 如果无法获取当前IP,记录错误但继续循环
if [ -z "${current_ip}" ]; then
log "警告: 无法获取接口 ${INTERFACE} 的IP地址"
sleep $LOOP_INTERVAL
continue
fi
# log "当前检测到IP: ${current_ip}"
# 检查是否存在存储的上次IP
if [ -f "${IP_FILE}" ]; then
last_ip=$(cat ${IP_FILE})
log "上次记录的IP: ${last_ip}"
# 比较IP是否变化
if [ "${current_ip}" != "${last_ip}" ]; then
log "检测到IP变化,当前检测到IP: ${current_ip},上次记录的IP: ${last_ip},执行认证脚本..."
# 执行认证脚本
if [ -x "/etc/auto_login.sh" ]; then
/etc/auto_login.sh >> ${LOG_FILE} 2>&1
auth_result=$?
if [ ${auth_result} -eq 0 ]; then
log "认证脚本执行成功"
else
log "认证脚本执行失败,返回码: ${auth_result}"
fi
else
log "错误: 认证脚本 /etc/auto_login.sh 不存在或不可执行"
fi
# 更新存储的IP
echo "${current_ip}" > ${IP_FILE}
log "已更新存储的IP为: ${current_ip}"
fi
else
# 首次运行,创建IP记录文件
echo "${current_ip}" > ${IP_FILE}
log "首次运行,记录IP: ${current_ip}"
fi
# 等待下一次循环
if [ $i -lt $LOOP_COUNT ]; then
sleep $LOOP_INTERVAL
fi
done
}
# 执行主循环
main_loop
log "完成 ${LOOP_COUNT} 次循环检测,脚本退出"
{/collapse-item}
2.设置权限
chmod +x /etc/check_ip_change.sh
3.设置计划任务 每分钟运行一次
* * * * * /etc/check_ip_change.sh
# 丢弃所有输出 不保留任何日志
* * * * * /etc/check_ip_change.sh >/dev/null 2>&1
6.配置web设置界面
1.创建用于读写的脚本
vi /www/cgi-bin/read_file.cgi
vi /www/cgi-bin/write_file.cgi
read_file.cgi文件
#!/bin/sh
echo "Content-type: text/plain"
echo ""
# 获取查询字符串中的file参数
FILE=$(echo "$QUERY_STRING" | sed -n 's/^.*file=\([^&]*\).*$/\1/p' | sed "s/%20/ /g")
# 将相对路径转换为绝对路径
case "$FILE" in
./etc/auto_login.sh)
ABS_PATH="/etc/auto_login.sh"
;;
./tmp/auto_login.log)
ABS_PATH="/tmp/auto_login.log"
;;
*)
echo "错误: 无权访问该文件"
exit 1
;;
esac
# 改进的安全检查:只防止包含../的路径遍历,允许单个.
if echo "$FILE" | grep -q "\.\./"; then
echo "错误: 无效的文件路径(包含路径遍历)"
exit 1
fi
# 检查文件是否存在
if [ ! -f "$ABS_PATH" ]; then
echo "错误: 文件不存在: $ABS_PATH"
exit 1
fi
# 读取文件内容
cat "$ABS_PATH"
{/collapse-item}
write_file.cgi文件
#!/bin/sh
echo "Content-type: text/plain"
echo ""
# 读取POST数据
read -r POST_DATA
# 解析POST数据,获取file和content参数
FILE_PARAM=$(echo "$POST_DATA" | sed 's/&.*//' | sed 's/file=//')
CONTENT_PARAM=$(echo "$POST_DATA" | sed 's/.*&//' | sed 's/content=//')
# 对URL编码进行解码
FILE=$(printf "%b" "$(echo "$FILE_PARAM" | sed 's/+/ /g; s/%/\\x/g')")
CONTENT=$(printf "%b" "$(echo "$CONTENT_PARAM" | sed 's/+/ /g; s/%/\\x/g')")
# 将相对路径转换为绝对路径
case "$FILE" in
"./etc/auto_login.sh")
ABS_PATH="/etc/auto_login.sh"
;;
*)
echo "错误: 只能保存到auto_login.sh文件"
exit 1
;;
esac
# 安全检查:防止路径遍历攻击
if echo "$ABS_PATH" | grep -q "\.\."; then
echo "错误: 无效的文件路径"
exit 1
fi
# 只允许写入特定文件
if [ "$ABS_PATH" = "/etc/auto_login.sh" ]; then
# 检查目录是否存在且可写
if [ ! -w "$(dirname "$ABS_PATH")" ]; then
echo "错误: 目录不可写: $(dirname "$ABS_PATH")"
exit 1
fi
# 写入文件
echo "$CONTENT" > "$ABS_PATH"
# 检查是否成功写入
if [ $? -eq 0 ]; then
echo "文件保存成功"
else
echo "错误: 文件保存失败"
exit 1
fi
else
echo "错误: 只能保存到auto_login.sh文件"
exit 1
fi
{/collapse-item}
{collapse-item label="9月11更新write_file.cgi"}
#!/bin/sh
echo "Content-type: text/plain"
echo ""
# 读取POST数据
read -r POST_DATA
# 解析POST数据,获取file和content参数
FILE_PARAM=$(echo "$POST_DATA" | sed 's/&.*//' | sed 's/file=//')
CONTENT_PARAM=$(echo "$POST_DATA" | sed 's/.*&//' | sed 's/content=//')
# 对URL编码进行解码
FILE=$(printf "%b" "$(echo "$FILE_PARAM" | sed 's/+/ /g; s/%/\\x/g')")
CONTENT=$(printf "%b" "$(echo "$CONTENT_PARAM" | sed 's/+/ /g; s/%/\\x/g')")
# 将相对路径转换为绝对路径
case "$FILE" in
"./etc/auto_login.sh")
ABS_PATH="/etc/auto_login.sh"
;;
*)
echo "错误: 只能保存到auto_login.sh文件"
exit 1
;;
esac
# 安全检查:防止路径遍历攻击
if echo "$ABS_PATH" | grep -q "\.\."; then
echo "错误: 无效的文件路径"
exit 1
fi
# 只允许写入特定文件
if [ "$ABS_PATH" = "/etc/auto_login.sh" ]; then
# 检查目录是否存在且可写
if [ ! -w "$(dirname "$ABS_PATH")" ]; then
echo "错误: 目录不可写: $(dirname "$ABS_PATH")"
exit 1
fi
# 写入文件
echo "$CONTENT" > "$ABS_PATH"
# 检查是否成功写入
if [ $? -eq 0 ]; then
echo "文件保存成功"
#2025.9.11添加 保存后自动运行一次
# 添加执行权限
chmod +x "$ABS_PATH"
# 延迟1秒后执行
# sleep 1
# 执行脚本并捕获输出
OUTPUT=$($ABS_PATH 2>&1)
echo "脚本执行结果: $OUTPUT"
else
echo "错误: 文件保存失败"
exit 1
fi
else
echo "错误: 只能保存到auto_login.sh文件"
exit 1
fi
{/collapse-item}
2.创建网页文件
先创建目录和文件
mkdir -p www/webconfig/
vi /www/webconfig/index.html
index.html文件
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>OpenWrt 自动登录配置</title>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
color: #333;
background-color: #f5f5f5;
padding: 20px;
max-width: 1200px;
margin: 0 auto;
}
header {
background-color: #2c3e50;
color: white;
padding: 1rem;
border-radius: 5px;
margin-bottom: 20px;
text-align: center;
}
h1 {
font-size: 1.8rem;
margin-bottom: 0.5rem;
}
.description {
font-size: 1rem;
opacity: 0.9;
}
.container {
display: flex;
flex-wrap: wrap;
gap: 20px;
}
.panel {
flex: 1;
min-width: 300px;
background: white;
border-radius: 5px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
padding: 20px;
}
.panel h2 {
margin-bottom: 15px;
padding-bottom: 10px;
border-bottom: 2px solid #eaeaea;
color: #2c3e50;
}
.editor {
width: 100%;
min-height: 300px;
font-family: monospace;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
resize: vertical;
}
.log-view {
width: 100%;
min-height: 300px;
max-height: 500px;
overflow-y: auto;
font-family: monospace;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
background-color: #f8f8f8;
white-space: pre-wrap;
}
.buttons {
margin-top: 15px;
display: flex;
gap: 10px;
}
button {
padding: 8px 16px;
background-color: #3498db;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-weight: 500;
transition: background-color 0.2s;
}
button:hover {
background-color: #2980b9;
}
button.save {
background-color: #27ae60;
}
button.save:hover {
background-color: #219653;
}
button.refresh {
background-color: #95a5a6;
}
button.refresh:hover {
background-color: #7f8c8d;
}
.status {
margin-top: 10px;
padding: 8px;
border-radius: 4px;
display: none;
}
.status.success {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
display: block;
}
.status.error {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
display: block;
}
.loading {
color: #7f8c8d;
font-style: italic;
}
@media (max-width: 768px) {
.container {
flex-direction: column;
}
}
</style>
</head>
<body>
<header>
<h1>OpenWrt 自动登录配置</h1>
<p class="description">编辑自动登录脚本并查看运行日志</p>
</header>
<div class="container">
<div class="panel">
<h2>自动登录脚本 (./etc/auto_login.sh)</h2>
<textarea id="editor" class="editor" placeholder="加载中..."></textarea>
<div class="buttons">
<button id="saveBtn" class="save">保存更改</button>
<button id="reloadBtn" class="refresh">重新加载</button>
</div>
<div id="editorStatus" class="status"></div>
</div>
<div class="panel">
<h2>运行日志 (./tmp/auto_login.log)</h2>
<pre id="logView" class="log-view">加载日志内容...</pre>
<div class="buttons">
<button id="refreshLogBtn" class="refresh">刷新日志</button>
</div>
<div id="logStatus" class="status"></div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const editor = document.getElementById('editor');
const logView = document.getElementById('logView');
const saveBtn = document.getElementById('saveBtn');
const reloadBtn = document.getElementById('reloadBtn');
const refreshLogBtn = document.getElementById('refreshLogBtn');
const editorStatus = document.getElementById('editorStatus');
const logStatus = document.getElementById('logStatus');
// 加载脚本内容
function loadScript() {
editor.classList.add('loading');
editor.value = '加载中...';
fetch('/cgi-bin/read_file.cgi?file=./etc/auto_login.sh')
.then(response => {
if (!response.ok) {
throw new Error('无法读取文件');
}
return response.text();
})
.then(data => {
editor.value = data;
editor.classList.remove('loading');
showStatus(editorStatus, '脚本加载成功', 'success');
})
.catch(error => {
editor.value = '';
showStatus(editorStatus, '错误: ' + error.message, 'error');
editor.classList.remove('loading');
});
}
// 加载日志内容
function loadLog() {
logView.textContent = '加载中...';
logView.classList.add('loading');
fetch('/cgi-bin/read_file.cgi?file=./tmp/auto_login.log')
.then(response => {
if (!response.ok) {
throw new Error('无法读取日志文件');
}
return response.text();
})
.then(data => {
logView.textContent = data || '日志文件为空';
logView.classList.remove('loading');
showStatus(logStatus, '日志加载成功', 'success');
})
.catch(error => {
logView.textContent = '加载失败: ' + error.message;
showStatus(logStatus, '错误: ' + error.message, 'error');
logView.classList.remove('loading');
});
}
// 保存脚本内容
function saveScript() {
const content = editor.value;
fetch('/cgi-bin/write_file.cgi', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `file=./etc/auto_login.sh&content=${encodeURIComponent(content)}`
})
.then(response => {
if (!response.ok) {
throw new Error('保存失败');
}
return response.text();
})
.then(data => {
showStatus(editorStatus, '保存成功', 'success');
})
.catch(error => {
showStatus(editorStatus, '错误: ' + error.message, 'error');
});
}
// 显示状态消息
function showStatus(element, message, type) {
element.textContent = message;
element.className = 'status ' + type;
// 3秒后隐藏状态消息
setTimeout(() => {
element.className = 'status';
element.textContent = '';
}, 3000);
}
// 绑定按钮事件
saveBtn.addEventListener('click', saveScript);
reloadBtn.addEventListener('click', loadScript);
refreshLogBtn.addEventListener('click', loadLog);
// 初始加载内容
loadScript();
loadLog();
});
</script>
</body>
</html>
{/collapse-item}
{collapse-item label="9月11更新index.html"}
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>OpenWrt 自动登录配置</title>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
color: #333;
background-color: #f5f5f5;
padding: 20px;
max-width: 1200px;
margin: 0 auto;
}
header {
background-color: #2c3e50;
color: white;
padding: 1rem;
border-radius: 5px;
margin-bottom: 20px;
text-align: center;
}
h1 {
font-size: 1.8rem;
margin-bottom: 0.5rem;
}
.description {
font-size: 1rem;
opacity: 0.9;
}
.container {
display: flex;
flex-wrap: wrap;
gap: 20px;
}
.panel {
flex: 1;
min-width: 300px;
background: white;
border-radius: 5px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
padding: 20px;
}
.panel h2 {
margin-bottom: 15px;
padding-bottom: 10px;
border-bottom: 2px solid #eaeaea;
color: #2c3e50;
}
.editor {
width: 100%;
min-height: 300px;
font-family: monospace;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
resize: vertical;
}
.log-view {
width: 100%;
min-height: 300px;
max-height: 500px;
overflow-y: auto;
font-family: monospace;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
background-color: #f8f8f8;
white-space: pre-wrap;
}
.buttons {
margin-top: 15px;
display: flex;
gap: 10px;
flex-wrap: wrap;
}
button {
padding: 8px 16px;
background-color: #3498db;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-weight: 500;
transition: background-color 0.2s;
}
button:hover {
background-color: #2980b9;
}
button.save {
background-color: #27ae60;
}
button.save:hover {
background-color: #219653;
}
button.refresh {
background-color: #95a5a6;
}
button.refresh:hover {
background-color: #7f8c8d;
}
button.download {
background-color: #9b59b6;
}
button.download:hover {
background-color: #8e44ad;
}
.status {
margin-top: 10px;
padding: 8px;
border-radius: 4px;
display: none;
}
.status.success {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
display: block;
}
.status.error {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
display: block;
}
.loading {
color: #7f8c8d;
font-style: italic;
}
@media (max-width: 768px) {
.container {
flex-direction: column;
}
.buttons {
flex-direction: column;
}
button {
width: 100%;
}
}
</style>
</head>
<body>
<header>
<h1>OpenWrt 自动登录配置</h1>
<p class="description">编辑自动登录脚本并查看运行日志</p>
</header>
<div class="container">
<div class="panel">
<h2>自动登录脚本 (./etc/auto_login.sh)</h2>
<textarea id="editor" class="editor" placeholder="加载中..."></textarea>
<div class="buttons">
<button id="saveBtn" class="save">保存更改</button>
<button id="reloadBtn" class="refresh">重新加载</button>
</div>
<div id="editorStatus" class="status"></div>
</div>
<div class="panel">
<h2>运行日志 (./tmp/auto_login.log)</h2>
<pre id="logView" class="log-view">加载日志内容...</pre>
<div class="buttons">
<button id="refreshLogBtn" class="refresh">刷新日志</button>
<button id="downloadLogBtn" class="download">下载日志</button>
</div>
<div id="logStatus" class="status"></div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const editor = document.getElementById('editor');
const logView = document.getElementById('logView');
const saveBtn = document.getElementById('saveBtn');
const reloadBtn = document.getElementById('reloadBtn');
const refreshLogBtn = document.getElementById('refreshLogBtn');
const downloadLogBtn = document.getElementById('downloadLogBtn');
const editorStatus = document.getElementById('editorStatus');
const logStatus = document.getElementById('logStatus');
// 加载脚本内容
function loadScript() {
editor.classList.add('loading');
editor.value = '加载中...';
fetch('/cgi-bin/read_file.cgi?file=./etc/auto_login.sh')
.then(response => {
if (!response.ok) {
throw new Error('无法读取文件');
}
return response.text();
})
.then(data => {
editor.value = data;
editor.classList.remove('loading');
showStatus(editorStatus, '脚本加载成功', 'success');
})
.catch(error => {
editor.value = '';
showStatus(editorStatus, '错误: ' + error.message, 'error');
editor.classList.remove('loading');
});
}
// 加载日志内容
function loadLog() {
logView.textContent = '加载中...';
logView.classList.add('loading');
fetch('/cgi-bin/read_file.cgi?file=./tmp/auto_login.log')
.then(response => {
if (!response.ok) {
throw new Error('无法读取日志文件');
}
return response.text();
})
.then(data => {
logView.textContent = data || '日志文件为空';
logView.classList.remove('loading');
showStatus(logStatus, '日志加载成功', 'success');
})
.catch(error => {
logView.textContent = '加载失败: ' + error.message;
showStatus(logStatus, '错误: ' + error.message, 'error');
logView.classList.remove('loading');
});
}
// 保存脚本内容
function saveScript() {
const content = editor.value;
fetch('/cgi-bin/write_file.cgi', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `file=./etc/auto_login.sh&content=${encodeURIComponent(content)}`
})
.then(response => {
if (!response.ok) {
throw new Error('保存失败');
}
return response.text();
})
.then(data => {
showStatus(editorStatus, '保存成功', 'success');
})
.catch(error => {
showStatus(editorStatus, '错误: ' + error.message, 'error');
});
}
// 下载日志功能
function downloadLog() {
const logContent = logView.textContent;
if (!logContent || logContent === '加载中...' || logContent === '加载日志内容...') {
showStatus(logStatus, '没有可下载的日志内容', 'error');
return;
}
try {
// 创建Blob对象
const blob = new Blob([logContent], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
// 创建下载链接
const a = document.createElement('a');
a.href = url;
a.download = 'auto_login.log';
document.body.appendChild(a);
a.click();
// 清理
setTimeout(() => {
document.body.removeChild(a);
URL.revokeObjectURL(url);
showStatus(logStatus, '日志下载成功', 'success');
}, 100);
} catch (error) {
showStatus(logStatus, '下载失败: ' + error.message, 'error');
}
}
// 显示状态消息
function showStatus(element, message, type) {
element.textContent = message;
element.className = 'status ' + type;
// 3秒后隐藏状态消息
setTimeout(() => {
element.className = 'status';
element.textContent = '';
}, 3000);
}
// 绑定按钮事件
saveBtn.addEventListener('click', saveScript);
reloadBtn.addEventListener('click', loadScript);
refreshLogBtn.addEventListener('click', loadLog);
downloadLogBtn.addEventListener('click', downloadLog);
// 初始加载内容
loadScript();
loadLog();
});
</script>
</body>
</html>
{/collapse-item}
3.设置权限
chmod +x /www/cgi-bin/read_file.cgi /www/cgi-bin/write_file.cgi
4.管理地址
所有工具和包
评论 (0)