实验环境:

  • 网关:RouterOS v7.19.4
  • 认证:TekRADIUS v5.7
  • 数据:SQL Server 2008 R2 Enterprise
  • 系统:Windows Server 2022 Standard + Internet Information Services (IIS)

核心技术栈

  • 后端引擎:Classic ASP (VBScript) 在 Windows Server 环境下部署极快。
  • Radius 服务器:TekRADIUS(广泛用于拨号上网认证)。
  • 数据库:SQL Server ( SQLNCLI10 ) 用于校验账号合法性。
  • 执行工具:trcli.exeTekRADIUS 提供的命令行工具,用于实时修改认证数据。

重置代码 password-reset.asp

<%
Option Explicit
Response.Buffer = True
Response.Charset = "UTF-8"

' -------------------------
' TekRADIUS trcli.exe 路径
' -------------------------
Const TRCLI_PATH = "C:\Program Files\TekRADIUS\trcli.exe"

' -------------------------
' 数据库配置(仅用于检查账号是否存在)
' -------------------------
Dim DB_SERVER, DB_NAME, DB_USER, DB_PASS
DB_SERVER = "1.1.1.1"
DB_NAME   = "TekRADIUS"
DB_USER   = "sa"
DB_PASS   = "111111"

' -------------------------
' 工具函数
' -------------------------
Function TrimSafe(s)
    If IsNull(s) Then
        TrimSafe = ""
    Else
        TrimSafe = Trim(CStr(s))
    End If
End Function

Sub Fail(msg)
    Response.Write "<script>alert('" & Replace(msg,"'","\'") & "');history.back();</script>"
    Response.End
End Sub

Function Success(msg)
    Response.Write "<script>alert('" & Replace(msg,"'","\'") & "');window.location='password.asp';</script>"
    Response.End
End Function

Function RunCmd(cmd)
    Dim WshShell, execObj, output
    Set WshShell = Server.CreateObject("WScript.Shell")
    Set execObj = WshShell.Exec(cmd)
    output = execObj.StdOut.ReadAll & vbCrLf & execObj.StdErr.ReadAll
    RunCmd = output
End Function

' -------------------------
' 表单提交处理
' -------------------------
If Request.ServerVariables("REQUEST_METHOD") = "POST" Then
    Dim username, newpwd, confpwd
    username = TrimSafe(Request.Form("username"))
    newpwd   = TrimSafe(Request.Form("newpwd"))
    confpwd  = TrimSafe(Request.Form("confpwd"))

    If username = "" Or newpwd = "" Or confpwd = "" Then Fail "请填写所有字段!"
    If newpwd <> confpwd Then Fail "新的密码与确认密码不一致!"
    If Len(newpwd) < 6 Then Fail "密码至少需要 6 !"

    ' -------------------------
    ' 检查账号是否存在
    ' -------------------------
    Dim conn, cmd, rs
    Set conn = Server.CreateObject("ADODB.Connection")
    conn.Open "Provider=SQLNCLI10;Server=" & DB_SERVER & ";Database=" & DB_NAME & ";Uid=" & DB_USER & ";Pwd=" & DB_PASS & ";"

    Set cmd = Server.CreateObject("ADODB.Command")
    cmd.ActiveConnection = conn
    cmd.CommandType = 1 ' adCmdText
    cmd.CommandText = "SELECT UserName FROM dbo.Users WHERE RTRIM(UserName)=?"
    cmd.Parameters.Append cmd.CreateParameter("", 202, 1, 64, username)
    Set rs = cmd.Execute()
    If rs.EOF Then
        rs.Close
        conn.Close
        Fail "上网账号不存在!"
    End If
    rs.Close
    conn.Close

    ' -------------------------
    ' 调用 trcli.exe 修改密码重置
    ' -------------------------
    Dim delCmd, addCmd

    ' 生成可在 cmd 中正确执行的命令路径空格问题已解决
    delCmd = "%comspec% /c """"" & TRCLI_PATH & """" & " -m " & username & " ""ietf|2"" check"""
    RunCmd(delCmd)

    addCmd = "%comspec% /c """"" & TRCLI_PATH & """" & " -a " & username & " ""ietf|2"" " & newpwd & " check"""
    RunCmd(addCmd)

    Success "上网密码重置成功!"
End If
%>

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PPPoE上网账号_密码重置</title>
<style>
body{font-family:Arial, "Microsoft YaHei"; margin:30px;}
.form{max-width:420px; padding:18px; border:1px solid #ddd; border-radius:6px; box-shadow:0 2px 6px rgba(0,0,0,.05);}
.form h2{margin-top:0;}
.form label{display:block; margin:4px 0 2px;}
.form input[type=text], .form input[type=password]{width:100%; padding:8px; box-sizing:border-box;}
.form .btn{margin-top:12px; padding:8px 12px; cursor:pointer;}
</style>
<script>
function validateForm(){
    var u=document.getElementById('username').value.trim();
    var np=document.getElementById('newpwd').value;
    var cp=document.getElementById('confpwd').value;
    if(u===''||np===''||cp===''){alert('请填写所有字段!');return false;}
    if(np!==cp){alert('新的密码与确认密码不一致!');return false;}
    if(np.length<6){alert('密码至少需要 6 位!');return false;}
    return true;
}
</script>
</head>
<style>
/* 全局样式 */
body {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    margin: 0;
    padding: 20px 0 0 0;   /* 页面上方留空,不会贴顶 */
    background-color: #f0f2f5;
}

/* 表单容器 */
.form {
    width: 90%;                /* 移动端:占屏幕 90% */
    max-width: 420px;          /* 大屏:最大宽度 420px */
    margin: 0 auto;            /* 关键:水平居中 */
    padding: 30px;
    border-radius: 12px;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
    background-color: #ffffff;
    box-sizing: border-box;
    text-align: left;
}

/* 标题 */
.form h2 {
    margin-top: 0;
    margin-bottom: 24px;
    text-align: center;
    color: #333333;
    font-size: 22px;
    font-weight: 600;
}

/* 标签 */
.form label {
    display: block;
    margin-bottom: 8px;
    color: #555555;
    font-weight: 500;
    font-size: 14px;
}

/* 输入框 */
.form input[type=text],
.form input[type=password] {
    width: 100%;
    padding: 12px;
    margin-bottom: 14px;
    border: 1px solid #dcdfe6;
    border-radius: 8px;
    box-sizing: border-box;
    font-size: 14px;
    transition: border-color 0.3s, box-shadow 0.3s;
}

.form input[type=text]:focus,
.form input[type=password]:focus {
    outline: none;
    border-color: #409eff;
    box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
}

/* 按钮 */
/* font-weight: 500; */
/* font-weight: bold; */
.form .btn {
    width: 100%;
    padding: 12px;
    border: none;
    border-radius: 8px;
    background-color: #409eff;
    color: white;
    font-size: 16px;
    cursor: pointer;
    transition: background-color 0.3s, transform 0.2s;
}

.form .btn:hover {
    background-color: #66b1ff;
}
.form .btn:active {
    background-color: #3a8ee6;
    transform: translateY(1px);
}

/* 底部帮助区 */
.help-section {
    display: flex;
    justify-content: space-between;
    margin-top: 16px;
	font-weight: 500;
    font-size: 14px;
}

.help-section a {
    color: #555555;
    text-decoration: none;
    transition: color 0.3s, text-decoration 0.3s;
}
.help-section a:hover {
    color: #3a8ee6;
    text-decoration: underline;
}

/* 小屏适配优化 */
@media (max-width: 400px) {
    .form h2 {
        font-size: 20px;
    }
    .help-section {
        flex-direction: column;
        align-items: center;
        gap: 8px; /* 链接上下间距 */
    }
}
</style>
<body>
<div class="form">
    <h2>PPPoE上网账号_密码重置</h2>
    <form method="post" action="password.asp" onsubmit="return validateForm();">
        <label for="username">上网帐号:</label>
        <input id="username" name="username" type="text" maxlength="64" />
        <label for="newpwd">新的密码:</label>
        <input id="newpwd" name="newpwd" type="password" maxlength="256" />
        <label for="confpwd">确认密码:</label>
        <input id="confpwd" name="confpwd" type="password" maxlength="256" />
        <input class="btn" type="submit" value="确 认 密 码 重 置" />
		<p></p>
    </form>
</div>
</body>
</html>

技术架构图解流程:

用户提交表单ASP 校验账号(SQL Server)ASP调用 Shell 执行 trcli.exeRadius 数据更新

功能亮点(代码解析)

  • 安全性校验: 代码中检查了空字段、密码一致性以及最小长度限制,防止非法提交。
  • 数据库预编译: 使用ADODB.Command处理 SQL,有效防止了 SQL 注入。
  • 解决路径空格难题: 在 Windows 环境下调用带空格的路径(如C:\Program Files\...)是很多人的噩梦。代码中通过嵌套双引号""...""的方式解决了 WshShell.Exec 的路径识别问题。
  • 前后端联动的交互: 前端用 JavaScript 做第一次拦截,后端做二次校验,最后用 alert 弹窗反馈结果并跳转。

界面设计

采用了 CSS 样式(Flexbox 布局、阴影效果、圆角),让 ASP 页面在移动端也能有不错的视觉体验。

部署注意事项(避坑指南)

  • 权限问题:运行 IIS 的账号(如 IUSR)必须对trcli.exe有执行权限。
  • 驱动程序:确保服务器安装了 SQLNCLI10(SQL Server Native Client)。
  • 路径配置:TRCLI_PATH必须指向正确的安装位置。

结语

虽然 ASP 是老技术,但配合合适的 CLI 工具,依然是解决特定运维场景的最快路径。