# ===== CodeLab: net-auth =====
# 以下代码片段按文章出现顺序拼接, 共 8 段

# ----- 片段 1 (bash) -----
# 请求头里携带 Token 的标准姿势
curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.xxx" https://api.example.com/me

# ----- 片段 2 (text) -----
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJ1c2VyIjoiY29kZWxhYiIsImV4cCI6MTc1MDAwMDAwMH0.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

# ----- 片段 3 (bash) -----
# 第二段是 base64url 编码的 JSON
echo "eyJ1c2VyIjoiY29kZWxhYiIsImV4cCI6MTc1MDAwMDAwMH0" | base64 -d

# ----- 片段 4 (python) -----
import base64, hashlib, hmac, json, time

SECRET = b"my-secret-key"   # 真实项目中放环境变量,绝不能写进代码

def b64url(data: bytes) -> str:
    return base64.urlsafe_b64encode(data).rstrip(b"=").decode()

def make_token(payload: dict, ttl: int = 3600) -> str:
    header = {"alg": "HS256", "typ": "JWT"}
    body = dict(payload, exp=int(time.time()) + ttl)  # 内置过期时间
    seg1 = b64url(json.dumps(header, separators=(",", ":")).encode())
    seg2 = b64url(json.dumps(body, separators=(",", ":")).encode())
    sig = b64url(hmac.new(SECRET, f"{seg1}.{seg2}".encode(), hashlib.sha256).digest())
    return f"{seg1}.{seg2}.{sig}"

token = make_token({"user": "codelab"})
print("签发结果:", token)

# ----- 片段 5 (python) -----
import base64, hashlib, hmac, json, time

SECRET = b"my-secret-key"

def b64url(data: bytes) -> str:
    return base64.urlsafe_b64encode(data).rstrip(b"=").decode()

def make_token(payload: dict, ttl: int = 3600) -> str:
    header = {"alg": "HS256", "typ": "JWT"}
    body = dict(payload, exp=int(time.time()) + ttl)
    seg1 = b64url(json.dumps(header, separators=(",", ":")).encode())
    seg2 = b64url(json.dumps(body, separators=(",", ":")).encode())
    sig = b64url(hmac.new(SECRET, f"{seg1}.{seg2}".encode(), hashlib.sha256).digest())
    return f"{seg1}.{seg2}.{sig}"

def verify(token: str):
    try:
        seg1, seg2, sig = token.split(".")
        # 恒定时间比较,防止攻击者根据响应时间差猜签名
        expect = b64url(hmac.new(SECRET, f"{seg1}.{seg2}".encode(), hashlib.sha256).digest())
        if not hmac.compare_digest(expect, sig):
            return None, "签名无效,Token 被篡改"
        body = json.loads(base64.urlsafe_b64decode(seg2 + "=="))
        if body.get("exp", 0) < time.time():
            return None, "Token 已过期"
        return body, None
    except Exception as e:
        return None, f"解析失败: {e}"

token = make_token({"user": "codelab"})
print("正常校验:", verify(token))
print("篡改校验:", verify(token[:-1] + ("A" if token[-1] != "A" else "B")))

# ----- 片段 6 (python) -----
import base64, hashlib, hmac, json, time
import threading
from http.server import BaseHTTPRequestHandler, HTTPServer
import http.client

SECRET = b"my-secret-key"

def b64url(data: bytes) -> str:
    return base64.urlsafe_b64encode(data).rstrip(b"=").decode()

def make_token(user: str) -> str:
    header = {"alg": "HS256", "typ": "JWT"}
    body = {"user": user, "exp": int(time.time()) + 3600}
    seg1 = b64url(json.dumps(header, separators=(",", ":")).encode())
    seg2 = b64url(json.dumps(body, separators=(",", ":")).encode())
    sig = b64url(hmac.new(SECRET, f"{seg1}.{seg2}".encode(), hashlib.sha256).digest())
    return f"{seg1}.{seg2}.{sig}"

def verify(token: str):
    try:
        seg1, seg2, sig = token.split(".")
        expect = b64url(hmac.new(SECRET, f"{seg1}.{seg2}".encode(), hashlib.sha256).digest())
        if not hmac.compare_digest(expect, sig):
            return None
        body = json.loads(base64.urlsafe_b64decode(seg2 + "=="))
        if body.get("exp", 0) < time.time():
            return None
        return body
    except Exception:
        return None

class App(BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path == "/login":
            body = json.dumps({"token": make_token("codelab")}).encode()
            self.send_response(200)
            self.send_header("Content-Type", "application/json")
            self.end_headers()
            self.wfile.write(body)
        elif self.path == "/me":
            auth = self.headers.get("Authorization", "")
            user = verify(auth.removeprefix("Bearer "))
            if user:
                body = json.dumps({"ok": True, "user": user["user"]}).encode()
                self.send_response(200)
            else:
                body = b'{"ok": false, "reason": "unauthorized"}'
                self.send_response(401)
            self.send_header("Content-Type", "application/json")
            self.end_headers()
            self.wfile.write(body)
        else:
            self.send_response(404)
            self.end_headers()

    def log_message(self, *args):
        pass

server = HTTPServer(("127.0.0.1", 0), App)
port = server.server_address[1]
threading.Thread(target=server.serve_forever, daemon=True).start()

def get(path, headers=None):
    conn = http.client.HTTPConnection("127.0.0.1", port, timeout=3)
    conn.request("GET", path, headers=headers or {})
    r = conn.getresponse()
    data = r.read().decode()
    conn.close()
    return r.status, data

print("不带 Token:", get("/me"))
_, login = get("/login")
token = json.loads(login)["token"]
print("带 Token:", get("/me", {"Authorization": "Bearer " + token}))
print("篡改 Token:", get("/me", {"Authorization": "Bearer abc.def.ghi"}))

server.shutdown()

# ----- 片段 7 (bash) -----
# 1. 登录拿 Token
curl -s http://127.0.0.1:8000/login

# 2. 把上一步返回的 token 填进来,访问受保护接口
curl -s -H "Authorization: Bearer <上一步的token>" http://127.0.0.1:8000/me

# 3. 不带 Token:被 401 拒绝
curl -s http://127.0.0.1:8000/me

# ----- 片段 8 (javascript) -----
fetch("/api/me", {
  headers: { Authorization: "Bearer " + token }
}).then(r => r.json()).then(console.log);
