首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> ASP.NET 基礎 >> ASP.NET 基礎

ASP.NET + jQuery AJAX 無刷新登入完整實作:前端驗證、非同步請求、後端處理

     編輯:ASP.NET 基礎

架構概覽

  • 前端:HTML 表單 + jQuery 驗證 + `$.post` 非同步送出 + `return false` 阻止預設提交
  • 後端:獨立頁面/ASHX/Web API 接收 `Request.Form`、驗證帳密、寫入 Session、回傳純文字/JSON 狀態碼
  • 優點:無整頁重整、使用者體驗佳、前後端分離雛形

一、前端頁面:Login.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Login.aspx.cs" Inherits="Demo.Login" %>
<!DOCTYPE html>
<html><head><title>AJAX 登入</title>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<style>
    .input-error { border-color: #dc3545; }
    .msg { margin-top:10px; padding:8px; border-radius:4px; display:none; }
    .msg.success { background:#d4edda; color:#155724; border:1px solid #c3e6cb; }
    .msg.error { background:#f8d7da; color:#721c24; border:1px solid #f5c6cb; }
</style>
</head><body>
<div style="max-width:360px; margin:50px auto; padding:20px; border:1px solid #ddd; border-radius:8px;">
    <h3 class="text-center">會員登入</h3>
    <div class="form-group">
        <label>帳號</label>
        <input type="text" id="txtUserName" class="form-control" placeholder="請輸入帳號" autocomplete="username" />
        <span class="text-danger" id="valUserName"></span>
    </div>
    <div class="form-group">
        <label>密碼</label>
        <input type="password" id="txtPassword" class="form-control" placeholder="請輸入密碼" autocomplete="current-password" />
        &span class="text-danger" id="valPassword"></span>
    </div>
    <button type="button" id="btnLogin" class="btn btn-primary btn-block">登入</button>
    <div id="msgBox" class="msg"></div>
</div>

<script>
$(function () {
    $('#btnLogin').on('click', function () {
        // 1. 前端驗證
        var user = $('#txtUserName').val().trim();
        var pwd = $('#txtPassword').val();
        var valid = true;
        
        $('#valUserName, #valPassword').text('');
        $('#txtUserName, #txtPassword').removeClass('input-error');
        
        if (!user) { $('#valUserName').text('請輸入帳號'); $('#txtUserName').addClass('input-error'); valid = false; }
        if (!pwd) { $('#valPassword').text('請輸入密碼'); $('#txtPassword').addClass('input-error'); valid = false; }
        if (!valid) return;

        // 2. 發送 AJAX
        var $btn = $(this).prop('disabled', true).text('登入中...');
        $('#msgBox').hide().removeClass('success error').text('');

        $.post('LoginHandler.ashx', { UserName: user, UserPass: pwd }, function (res) {
            $btn.prop('disabled', false).text('登入');
            var msg = $('#msgBox').show();
            
            switch (res) {
                case '1': // 成功
                    msg.addClass('success').text('登入成功,正在跳轉...');
                    setTimeout(function () { window.location.href = 'Default.aspx'; }, 800);
                    break;
                case '2': // 密碼錯誤
                    msg.addClass('error').text('密碼錯誤!');
                    $('#txtPassword').addClass('input-error').focus();
                    break;
                case '3': // 帳號不存在
                    msg.addClass('error').text('帳號不存在!');
                    $('#txtUserName').addClass('input-error').focus();
                    break;
                default:
                    msg.addClass('error').text('登入失敗:' + res);
            }
        }).fail(function () {
            $btn.prop('disabled', false).text('登入');
            $('#msgBox').show().addClass('error').text('網路或伺服器錯誤,請稍後再試');
        });
    });
});
</script>
</body></html>

二、後端處理程序:LoginHandler.ashx


using System;
using System.Web;

namespace Demo
{
    public class LoginHandler : IHttpHandler
    {
        public void ProcessRequest(HttpContext context)
        {
            context.Response.ContentType = "text/plain";
            context.Response.CacheControl = "no-cache";

            string userName = context.Request.Form["UserName"];
            string userPass = context.Request.Form["UserPass"];

            if (string.IsNullOrWhiteSpace(userName) || string.IsNullOrWhiteSpace(userPass))
            {
                context.Response.Write("0"); // 參數缺失
                return;
            }

            // 模擬資料庫驗證(實際呼叫 Service/Repository)
            var user = UserService.ValidateLogin(userName, userPass);

            if (user != null)
            {
                // 登入成功:寫入 Session
                context.Session["CurrentUser"] = user;
                // 若需跨應用程式單一登入,這裡可簽發 FormsAuthentication Ticket
                // FormsAuthentication.SetAuthCookie(userName, false);
                context.Response.Write("1");
            }
            else
            {
                // 區分帳號不存在 vs 密碼錯誤(防止帳號掃描,實務建議統一提示「帳號或密碼錯誤」)
                bool userExists = UserService.Exists(userName);
                context.Response.Write(userExists ? "2" : "3");
            }
        }

        public bool IsReusable => false;
    }

    // 簡易服務類別(實際應分層)
    public static class UserService
    {
        public static UserModel ValidateLogin(string name, string pwd)
        {
            // 參數化查詢:SELECT * FROM Users WHERE UserName=@name AND PasswordHash=@hash
            // 這裡模擬
            if (name == "admin" && pwd == "123456") return new UserModel { Id = 1, Name = "admin", Role = "Admin" };
            return null;
        }

        public static bool Exists(string name) => name == "admin"; // 模擬
    }

    public class UserModel
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Role { get; set; }
    }
}

三、關鍵細節總結

項目重點
按鈕型別`type="button"` + `return false` / `e.preventDefault()` 防止表單預設提交
前端驗證必填、格式、長度,**後端必須再次驗證**
傳輸加密**生產環境強制 HTTPS**,密碼不可明文傳輸
密碼處理資料庫儲存 **BCrypt/Argon2/PBKDF2 雜湊**,不可明文/MD5/SHA1
防暴力破解失敗計數、驗證碼、帳號鎖定、IP 限流
回傳格式建議統一 JSON:`{ success: true, message: '', redirect: '' }`
CSRF 防護單頁應用可用 AntiForgeryToken / SameSite Cookie / 雙重提交 Cookie
Session 固定攻擊登入成功後 `Session.RegenerateID()` / 棄用舊 Session

現代化改寫建議


// ASP.NET Core / Web API 版本
[HttpPost("login")]
public async Task Login([FromBody] LoginDto dto)
{
    var user = await _userService.ValidateAsync(dto.UserName, dto.Password);
    if (user == null) return Unauthorized(new { code = 401, message = "帳號或密碼錯誤" });
    
    var claims = new[] { new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), new Claim(ClaimTypes.Role, user.Role) };
    var token = _jwtService.GenerateToken(claims);
    return Ok(new { code = 200, token, user = new { user.Id, user.Name, user.Role } });
}

// 前端 fetch
const res = await fetch('/api/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ userName, password }) });
const data = await res.json();
if (data.code === 200) { localStorage.setItem('token', data.token); router.push('/'); }
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved