Files
server-core/app/jwt.py
T
Pine 6bb988cc3e refactor: 角色组织账号模型重构 - 阶段一
1. capabilities 写入 JWT,避免每次请求查库
2. 移除 service 账号类型,非 operator/carrier 统一归为 opc_member
3. 前端统一基于 capabilities 判断权限
4. 清理登录接口死代码,恢复账号密码登录
5. 移除 enterprise/provider/government/investor/developer 角色
6. 清理 seed.py 中多余角色和权限定义
7. 重写 rbac_portals.py 只保留 carrier 路由
2026-09-03 00:35:40 +08:00

78 lines
2.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""JWT 访问令牌:签发与验签。
- 无状态验签(HS256),claims 携带角色/组织/数据范围/权限。
- 撤销由 ``sessions`` 表(按 jti+ ``users.token_version`` 兜底,
``get_current_user`` 每请求一次 DB 读校验。
"""
from __future__ import annotations
import uuid
from datetime import datetime, timedelta, timezone
import jwt as pyjwt
from . import config
JTI_TTL_SECONDS = config.JWT_ACCESS_EXPIRY_SECONDS
def _now_ts() -> int:
return int(datetime.now(timezone.utc).timestamp())
def create_access_token(
user: dict,
*,
permissions: list[str],
scope_region_ids: list[str],
scope_level: str | None,
capabilities: list[str],
expiry_seconds: int,
identity_id: str | None = None,
) -> tuple[str, str, str]:
"""签发 JWT,返回 (token, jti, expires_at_iso)。
``user`` 为来自 UserRepository 的用户 dict(含 id/username/role/
sub_role/org_id/region_id/token_version)。``identity_id`` 非空时表示
该令牌绑定到指定端口身份(get_current_user 据此解析当前身份)。
``capabilities`` 为叠加制能力集合(opc_member/operator/carrier/enterprise),
写入 JWT 后避免每次请求查库计算。
"""
jti = str(uuid.uuid4())
now = _now_ts()
exp_ts = now + (expiry_seconds if expiry_seconds else config.JWT_ACCESS_EXPIRY_SECONDS)
payload = {
"sub": user["id"],
"username": user.get("username", ""),
"role": user.get("role", "opc_member"),
"sub_role": user.get("sub_role"),
"org_id": user.get("org_id"),
"region_id": user.get("region_id"),
"identity_id": identity_id,
"scope_level": scope_level,
"scope_region_ids": scope_region_ids,
"perms": permissions,
"capabilities": capabilities,
"ver": user.get("token_version", 0),
"jti": jti,
"iat": now,
"exp": exp_ts,
}
token = pyjwt.encode(payload, config.JWT_SECRET, algorithm=config.JWT_ALGORITHM)
expires_at = datetime.fromtimestamp(exp_ts, tz=timezone.utc).isoformat()
return token, jti, expires_at
def decode_access_token(token: str) -> dict | None:
"""验签并返回 claims;无效/过期返回 None。"""
try:
payload = pyjwt.decode(
token,
config.JWT_SECRET,
algorithms=[config.JWT_ALGORITHM],
)
except pyjwt.PyJWTError:
return None
return payload