使用PHP+html+MySQL实现用户的注册和登录(源码)

使用的环境:

MySQL5.5.50

腾讯云(可以注册免费试用)

体验页面

https://wyjfjs.site/demo/html/login.html

打包的源代码

通过网盘分享的文件:登录注册

链接: https://pan.baidu.com/s/1nBxxwwKFX4rAHqwQE8g0_A 提取码: jjgp

登录

注册

登陆后的页面

创建数据库

打开数据库

复制代码
systemctl stop mysqld
mysqld_safe --skip-grant-tables &
systemctl restart mysqld
mysql -u root    

USE jssj;

创建登录和注册的数据表

复制代码
CREATE TABLE IF NOT EXISTS users (
  id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  username VARCHAR(50) NOT NULL UNIQUE,
  email VARCHAR(191) DEFAULT NULL UNIQUE,
  password_hash VARCHAR(255) NOT NULL,
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

目录结构

复制代码
demo/
├─ html/
│  ├─ login.html
│  ├─ register.html
│  └─ dashboard.html
└─ php/
   ├─ config.php
   ├─ register_handler.php
   ├─ login_handler.php
   ├─ dashboard.php       
   └─ logout.php

源代码

login.html

复制代码
<!doctype html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width,initial-scale=1" />
  <title>登录控制台</title>
  <meta name="color-scheme" content="dark light" />
  <style>
    :root{
      --bg0:#070A12;
      --bg1:#0B1020;
      --text:#EAF0FF;
      --muted: rgba(234,240,255,.72);
      --border: rgba(255,255,255,.14);
      --shadow: 0 20px 60px rgba(0,0,0,.55);
      --accent1:#7C3AED;
      --accent2:#22D3EE;
      --radius: 18px;
    }
    *{box-sizing:border-box}
    html,body{height:100%}
    body{
      margin:0;
      font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Arial, "PingFang SC", "Microsoft YaHei";
      color:var(--text);
      background:
        radial-gradient(1000px 600px at 15% 20%, rgba(124,58,237,.28), transparent 55%),
        radial-gradient(900px 600px at 85% 25%, rgba(34,211,238,.25), transparent 55%),
        linear-gradient(180deg, var(--bg0), var(--bg1));
      overflow-x:hidden;
    }
    .grid{
      position:fixed; inset:0;
      background-image:
        linear-gradient(rgba(255,255,255,.05) 1px, transparent 1px),
        linear-gradient(90deg, rgba(255,255,255,.05) 1px, transparent 1px);
      background-size: 44px 44px;
      mask-image: radial-gradient(circle at 50% 30%, rgba(0,0,0,1), rgba(0,0,0,.2) 55%, rgba(0,0,0,0) 75%);
      pointer-events:none;
      opacity:.55;
    }
    .wrap{
      min-height:100%;
      display:grid;
      place-items:center;
      padding:40px 16px;
    }
    .card{
      width:min(860px, 100%);
      display:grid;
      grid-template-columns: 1fr 1fr;
      gap:22px;
      padding:22px;
      border:1px solid var(--border);
      border-radius: calc(var(--radius) + 10px);
      background: linear-gradient(180deg, rgba(255,255,255,.08), rgba(255,255,255,.04));
      box-shadow: var(--shadow);
      backdrop-filter: blur(14px);
    }
    @media (max-width: 860px){
      .card{grid-template-columns:1fr; padding:18px}
    }
    .hero{
      padding:18px;
      border-radius: var(--radius);
      background:
        radial-gradient(900px 420px at 25% 15%, rgba(124,58,237,.22), transparent 60%),
        radial-gradient(800px 360px at 70% 30%, rgba(34,211,238,.22), transparent 60%),
        rgba(255,255,255,.05);
      border:1px solid rgba(255,255,255,.10);
    }
    .title{
      margin:0;
      font-size: 36px;
      line-height: 1.1;
      letter-spacing: -0.6px;
    }
    .grad{
      background: linear-gradient(90deg, #EAF0FF, rgba(34,211,238,.95), rgba(124,58,237,.95));
      -webkit-background-clip:text;
      background-clip:text;
      color: transparent;
    }
    .sub{
      margin:10px 0 0;
      color: var(--muted);
      font-size: 14px;
      line-height: 1.65;
      max-width: 52ch;
    }
    .panel{
      padding:18px;
      border-radius: var(--radius);
      border:1px solid rgba(255,255,255,.12);
      background: rgba(10,14,28,.55);
      box-shadow: 0 0 0 1px rgba(255,255,255,.10), 0 18px 60px rgba(0,0,0,.55);
    }
    .panel h2{margin:0 0 6px 0; font-size:16px}
    .panel .tip{margin:0 0 14px 0; color:var(--muted); font-size:13px}

    form{display:grid; gap:12px}
    label{font-size:12px; color: rgba(234,240,255,.78); letter-spacing:.2px}
    .row{display:grid; gap:8px}
    .field{position:relative}
    input{
      width:100%;
      padding: 12px 12px 12px 40px;
      border-radius: 14px;
      border:1px solid rgba(255,255,255,.14);
      outline:none;
      background: rgba(255,255,255,.06);
      color: var(--text);
      font-size: 14px;
      transition: .18s ease;
    }
    input::placeholder{color: rgba(234,240,255,.35)}
    input:focus{
      border-color: rgba(34,211,238,.55);
      box-shadow: 0 0 0 4px rgba(34,211,238,.12);
      background: rgba(255,255,255,.08);
    }
    .icon{
      position:absolute;
      left:12px; top:50%;
      transform: translateY(-50%);
      width:18px; height:18px;
      opacity:.75;
      pointer-events:none;
      fill: rgba(234,240,255,.78);
    }
    .btn{
      appearance:none;
      border:0;
      cursor:pointer;
      padding: 12px 14px;
      border-radius: 14px;
      color: #06101a;
      font-weight: 800;
      letter-spacing:.2px;
      background: linear-gradient(135deg, rgba(34,211,238,.95), rgba(124,58,237,.95));
      box-shadow: 0 14px 40px rgba(34,211,238,.18);
      transition: transform .15s ease, filter .15s ease;
      width: 100%;
    }
    .btn:hover{transform: translateY(-1px); filter: brightness(1.05)}
    .btn:active{transform: translateY(0px); filter: brightness(.98)}
    .link{
      color: rgba(234,240,255,.80);
      text-decoration:none;
      font-size: 13px;
    }
    .link:hover{color:#fff; text-decoration:underline}
    .foot{margin-top: 14px; color: rgba(234,240,255,.55); font-size: 12px; line-height:1.6}
  </style>
</head>
<body>
  <div class="grid"></div>

  <div class="wrap">
    <div class="card">
      <section class="hero">
        <h1 class="title"><span class="grad">登录</span> 控制台</h1>
        <p class="sub">
          请输入你的账号与密码。系统将通过 PHP 后端验证并创建安全会话,然后跳转至控制台页面。
        </p>
        <p class="sub" style="margin-top:14px;color:rgba(234,240,255,.62)">
          Tip:账号可使用「用户名或邮箱」。
        </p>
      </section>

      <aside class="panel">
        <h2>账号登录</h2>
        <p class="tip">登录成功将进入 dashboard。</p>

        <form method="post" action="../php/login_handler.php" autocomplete="off">
          <div class="row">
            <label for="account">用户名或邮箱</label>
            <div class="field">
              <svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 12a4 4 0 1 0-4-4 4 4 0 0 0 4 4zm0 2c-4.42 0-8 2-8 4.5V21h16v-2.5C20 16 16.42 14 12 14z"/></svg>
              <input id="account" name="account" placeholder="例如:admin / name@example.com" required />
            </div>
          </div>

          <div class="row">
            <label for="password">密码</label>
            <div class="field">
              <svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M17 8h-1V6a4 4 0 0 0-8 0v2H7a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2zm-7-2a2 2 0 0 1 4 0v2h-4zm7 14H7V10h10z"/></svg>
              <input id="password" type="password" name="password" placeholder="请输入密码" required />
            </div>
          </div>

          <button class="btn" type="submit">进入系统</button>

          <div class="foot">
            还没有账号?
            <a class="link" href="register.html">去注册</a>
          </div>
        </form>
      </aside>
    </div>
  </div>
</body>
</html>

register.html

复制代码
<!doctype html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width,initial-scale=1" />
  <title>创建账号</title>
  <meta name="color-scheme" content="dark light" />
  <style>
    :root{
      --bg0:#070A12;
      --bg1:#0B1020;
      --card: rgba(255,255,255,.06);
      --card2: rgba(255,255,255,.10);
      --text:#EAF0FF;
      --muted: rgba(234,240,255,.72);
      --border: rgba(255,255,255,.14);
      --shadow: 0 20px 60px rgba(0,0,0,.55);
      --glow: 0 0 0 1px rgba(255,255,255,.10), 0 18px 60px rgba(0,0,0,.55);
      --accent1:#7C3AED; /* 紫 */
      --accent2:#22D3EE; /* 青 */
      --accent3:#A3FF12; /* 绿 */
      --danger:#FF4D6D;
      --ok:#2EE59D;
      --radius: 18px;
    }
    *{box-sizing:border-box}
    html,body{height:100%}
    body{
      margin:0;
      font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Arial, "PingFang SC", "Microsoft YaHei";
      color:var(--text);
      background:
        radial-gradient(1000px 600px at 10% 15%, rgba(34,211,238,.25), transparent 55%),
        radial-gradient(900px 600px at 90% 20%, rgba(124,58,237,.28), transparent 55%),
        radial-gradient(900px 700px at 60% 95%, rgba(163,255,18,.12), transparent 60%),
        linear-gradient(180deg, var(--bg0), var(--bg1));
      overflow-x:hidden;
    }
    .grid{
      position:fixed; inset:0;
      background-image:
        linear-gradient(rgba(255,255,255,.05) 1px, transparent 1px),
        linear-gradient(90deg, rgba(255,255,255,.05) 1px, transparent 1px);
      background-size: 44px 44px;
      mask-image: radial-gradient(circle at 50% 30%, rgba(0,0,0,1), rgba(0,0,0,.2) 55%, rgba(0,0,0,0) 75%);
      pointer-events:none;
      opacity:.55;
    }
    .wrap{
      min-height:100%;
      display:grid;
      place-items:center;
      padding:40px 16px;
    }
    .card{
      width:min(960px, 100%);
      display:grid;
      grid-template-columns: 1.1fr .9fr;
      gap:22px;
      padding:22px;
      border:1px solid var(--border);
      border-radius: calc(var(--radius) + 10px);
      background: linear-gradient(180deg, rgba(255,255,255,.08), rgba(255,255,255,.04));
      box-shadow: var(--shadow);
      backdrop-filter: blur(14px);
    }
    @media (max-width: 860px){
      .card{grid-template-columns:1fr; padding:18px}
    }

    .hero{
      padding:18px 18px 18px 18px;
      border-radius: var(--radius);
      background:
        radial-gradient(900px 420px at 30% 15%, rgba(34,211,238,.20), transparent 60%),
        radial-gradient(700px 360px at 70% 30%, rgba(124,58,237,.22), transparent 60%),
        linear-gradient(180deg, rgba(255,255,255,.06), rgba(255,255,255,.03));
      border:1px solid rgba(255,255,255,.10);
      position:relative;
      overflow:hidden;
    }
    .hero:before{
      content:"";
      position:absolute; inset:-2px;
      background: conic-gradient(from 220deg,
        rgba(34,211,238,.0),
        rgba(34,211,238,.35),
        rgba(124,58,237,.35),
        rgba(163,255,18,.20),
        rgba(34,211,238,.0));
      filter: blur(22px);
      opacity:.55;
      transform: translate3d(0,0,0);
      pointer-events:none;
    }
    .brand{
      position:relative;
      display:flex;
      align-items:center;
      gap:12px;
      margin-bottom:12px;
    }
    .logo{
      width:44px;height:44px;border-radius:14px;
      background:
        radial-gradient(circle at 30% 25%, rgba(255,255,255,.55), rgba(255,255,255,0) 55%),
        linear-gradient(135deg, rgba(34,211,238,.9), rgba(124,58,237,.9));
      box-shadow: 0 14px 40px rgba(124,58,237,.25);
      border:1px solid rgba(255,255,255,.20);
    }
    .brand h1{
      margin:0;
      font-size: 18px;
      letter-spacing:.2px;
      color: rgba(234,240,255,.92);
    }
    .brand p{
      margin:2px 0 0 0;
      color: var(--muted);
      font-size: 13px;
    }
    .headline{
      position:relative;
      margin:14px 0 10px;
      font-size: 38px;
      line-height: 1.1;
      letter-spacing: -0.6px;
    }
    .headline .grad{
      background: linear-gradient(90deg, #EAF0FF, rgba(34,211,238,.95), rgba(124,58,237,.95));
      -webkit-background-clip:text;
      background-clip:text;
      color: transparent;
    }
    .sub{
      position:relative;
      margin: 8px 0 0;
      color: rgba(234,240,255,.72);
      font-size: 14px;
      line-height: 1.65;
      max-width: 52ch;
    }
    .pills{
      position:relative;
      display:flex; flex-wrap:wrap;
      gap:10px;
      margin-top:14px;
    }
    .pill{
      padding:8px 10px;
      border-radius: 999px;
      border:1px solid rgba(255,255,255,.14);
      background: rgba(255,255,255,.06);
      font-size: 12px;
      color: rgba(234,240,255,.78);
    }

    .panel{
      padding:18px;
      border-radius: var(--radius);
      border:1px solid rgba(255,255,255,.12);
      background: rgba(10,14,28,.55);
      box-shadow: var(--glow);
    }
    .panel h2{
      margin:0 0 6px 0;
      font-size: 16px;
      letter-spacing:.2px;
    }
    .panel .tip{
      margin:0 0 14px 0;
      color: var(--muted);
      font-size: 13px;
    }

    form{display:grid; gap:12px}
    .row{display:grid; gap:8px}
    label{
      font-size: 12px;
      color: rgba(234,240,255,.78);
      letter-spacing:.2px;
    }
    .field{
      position:relative;
    }
    input{
      width:100%;
      padding: 12px 12px 12px 40px;
      border-radius: 14px;
      border:1px solid rgba(255,255,255,.14);
      outline:none;
      background: rgba(255,255,255,.06);
      color: var(--text);
      font-size: 14px;
      transition: .18s ease;
    }
    input::placeholder{color: rgba(234,240,255,.35)}
    input:focus{
      border-color: rgba(34,211,238,.55);
      box-shadow: 0 0 0 4px rgba(34,211,238,.12);
      background: rgba(255,255,255,.08);
    }
    .icon{
      position:absolute;
      left:12px; top:50%;
      transform: translateY(-50%);
      width:18px; height:18px;
      opacity:.75;
      pointer-events:none;
      fill: rgba(234,240,255,.78);
    }

    .actions{
      display:flex;
      gap:10px;
      align-items:center;
      margin-top:4px;
    }
    .btn{
      appearance:none;
      border:0;
      cursor:pointer;
      padding: 12px 14px;
      border-radius: 14px;
      color: #06101a;
      font-weight: 700;
      letter-spacing:.2px;
      background: linear-gradient(135deg, rgba(34,211,238,.95), rgba(124,58,237,.95));
      box-shadow: 0 14px 40px rgba(34,211,238,.18);
      transition: transform .15s ease, filter .15s ease;
      width: 100%;
    }
    .btn:hover{transform: translateY(-1px); filter: brightness(1.05)}
    .btn:active{transform: translateY(0px); filter: brightness(.98)}
    .link{
      color: rgba(234,240,255,.80);
      text-decoration:none;
      font-size: 13px;
    }
    .link:hover{color:#fff; text-decoration:underline}

    .foot{
      margin-top: 14px;
      color: rgba(234,240,255,.55);
      font-size: 12px;
      line-height:1.6;
    }

    /* 简易前端提示(HTML5 invalid) */
    input:invalid{border-color: rgba(255,77,109,.30)}
    input:invalid:focus{box-shadow: 0 0 0 4px rgba(255,77,109,.12)}
  </style>
</head>
<body>
  <div class="grid"></div>

  <div class="wrap">
    <div class="card">
      <section class="hero" aria-label="介绍">
        <div class="brand">
          <div class="logo" aria-hidden="true"></div>
          <div>
            <h1>Auth Console</h1>
            <p>Secure · Minimal · Modern</p>
          </div>
        </div>

        <div class="headline">
          <span class="grad">创建你的新身份</span><br/>
          进入系统控制台
        </div>
        <p class="sub">
          使用安全的密码哈希存储与会话管理。此页面为纯 HTML 前端,提交给 PHP 后端处理注册逻辑。
        </p>

        <div class="pills" aria-label="功能亮点">
          <span class="pill">Glass UI</span>
          <span class="pill">Gradient</span>
          <span class="pill">Secure Session</span>
          <span class="pill">PDO</span>
          <span class="pill">Password Hash</span>
        </div>
      </section>

      <aside class="panel" aria-label="注册表单">
        <h2>创建账号</h2>
        <p class="tip">填写信息后提交,注册成功将跳转到登录页。</p>

        <form method="post" action="../php/register_handler.php" autocomplete="off">
          <div class="row">
            <label for="username">用户名</label>
            <div class="field">
              <svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 12a4 4 0 1 0-4-4 4 4 0 0 0 4 4zm0 2c-4.42 0-8 2-8 4.5V21h16v-2.5C20 16 16.42 14 12 14z"/></svg>
              <input id="username" name="username" placeholder="例如:admin" required minlength="2" maxlength="50" />
            </div>
          </div>

          <div class="row">
            <label for="email">邮箱(可选)</label>
            <div class="field">
              <svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M20 4H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2zm0 4-8 5L4 8V6l8 5 8-5z"/></svg>
              <input id="email" name="email" placeholder="name@example.com" type="email" maxlength="191" />
            </div>
          </div>

          <div class="row">
            <label for="password">密码</label>
            <div class="field">
              <svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M17 8h-1V6a4 4 0 0 0-8 0v2H7a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2zm-7-2a2 2 0 0 1 4 0v2h-4zm7 14H7V10h10z"/></svg>
              <input id="password" name="password" placeholder="至少 6 位" type="password" required minlength="6" />
            </div>
          </div>

          <div class="row">
            <label for="confirm">确认密码</label>
            <div class="field">
              <svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 1a11 11 0 1 0 11 11A11 11 0 0 0 12 1zm-1 16-4-4 1.41-1.41L11 14.17l5.59-5.59L18 10z"/></svg>
              <input id="confirm" name="confirm" placeholder="再次输入密码" type="password" required minlength="6" />
            </div>
          </div>

          <div class="actions">
            <button class="btn" type="submit">立即注册</button>
          </div>

          <div class="foot">
            已有账号?
            <a class="link" href="login.html">去登录</a>
          </div>
        </form>
      </aside>
    </div>
  </div>

  <script>
    // 前端小校验:确认密码一致(不影响后端校验)
    const pw = document.getElementById('password');
    const cf = document.getElementById('confirm');
    function check(){
      if (!pw.value || !cf.value) { cf.setCustomValidity(''); return; }
      cf.setCustomValidity(pw.value === cf.value ? '' : '两次输入的密码不一致');
    }
    pw.addEventListener('input', check);
    cf.addEventListener('input', check);
  </script>
</body>
</html>

dashboard.html

复制代码
<!doctype html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width,initial-scale=1" />
  <title>控制台</title>
  <meta name="color-scheme" content="dark light" />
  <style>
    :root{
      --bg0:#070A12; --bg1:#0B1020;
      --text:#EAF0FF; --muted: rgba(234,240,255,.72);
      --border: rgba(255,255,255,.14);
      --shadow: 0 20px 60px rgba(0,0,0,.55);
      --accent1:#7C3AED; --accent2:#22D3EE; --accent3:#A3FF12;
      --radius: 18px;
    }
    *{box-sizing:border-box}
    html,body{height:100%}
    body{
      margin:0;
      font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Arial, "PingFang SC", "Microsoft YaHei";
      color:var(--text);
      background:
        radial-gradient(1000px 600px at 10% 15%, rgba(34,211,238,.25), transparent 55%),
        radial-gradient(900px 600px at 90% 20%, rgba(124,58,237,.28), transparent 55%),
        radial-gradient(900px 700px at 60% 95%, rgba(163,255,18,.10), transparent 60%),
        linear-gradient(180deg, var(--bg0), var(--bg1));
      overflow-x:hidden;
    }
    .grid{
      position:fixed; inset:0;
      background-image:
        linear-gradient(rgba(255,255,255,.05) 1px, transparent 1px),
        linear-gradient(90deg, rgba(255,255,255,.05) 1px, transparent 1px);
      background-size: 44px 44px;
      mask-image: radial-gradient(circle at 50% 30%, rgba(0,0,0,1), rgba(0,0,0,.2) 55%, rgba(0,0,0,0) 75%);
      pointer-events:none;
      opacity:.55;
    }
    .wrap{min-height:100%; padding:34px 16px;}
    .top{
      max-width:1100px; margin:0 auto 16px auto;
      display:flex; align-items:center; justify-content:space-between; gap:12px;
    }
    .brand{display:flex; align-items:center; gap:12px;}
    .logo{
      width:44px;height:44px;border-radius:14px;
      background:
        radial-gradient(circle at 30% 25%, rgba(255,255,255,.55), rgba(255,255,255,0) 55%),
        linear-gradient(135deg, rgba(34,211,238,.9), rgba(124,58,237,.9));
      box-shadow: 0 14px 40px rgba(124,58,237,.25);
      border:1px solid rgba(255,255,255,.20);
      flex:0 0 auto;
    }
    .brand h1{margin:0; font-size:16px; letter-spacing:.2px;}
    .brand p{margin:2px 0 0 0; color:var(--muted); font-size:12px;}
    .user{color:rgba(234,240,255,.82); font-size:13px}
    .btn{
      display:inline-flex; align-items:center; justify-content:center;
      padding:10px 12px; border-radius:14px;
      border:1px solid rgba(255,255,255,.14);
      background: rgba(255,255,255,.06);
      color: rgba(234,240,255,.88);
      text-decoration:none;
      transition:.15s ease;
    }
    .btn:hover{transform: translateY(-1px); background: rgba(255,255,255,.08);}
    .btn.primary{
      border:0;
      color:#06101a;
      font-weight:800;
      background: linear-gradient(135deg, rgba(34,211,238,.95), rgba(124,58,237,.95));
      box-shadow: 0 14px 40px rgba(34,211,238,.18);
    }

    .container{
      max-width:1100px; margin:0 auto;
      display:grid; grid-template-columns: 1fr;
      gap:16px;
    }
    .hero{
      border:1px solid rgba(255,255,255,.12);
      border-radius: calc(var(--radius) + 10px);
      background: linear-gradient(180deg, rgba(255,255,255,.08), rgba(255,255,255,.04));
      box-shadow: var(--shadow);
      backdrop-filter: blur(14px);
      padding:22px;
      overflow:hidden;
      position:relative;
    }
    .hero:before{
      content:""; position:absolute; inset:-2px;
      background: conic-gradient(from 220deg,
        rgba(34,211,238,.0),
        rgba(34,211,238,.30),
        rgba(124,58,237,.32),
        rgba(163,255,18,.14),
        rgba(34,211,238,.0));
      filter: blur(26px);
      opacity:.55;
      pointer-events:none;
    }
    .hero h2{
      position:relative;
      margin:0;
      font-size:34px;
      line-height:1.15;
      letter-spacing:-.6px;
    }
    .grad{
      background: linear-gradient(90deg, #EAF0FF, rgba(34,211,238,.95), rgba(124,58,237,.95));
      -webkit-background-clip:text; background-clip:text; color:transparent;
    }
    .hero p{position:relative; margin:10px 0 0; color:var(--muted); line-height:1.7}

    .cards{
      display:grid;
      grid-template-columns: repeat(12, 1fr);
      gap:12px;
    }
    .card{
      grid-column: span 4;
      border:1px solid rgba(255,255,255,.12);
      border-radius: var(--radius);
      background: rgba(10,14,28,.55);
      box-shadow: 0 0 0 1px rgba(255,255,255,.10), 0 18px 60px rgba(0,0,0,.55);
      padding:16px;
      min-height:110px;
    }
    @media (max-width: 900px){ .card{grid-column: span 6;} }
    @media (max-width: 620px){ .card{grid-column: span 12;} }
    .card h3{margin:0 0 6px 0; font-size:14px}
    .card p{margin:0; color:var(--muted); font-size:13px; line-height:1.6}
    .meta{
      margin-top:10px;
      display:flex; gap:10px; flex-wrap:wrap;
      color: rgba(234,240,255,.68);
      font-size:12px;
    }
    .tag{
      padding:6px 8px; border-radius:999px;
      border:1px solid rgba(255,255,255,.14);
      background: rgba(255,255,255,.06);
    }
    /* ===== 修复:标题竖排/容器过窄导致的异常换行 ===== */
    .hero{min-width:0;}
    .hero h2{
      writing-mode: horizontal-tb !important;   /* 强制横排 */
      text-orientation: mixed !important;
      white-space: normal;
      word-break: keep-all;                     /* 中文不要逐字断 */
      overflow-wrap: anywhere;                  /* 必要时才断 */
    }
    
    /* 防止 grid 子项被挤爆(尤其在有全局样式时) */
    .container, .cards, .card{min-width:0;}
    .cards{grid-template-columns: repeat(12, minmax(0, 1fr));}
    
    /* 如果外部样式把所有元素设了很小宽度,这里兜底 */
    .hero, .card{
      width: 100%;
    }
  </style>
</head>
<body>
  <div class="grid"></div>

  <div class="wrap">
    <header class="top">
      <div class="brand">
        <div class="logo" aria-hidden="true"></div>
        <div>
          <h1>Auth Console</h1>
          <p>Dashboard · Secure Session</p>
        </div>
      </div>

      <div style="display:flex; align-items:center; gap:10px;">
        <div class="user">当前状态:已登录</div>
        <a class="btn" href="../php/logout.php">退出</a>
      </div>
    </header>

    <main class="container">
      <section class="hero hero-full">
        <h2><span class="grad">控制台</span> 主页面</h2>
        <p>这是一个静态 HTML 页面(用于展示 UI/布局),但访问入口会由 PHP 进行登录校验后再输出。</p>
      </section>
    
      <section class="cards" aria-label="功能模块">
        <div class="card">
          <h3>账户概览</h3>
          <p>展示当前登录态、账号信息(如需动态展示可后续改成模板变量或 AJAX)。</p>
          <div class="meta"><span class="tag">Session</span><span class="tag">Secure</span></div>
        </div>
        <div class="card">
          <h3>系统状态</h3>
          <p>这里可以放:服务状态、公告、最近登录记录等。</p>
          <div class="meta"><span class="tag">Status</span><span class="tag">Logs</span></div>
        </div>
        <div class="card">
          <h3>快捷入口</h3>
          <p>放你的业务模块入口链接。</p>
          <div class="meta"><span class="tag">Links</span><span class="tag">Apps</span></div>
        </div>
      </section>
    </main>
    
    
    
  </div>
</body>
</html>

config.php

复制代码
<?php
declare(strict_types=1);

session_start();

// ====== 数据库配置(按需修改)======
$DB_HOST = '127.0.0.1';
$DB_NAME = 'jssj';
$DB_USER = 'jssj';
$DB_PASS = '000000';
$DB_CHARSET = 'utf8mb4';
// ===============================

$dsn = "mysql:host={$DB_HOST};dbname={$DB_NAME};charset={$DB_CHARSET}";
$options = [
  PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
  PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
  PDO::ATTR_EMULATE_PREPARES   => false,
];

$pdo = new PDO($dsn, $DB_USER, $DB_PASS, $options);

function h(string $s): string {
  return htmlspecialchars($s, ENT_QUOTES, 'UTF-8');
}

register_handler.php

复制代码
<?php
declare(strict_types=1);

require __DIR__ . '/config.php';

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
  header('Location: ../html/register.html');
  exit;
}

$username = trim($_POST['username'] ?? '');
$email    = trim($_POST['email'] ?? '');
$password = (string)($_POST['password'] ?? '');
$confirm  = (string)($_POST['confirm'] ?? '');

if ($username === '' || $password === '' || $confirm === '') {
  http_response_code(400);
  exit('参数不完整:用户名/密码/确认密码必填');
}
if ($password !== $confirm) {
  http_response_code(400);
  exit('两次输入的密码不一致');
}
if ($email !== '' && !filter_var($email, FILTER_VALIDATE_EMAIL)) {
  http_response_code(400);
  exit('邮箱格式不正确');
}

// 用户名查重
$stmt = $pdo->prepare('SELECT id FROM users WHERE username = ? LIMIT 1');
$stmt->execute([$username]);
if ($stmt->fetch()) {
  http_response_code(409);
  exit('用户名已存在');
}

// 邮箱查重(仅在填写邮箱时)
if ($email !== '') {
  $stmt = $pdo->prepare('SELECT id FROM users WHERE email = ? LIMIT 1');
  $stmt->execute([$email]);
  if ($stmt->fetch()) {
    http_response_code(409);
    exit('邮箱已存在');
  }
}

$hash = password_hash($password, PASSWORD_DEFAULT);

$stmt = $pdo->prepare('INSERT INTO users (username, email, password_hash) VALUES (?, ?, ?)');
$stmt->execute([$username, $email !== '' ? $email : null, $hash]);

header('Location: ../html/login.html');
exit;

login_handler.php

复制代码
<?php
declare(strict_types=1);

require __DIR__ . '/config.php';

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
  header('Location: ../html/login.html');
  exit;
}

$account  = trim($_POST['account'] ?? '');
$password = (string)($_POST['password'] ?? '');

if ($account === '' || $password === '') {
  http_response_code(400);
  exit('请输入账号和密码');
}

$stmt = $pdo->prepare('SELECT id, username, password_hash FROM users WHERE username = ? OR email = ? LIMIT 1');
$stmt->execute([$account, $account]);
$user = $stmt->fetch();

if (!$user || !password_verify($password, $user['password_hash'])) {
  http_response_code(401);
  exit('账号或密码错误');
}

session_regenerate_id(true);
$_SESSION['user_id'] = (int)$user['id'];
$_SESSION['username'] = (string)$user['username'];

header('Location: dashboard.php');
exit;

dashboard.php

复制代码
<?php
declare(strict_types=1);

require __DIR__ . '/config.php';

if (!isset($_SESSION['user_id'])) {
  header('Location: ../html/login.html');
  exit;
}

// 输出静态 dashboard.html
$dashboardFile = __DIR__ . '/../html/dashboard.html';
if (!is_file($dashboardFile)) {
  http_response_code(500);
  exit('dashboard.html 不存在,请创建:/demo/html/dashboard.html');
}

header('Content-Type: text/html; charset=UTF-8');
readfile($dashboardFile);
exit;

logout.php

复制代码
<?php
declare(strict_types=1);

require __DIR__ . '/config.php';

// 清空会话数据
$_SESSION = [];

// 删除会话 Cookie(如果启用)
if (ini_get('session.use_cookies')) {
  $params = session_get_cookie_params();
  setcookie(
    session_name(),
    '',
    time() - 42000,
    $params['path'],
    $params['domain'],
    $params['secure'],
    $params['httponly']
  );
}

// 销毁会话
session_destroy();

header('Location: ../html/login.html');
exit;
相关推荐
Dxy12393102162 小时前
MySQL如何避免隐式转换
开发语言·mysql
历程里程碑2 小时前
Linux 18 进程控制
linux·运维·服务器·开发语言·数据结构·c++·笔记
froginwe112 小时前
C# 预处理器指令
开发语言
xdpcxq10292 小时前
MySQL 5.6 2000 万行高频读写表新增字段
数据库·mysql
爱装代码的小瓶子2 小时前
【c++与Linux基础】文件篇(5)- 文件管理系统:
linux·开发语言·c++
darkb1rd2 小时前
三、PHP字符串处理与编码安全
android·安全·php
马猴烧酒.2 小时前
【团队空间|第十一天】基础功能实现,RBAC权限控制,ShardingSphere详解
java·开发语言·数据库
fengxin_rou2 小时前
从 String 到 Zset:Redis 核心数据结构全解析及排行榜应用
java·开发语言·redis·多线程
Re.不晚2 小时前
Java进阶之路--线程最最详细讲解
java·开发语言