數據庫設計:RBAC數據模型
這是一個典型的基于SQLAlchemy的RBAC權限系統數據模型實現,各模型分工明確,共同構成完整的權限管理系統。
圖解說明:
-
實體關系:
-
用戶(USER)和角色(ROLE)通過?USER_ROLE?中間表實現多對多關系
-
角色(ROLE)和權限(PERMISSION)通過?ROLE_PERMISSION?中間表實現多對多關系
-
-
關鍵連接點:
USER → USER_ROLE ← ROLE → ROLE_PERMISSION ← PERMISSION
這個鏈條完整表達了"用戶→角色→權限"的授權路徑
-
箭頭含義:
-
||--o{
?表示一對多關系 -
PK
?表示主鍵 -
FK
?表示外鍵
-
-
實際業務流:
當檢查用戶是否有某接口的訪問權限時,系統會:用戶ID → 查詢USER_ROLE → 獲取角色列表 → 查詢ROLE_PERMISSION → 獲取權限列表 → 匹配權限中的resource字段與當前請求接口
這個圖示清晰地展示了您提供的四個模型如何協作完成RBAC權限控制。如果需要更詳細的流程圖或時序圖,我可以進一步補充。
1. Role (角色模型)
-
核心作用:定義系統中的角色實體
-
關鍵字段:
-
role_name
: 角色唯一標識(如admin/hr/interviewer) -
description
: 角色描述信息
-
-
關聯關系:
-
users
: 通過UserRole關聯到用戶(一對多) -
permissions
: 通過RolePermission關聯到權限(一對多)
-
-
業務意義:角色是權限分配的中間層,用戶通過角色間接獲得權限
2. Permission (權限模型)
-
核心作用:定義系統中的具體權限項
-
關鍵字段:
-
permission_name
: 權限唯一標識(如knowledge_create) -
resource
: 關聯的資源路徑/API端點
-
-
關聯關系:
-
roles
: 通過RolePermission關聯到角色(一對多)
-
-
業務意義:權限是系統中最細粒度的訪問控制單元,直接對應具體操作
3. UserRole (用戶-角色關聯模型)
-
核心作用:維護用戶與角色的多對多關系
-
關鍵設計:
-
聯合唯一約束(
uq_user_role
): 防止重復分配相同角色 -
級聯刪除: 用戶刪除時自動解除關聯
-
-
關聯關系:
-
user
: 關聯到用戶表 -
role
: 關聯到角色表
-
-
業務意義:實現"用戶擁有哪些角色"的映射關系
4. RolePermission (角色-權限關聯模型)
-
核心作用:維護角色與權限的多對多關系
-
關鍵設計:
-
聯合唯一約束(
uq_role_permission
): 防止重復分配相同權限 -
級聯刪除: 角色刪除時自動解除關聯
-
-
關聯關系:
-
role
: 關聯到角色表 -
permission
: 關聯到權限表
-
-
業務意義:實現"角色包含哪些權限"的映射關系
整體協作流程
-
管理員創建權限(Permission)并定義其對應的資源
-
創建角色(Role)并將相關權限關聯到角色(RolePermission)
-
為用戶分配角色(UserRole)
-
系統通過檢查用戶的角色→權限→資源路徑鏈來確定訪問權限
這種設計實現了權限管理的解耦,使系統能夠靈活應對權限變更,同時通過中間表實現了多對多關系的維護。
class User(Base):__tablename__ = 'user'__table_args__ = {'extend_existing': True}user_id = Column(Integer, primary_key=True, autoincrement=True, comment='Primary Key')user_name = Column(String(100), nullable=False)password = Column(String(200), nullable=False)user_email = Column(String(100), nullable=False, unique=True)user_department = Column(String(100), nullable=True)join_time = Column(DateTime, nullable=False, default=datetime.now)# 關聯 Session 模型,代表該用戶的所有會話sessions = relationship("Session", back_populates="user", cascade="all, delete-orphan")# 關聯 Knowledge 模型,代表該用戶關聯的所有知識knowledge = relationship("Knowledge", back_populates="user", cascade="all, delete-orphan")# 添加與UserRole的關系roles = relationship("UserRole", back_populates="user", cascade="all, delete-orphan")class Role(Base):__tablename__ = 'role'__table_args__ = {'extend_existing': True}role_id = Column(Integer, primary_key=True, autoincrement=True, comment='Primary Key')role_name = Column(String(50), nullable=False, unique=True, comment='角色名稱,如admin/hr/interviewer')description = Column(String(200), comment='角色描述')create_time = Column(DateTime, nullable=False, default=datetime.now)modify_time = Column(DateTime, nullable=False, default=datetime.now, onupdate=datetime.now)# 關聯到UserRole和RolePermissionusers = relationship("UserRole", back_populates="role", cascade="all, delete-orphan")permissions = relationship("RolePermission", back_populates="role", cascade="all, delete-orphan")class Permission(Base):__tablename__ = 'permission'__table_args__ = {'extend_existing': True}permission_id = Column(Integer, primary_key=True, autoincrement=True, comment='Primary Key')permission_name = Column(String(100), nullable=False, unique=True, comment='權限名稱,如knowledge_create/session_delete')description = Column(String(200), comment='權限描述')resource = Column(String(200), comment='對應的資源路徑/API端點')create_time = Column(DateTime, nullable=False, default=datetime.now)modify_time = Column(DateTime, nullable=False, default=datetime.now, onupdate=datetime.now)# 關聯到RolePermissionroles = relationship("RolePermission", back_populates="permission", cascade="all, delete-orphan")class UserRole(Base):__tablename__ = 'user_role'__table_args__ = (UniqueConstraint('user_id', 'role_id', name='uq_user_role'),{'extend_existing': True})id = Column(Integer, primary_key=True, autoincrement=True)user_id = Column(Integer, ForeignKey('user.user_id', ondelete='CASCADE'), nullable=False)role_id = Column(Integer, ForeignKey('role.role_id', ondelete='CASCADE'), nullable=False)create_time = Column(DateTime, nullable=False, default=datetime.now)# 關聯到User和Roleuser = relationship("User", back_populates="roles")role = relationship("Role", back_populates="users")class RolePermission(Base):__tablename__ = 'role_permission'__table_args__ = (UniqueConstraint('role_id', 'permission_id', name='uq_role_permission'),{'extend_existing': True})id = Column(Integer, primary_key=True, autoincrement=True)role_id = Column(Integer, ForeignKey('role.role_id', ondelete='CASCADE'), nullable=False)permission_id = Column(Integer, ForeignKey('permission.permission_id', ondelete='CASCADE'), nullable=False)create_time = Column(DateTime, nullable=False, default=datetime.now)# 關聯到Role和Permissionrole = relationship("Role", back_populates="permissions")permission = relationship("Permission", back_populates="roles")
后端技術實現流程:
JWT (JSON Web Token)
-
一種緊湊的、URL安全的令牌格式
-
包含三部分:Header(頭部)、Payload(負載)和Signature(簽名)
-
用于在各方之間安全地傳輸信息
OAuth 2.0
-
一個授權框架,允許第三方應用獲取對資源的有限訪問權限
-
定義了四種授權流程(授權碼、隱式、密碼、客戶端憑證)
典型 JWT + OAuth 2.0 流程 (授權碼模式)
-
用戶發起授權請求
-
客戶端將用戶重定向到授權服務器
-
攜帶參數:
response_type=code
,?client_id
,?redirect_uri
,?scope
等
-
-
用戶認證與同意
-
用戶在授權服務器登錄并同意請求的權限
-
-
獲取授權碼
-
授權服務器返回授權碼到客戶端的
redirect_uri
-
-
用授權碼交換令牌
-
客戶端向授權服務器的令牌端點發送請求
-
攜帶:
grant_type=authorization_code
,?code
,?redirect_uri
,?client_id
,?client_secret
-
-
返回JWT令牌
-
授權服務器驗證后返回訪問令牌(通常是JWT)和刷新令牌
-
響應示例:
{"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...","token_type": "Bearer","expires_in": 3600,"refresh_token": "def50200ae2f..." }
-
-
使用訪問令牌訪問資源
-
客戶端在請求頭中加入:
Authorization: Bearer <access_token>
-
資源服務器驗證JWT簽名和聲明
-
-
刷新令牌(可選)
-
當訪問令牌過期時,使用刷新令牌獲取新的訪問令牌
-
JWT在OAuth中的優勢
-
自包含:JWT包含所有必要信息,減少數據庫查詢
-
可驗證性:資源服務器可以獨立驗證JWT而不需聯系授權服務器
-
標準化結構:易于跨語言和平臺實現
# app/middlewares/permission.py
from fastapi import Request, HTTPException, Depends
from sqlalchemy.orm import Session
from app.db_init import SessionLocal
from app.models.model import User, Permission, RolePermission, UserRole, Role # 添加 Role 導入
from functools import wrapsdef permission_required(permission_name: str):"""權限檢查依賴工廠函數"""async def dependency(request: Request):db: Session = SessionLocal()try:user_id = getattr(request.state, 'user_id', None)if not user_id:raise HTTPException(status_code=401, detail="未認證")# 檢查是否是管理員/root用戶user = db.query(User).filter(User.user_id == user_id).first()if not user:raise HTTPException(status_code=401, detail="用戶不存在")# 檢查用戶角色admin_role = (db.query(UserRole).join(Role, Role.role_id == UserRole.role_id).filter(UserRole.user_id == user_id).filter(Role.role_name == 'admin').first())if admin_role:return # 管理員直接通過權限檢查# 普通用戶權限檢查has_permission = db.query(Permission).join(RolePermission, RolePermission.permission_id == Permission.permission_id).join(UserRole, UserRole.role_id == RolePermission.role_id).filter(UserRole.user_id == user_id,Permission.permission_name == permission_name).first()if not has_permission:raise HTTPException(status_code=403, detail="權限不足")finally:db.close()return Depends(dependency)
permission_required
?函數是一個 FastAPI 的權限檢查依賴工廠函數,主要用于實現基于權限的訪問控制。它的主要作用和功能如下:
-
功能概述:
-
它是一個裝飾器工廠函數,接收一個權限名稱作為參數,返回一個 FastAPI 依賴項
-
用于檢查當前請求的用戶是否擁有指定的權限
-
-
工作流程:
-
從請求中獲取用戶 ID
-
查詢數據庫驗證用戶是否存在
-
檢查用戶是否是管理員角色(role_name 為 'admin'),如果是則直接通過檢查
-
對于非管理員用戶,檢查用戶是否擁有指定的權限
-
如果沒有權限則返回 403 錯誤
-
-
權限檢查邏輯:
-
通過連接查詢檢查用戶角色關聯的權限
-
查詢涉及多個表:User → UserRole → RolePermission → Permission
-
只有當用戶至少有一個角色擁有指定的權限時才會通過檢查
-
-
異常處理:
-
未認證用戶(無 user_id):返回 401
-
用戶不存在:返回 401
-
權限不足:返回 403
-
什么是中間件?
在 Web 開發中,中間件(Middleware)?是一種機制,用于在?HTTP 請求到達路由處理函數之前?或?響應返回客戶端之前?執行某些邏輯。它類似于一個“攔截器”,可以對請求和響應進行預處理或后處理。
中間件的作用
-
全局處理請求/響應
-
例如:身份驗證、日志記錄、跨域處理(CORS)、請求限流、數據壓縮等。
-
-
修改請求或響應
-
例如:添加請求頭、解析請求體、修改響應數據。
-
-
攔截非法請求
-
例如:檢查權限(如你的?
permission_required
)、阻止未授權的訪問。
-
中間件的工作原理
在 FastAPI 或類似的框架(如 Express.js、Django)中,中間件通常按照?洋蔥模型(Onion Model)?工作:
請求 → 中間件1 → 中間件2 → ... → 路由處理 → ... → 中間件2 → 中間件1 → 響應
-
請求階段:中間件按順序執行(如先驗證身份,再檢查權限)。
-
響應階段:中間件按相反順序執行(如先記錄日志,再返回數據)。
FastAPI 中的中間件示例
1.?普通中間件(全局攔截)
from fastapi import FastAPI, Requestapp = FastAPI()@app.middleware("http")
async def log_requests(request: Request, call_next):print(f"收到請求: {request.method} {request.url}")response = await call_next(request) # 繼續執行后續中間件或路由print(f"返回響應: {response.status_code}")return response
-
這個中間件會在?每個請求前后?打印日志。
2.?依賴注入式中間件(如你的?permission_required
)
from fastapi import Depends, HTTPExceptiondef check_auth(token: str):if token != "secret":raise HTTPException(status_code=403, detail="無權訪問")return True@app.get("/admin")
async def admin_route(auth: bool = Depends(check_auth)):return {"message": "歡迎管理員"}
-
Depends(check_auth)
?是一個依賴項,作用類似于中間件,但更靈活(可以針對單個路由使用)。
中間件 vs 依賴注入
特性 | 中間件 | 依賴注入(如?Depends ) |
---|---|---|
作用范圍 | 全局或路由組 | 單個路由 |
執行順序 | 所有請求必經 | 僅對聲明了的路由生效 |
典型用途 | 日志、CORS、全局認證 | 細粒度權限檢查、數據庫會話管理 |
你的?permission_required
?是中間件嗎?
嚴格來說,它?不是傳統中間件,而是一個?依賴項工廠函數(返回?Depends
)。但它實現了類似中間件的功能(權限檢查),只是作用范圍更精確(僅對使用它的路由生效)。
總結
-
中間件:適合全局邏輯(如日志、CORS)。
-
依賴注入:適合路由級邏輯(如權限、數據庫會話)。
-
你的?
permission_required
?是依賴注入的典型應用,用于實現 RBAC(基于角色的訪問控制)。
@router.post('/login')
async def login(request_data: LoginRequest, db = Depends(get_db)):'''登錄界面'''username = request_data.usernamepassword = request_data.password# 根據用戶名查詢數據庫中用戶的密碼info = AuthService.select_user_info(username, db)if info is None:return {'code': 401,'message': 'user not found'}# 密碼驗證if password != info['password']:return {'code': 401,'message': 'password error'}# 獲取用戶角色user_roles = AuthService.get_user_roles(info['user_id'], db)is_admin = 'admin' in [role.lower() for role in user_roles]# 獲取用戶權限permissions = AuthService.get_user_permissions(info['user_id'], db)if permissions is None:permissions = []# 創建JWT令牌access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)access_token = create_access_token(data={"sub": str(info['user_id']), # 確保user_id是字符串"username": info['user_name'],"permissions": permissions,"is_admin": is_admin,"roles": user_roles},expires_delta=access_token_expires)return {'code': 200,'message': 'success','data': {'user_id': info['user_id'],'username': info['user_name'],'access_token': access_token,'token_type': 'bearer','permissions': permissions,'roles': user_roles,'is_admin': is_admin}}
這個?login
?函數是一個?FastAPI 路由處理函數,用于實現用戶登錄認證流程。它的核心作用是:
主要功能
-
用戶身份認證
-
接收前端提交的用戶名和密碼(通過?
LoginRequest
?模型)。 -
驗證用戶名是否存在、密碼是否正確。
-
返回 401 錯誤碼和提示信息(如 "user not found" 或 "password error")。
-
-
權限和角色信息收集
-
查詢用戶的角色(如?
user_roles
)并檢查是否是管理員(is_admin
)。 -
查詢用戶擁有的權限列表(
permissions
)。
-
-
生成 JWT 令牌
-
使用?
create_access_token
?生成包含用戶信息的 JWT(有效期通過?ACCESS_TOKEN_EXPIRE_MINUTES
?控制)。 -
令牌中存儲的關鍵數據:
{"sub": "用戶ID", # 用戶唯一標識"username": "用戶名","permissions": ["權限1", "權限2"], # 用戶權限列表"is_admin": True/False, # 是否是管理員"roles": ["角色1", "角色2"] # 用戶角色列表 }
-
-
返回登錄結果
-
返回 200 狀態碼和用戶信息(包括令牌、權限、角色等),供前端后續使用。
-
詳細流程
-
參數接收
-
通過?
LoginRequest
?模型(未展示但應包含?username
?和?password
?字段)接收前端提交的登錄數據。
-
-
用戶驗證
-
調用?
AuthService.select_user_info
?查詢數據庫,檢查用戶名是否存在。 -
比對數據庫中的密碼(明文比對,實際項目中應使用?哈希密碼比對,如?
bcrypt
)。
-
-
權限角色查詢
-
通過?
AuthService.get_user_roles
?獲取用戶角色列表,并檢查是否包含?admin
?角色。 -
通過?
AuthService.get_user_permissions
?獲取用戶權限列表(用于前端動態路由或按鈕權限控制)。
-
-
令牌生成
-
使用?
create_access_token
(可能是?fastapi-jwt
?或類似庫)生成 JWT,包含用戶關鍵信息。
-
-
響應返回
-
返回結構化響應,包含令牌和用戶信息,例如:
{"code": 200,"message": "success","data": {"user_id": 123,"username": "john","access_token": "xxx.yyy.zzz","permissions": ["read", "write"],"roles": ["user"],"is_admin": false} }
-
關鍵點說明
-
安全注意事項
-
密碼明文存儲問題:當前代碼直接比對明文密碼,實際項目應使用?密碼哈希(如?
bcrypt
)存儲和驗證。 -
JWT 敏感信息:令牌中存儲了?
permissions
?和?roles
,需確保 JWT 使用 HTTPS 傳輸并設置合理有效期。
-
-
權限控制設計
-
前端可根據返回的?
permissions
?和?roles
?動態渲染界面。 -
后端可通過?
permission_required
?中間件(如你之前提供的)進一步校驗權限。
-
-
擴展性
-
可添加登錄日志記錄、多設備登錄限制、驗證碼等功能。
-
典型使用場景
-
前端提交登錄表單 → 后端驗證 → 返回令牌。
-
前端將令牌存儲在?
localStorage
?或?Cookie
?中,后續請求通過?Authorization: Bearer <token>
?頭部攜帶。 -
其他受保護的路由通過中間件校驗令牌和權限(如?
@router.get("/admin", dependencies=[Depends(permission_required("admin_access"))]
)。
這個函數是典型的認證系統核心部分,實現了從登錄到權限令牌分發的完整流程。
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from jose import JWTError, jwt
from datetime import datetime, timedelta
from utils.configs import SECRET_KEY, ALGORITHM, ACCESS_TOKEN_EXPIRE_MINUTES
from fastapi import Request# OAuth2 配置
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")# 創建訪問令牌
def create_access_token(data: dict, expires_delta: timedelta = None):to_encode = data.copy()if expires_delta:expire = datetime.utcnow() + expires_deltaelse:expire = datetime.utcnow() + timedelta(minutes=15)to_encode.update({"exp": expire})encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)return encoded_jwtasync def get_current_user(token: str = Depends(oauth2_scheme)):credentials_exception = HTTPException(status_code=status.HTTP_401_UNAUTHORIZED,detail="Could not validate credentials",headers={"WWW-Authenticate": "Bearer"},)try:payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])user_id = payload.get("sub")if user_id is None:raise credentials_exceptionpermissions = payload.get("permissions", [])username = payload.get("username")is_admin = payload.get("is_admin", False)roles = payload.get("roles", [])# 返回一個包含所需信息的字典return {"user_id": user_id,"username": username,"permissions": permissions,"is_admin": is_admin,"roles": roles}except JWTError:raise credentials_exceptionasync def auth_middleware(request: Request, call_next):try:token = request.headers.get('Authorization')if token and token.startswith('Bearer '):token = token.split(' ')[1]payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])request.state.user_id = payload.get("sub")request.state.permissions = payload.get("permissions", [])except JWTError:passresponse = await call_next(request)return response
這幾個函數共同構成了一個?FastAPI 的 JWT 認證系統,分別負責?令牌生成、用戶認證和請求攔截。以下是它們的詳細作用:
1.?create_access_token
?- JWT 令牌生成
作用:
生成一個包含用戶信息的 JWT(JSON Web Token),用于身份驗證和授權。
關鍵邏輯:
-
接收一個字典?
data
(包含用戶信息如?user_id
、permissions
?等)。 -
設置令牌過期時間(默認 15 分鐘,或通過?
expires_delta
?自定義)。 -
使用?
SECRET_KEY
?和?ALGORITHM
(如 HS256)簽名令牌。
示例輸出:
token = create_access_token({"sub": "123", "username": "john"})
# 類似:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjMiLCJleHAiOjE2...
2.?get_current_user
?- 當前用戶認證
作用:
解析請求中的 JWT 令牌,驗證用戶身份并返回用戶信息。
關鍵邏輯:
-
通過?
OAuth2PasswordBearer
?自動從請求頭?Authorization: Bearer <token>
?提取令牌。 -
解碼令牌并驗證簽名(使用?
SECRET_KEY
)。 -
提取關鍵字段(如?
user_id
、permissions
、is_admin
)。 -
如果令牌無效或過期,拋出?
401 Unauthorized
?錯誤。
典型用法:
@app.get("/protected")
async def protected_route(user: dict = Depends(get_current_user)):return {"user": user}
3.?auth_middleware
?- 認證中間件
作用:
攔截所有請求,解析 JWT 令牌并將用戶信息注入到?request.state
?中,供后續路由或中間件使用。
關鍵邏輯:
-
從請求頭提取?
Bearer
?令牌。 -
解碼令牌并提取?
user_id
?和?permissions
,存入?request.state
。 -
如果令牌無效,靜默跳過(不中斷請求,適合非強制校驗的場景)。
與?get_current_user
?的區別:
特性 | auth_middleware | get_current_user |
---|---|---|
作用范圍 | 全局(所有請求) | 單個路由(需顯式聲明?Depends ) |
認證嚴格性 | 靜默失敗(僅注入數據) | 嚴格失敗(401 錯誤) |
典型用途 | 日志、基礎用戶信息注入 | 需要強認證的路由 |
協作流程
-
用戶登錄
-
調用?
create_access_token
?生成令牌并返回給前端。
-
-
前端請求
-
在請求頭中添加?
Authorization: Bearer <token>
。
-
-
請求處理
-
auth_middleware
?攔截請求,解析令牌并注入用戶信息到?request.state
。 -
路由層可通過?
get_current_user
?進一步嚴格校驗(如需要權限的接口)。
-
安全注意事項
-
密鑰管理
-
SECRET_KEY
?必須保密,且不要硬編碼在代碼中(從環境變量讀取)。
-
-
令牌有效期
-
生產環境中?
ACCESS_TOKEN_EXPIRE_MINUTES
?不宜過長(通常 15-60 分鐘)。
-
-
算法選擇
-
推薦使用?
HS256
(對稱加密)或?RS256
(非對稱加密)。
-
-
中間件靜默失敗
-
auth_middleware
?不強制攔截無效令牌,需確保關鍵路由額外依賴?get_current_user
。
-
擴展建議
-
刷新令牌:添加?
refresh_token
?機制實現無感續期。 -
權限校驗:結合?
permission_required
(你之前的代碼)實現細粒度控制。 -
日志記錄:在中間件中記錄用戶操作日志。
這些函數共同構建了一個完整的 JWT 認證體系,覆蓋了從令牌生成到請求處理的完整鏈路。
然后將這個認證中間件添加到接收HTTP請求之前
然后在路由中添加權限名稱
在 FastAPI 中,_=permission_required("abilities_read")
?是一種?依賴注入(Dependency Injection)?的用法,用于在路由處理函數執行前進行?權限校驗。以下是詳細解釋:
1. 代碼含義
-
permission_required("abilities_read")
這是一個依賴項工廠函數(你之前定義的),它會檢查當前用戶是否擁有?abilities_read
?權限。 -
_=
將依賴項的返回值賦值給變量?_
(下劃線是 Python 中約定用于“忽略此變量”的命名方式),表示我們不需要在函數體內使用這個返回值。
2. 執行流程
-
請求到達路由
當客戶端訪問?GET /api/abilities
?時,FastAPI 會先執行?permission_required("abilities_read")
?的校驗邏輯。 -
權限校驗
-
如果用戶?有權限:繼續執行?
get_abilities
?函數體。 -
如果用戶?無權限:直接返回?
403 Forbidden
?錯誤(由?permission_required
?拋出)。
-
-
處理業務邏輯
只有通過權限校驗后,才會調用?get_all_competency_items()
?獲取數據并返回。
3. 為什么用?_=
?
-
明確忽略返回值:
permission_required
?可能返回某些值(如用戶信息),但當前路由不需要使用它,用?_
?表示“故意忽略”。 -
代碼可讀性:
明確告訴其他開發者:“這里有一個權限檢查,但我不需要它的結果”。
前端:基于 Vuex 的前端狀態管理 + JWT 認證體系
import axios from 'axios'
import { API_URLS } from '@/config/api'const state = {token: localStorage.getItem('token') || null,user: null,permissions: [],role: null
}const mutations = {SET_TOKEN(state, token) {state.token = tokenlocalStorage.setItem('token', token)axios.defaults.headers.common['Authorization'] = `Bearer ${token}`},CLEAR_TOKEN(state) {state.token = nulllocalStorage.removeItem('token')delete axios.defaults.headers.common['Authorization']},SET_USER(state, user) {state.user = user},SET_PERMISSIONS(state, permissions) {state.permissions = permissions},SET_ROLE(state, role) {state.role = role}
}const actions = {async login({ commit }, { username, password }) {const response = await axios.post(API_URLS.LOGIN, { username, password })commit('SET_TOKEN', response.data.access_token)commit('SET_PERMISSIONS', response.data.permissions)return response.data},async fetchPermissions({ commit, state }) {if (!state.token) returnconst response = await axios.get(API_URLS.PERMISSIONS)commit('SET_PERMISSIONS', response.data.permissions)return response.data},logout({ commit }) {commit('CLEAR_TOKEN')commit('SET_USER', null)commit('SET_PERMISSIONS', [])commit('SET_ROLE', null)},async getPermissions({ commit }) {const response = await axios.get('/auth/permissions')commit('SET_PERMISSIONS', response.data)return response.data}
}export default {namespaced: true,state,mutations,actions
}
import { createStore } from 'vuex'
import user from './modules/user' // 導入user模塊export default createStore({state: {userId: '',isAuthenticated: false},mutations: {setUserId(state, id) {state.userId = parseInt(id)state.isAuthenticated = !!id}},modules: {user // 注冊user模塊},// 添加持久化plugins: [store => {// 初始化時從 localStorage 獲取狀態const userId = localStorage.getItem('userId')if (userId) {store.commit('setUserId', userId)}// 訂閱 mutationstore.subscribe((mutation, state) => {if (mutation.type === 'setUserId') {localStorage.setItem('userId', state.userId)}})}]
})
這兩部分代碼共同構成了一個?基于 Vuex 的前端狀態管理 + JWT 認證體系,主要處理用戶認證、權限管理和狀態持久化。以下是詳細解析:
1.?user
?模塊(核心功能)
作用:管理用戶登錄狀態、令牌(Token)、權限和角色信息。
const state = {token: localStorage.getItem('token') || null, // 從本地存儲初始化Tokenuser: null, // 用戶信息permissions: [], // 權限列表role: null // 用戶角色
}
關鍵方法:
方法/屬性 | 功能說明 |
---|---|
mutations.SET_TOKEN | 存儲Token到Vuex和localStorage,并設置Axios全局請求頭(Authorization ) |
mutations.CLEAR_TOKEN | 退出登錄時清除Token和Axios請求頭 |
actions.login | 提交登錄請求,保存Token和權限到Vuex |
actions.logout | 清理所有用戶狀態(Token、用戶信息、權限等) |
actions.fetchPermissions | 主動獲取用戶權限(需Token有效) |
流程示例:
-
用戶登錄
-
權限控制
-
前端根據?
state.permissions
?動態渲染菜單/按鈕。 -
每次API請求自動攜帶JWT(通過Axios攔截器)。
-
2.?index.js
(Vuex主倉庫)
作用:全局狀態管理 + 模塊整合 + 持久化。
export default createStore({state: {userId: '', // 全局用戶IDisAuthenticated: false // 認證狀態},modules: {user // 注冊user模塊(嵌套狀態)},plugins: [ /* 持久化邏輯 */ ]
})
關鍵設計:
-
模塊化
-
將用戶相關狀態(
user
模塊)與全局狀態分離,避免命名沖突。 -
通過?
namespaced: true
?啟用模塊命名空間。
-
-
狀態持久化
-
初始化時:從?
localStorage
?讀取?userId
?恢復登錄狀態。 -
狀態變化時:通過?
store.subscribe
?監聽?setUserId
?突變,自動同步到?localStorage
。
-
-
認證狀態同步
-
isAuthenticated
?由?userId
?自動計算得出(!!id
?轉為布爾值)。
-