fastapi+vue中的用戶權限管理設計

數據庫設計:RBAC數據模型

這是一個典型的基于SQLAlchemy的RBAC權限系統數據模型實現,各模型分工明確,共同構成完整的權限管理系統。

圖解說明:

  1. 實體關系

    • 用戶(USER)和角色(ROLE)通過?USER_ROLE?中間表實現多對多關系

    • 角色(ROLE)和權限(PERMISSION)通過?ROLE_PERMISSION?中間表實現多對多關系

  2. 關鍵連接點

    USER → USER_ROLE ← ROLE → ROLE_PERMISSION ← PERMISSION

    這個鏈條完整表達了"用戶→角色→權限"的授權路徑

  3. 箭頭含義

    • ||--o{?表示一對多關系

    • PK?表示主鍵

    • FK?表示外鍵

  4. 實際業務流
    當檢查用戶是否有某接口的訪問權限時,系統會:

    用戶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: 關聯到權限表

  • 業務意義:實現"角色包含哪些權限"的映射關系

整體協作流程

  1. 管理員創建權限(Permission)并定義其對應的資源

  2. 創建角色(Role)并將相關權限關聯到角色(RolePermission)

  3. 為用戶分配角色(UserRole)

  4. 系統通過檢查用戶的角色→權限→資源路徑鏈來確定訪問權限

這種設計實現了權限管理的解耦,使系統能夠靈活應對權限變更,同時通過中間表實現了多對多關系的維護。

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 流程 (授權碼模式)

  1. 用戶發起授權請求

    • 客戶端將用戶重定向到授權服務器

    • 攜帶參數:response_type=code,?client_id,?redirect_uri,?scope

  2. 用戶認證與同意

    • 用戶在授權服務器登錄并同意請求的權限

  3. 獲取授權碼

    • 授權服務器返回授權碼到客戶端的redirect_uri

  4. 用授權碼交換令牌

    • 客戶端向授權服務器的令牌端點發送請求

    • 攜帶:grant_type=authorization_code,?code,?redirect_uri,?client_id,?client_secret

  5. 返回JWT令牌

    • 授權服務器驗證后返回訪問令牌(通常是JWT)和刷新令牌

    • 響應示例:

      {"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...","token_type": "Bearer","expires_in": 3600,"refresh_token": "def50200ae2f..."
      }
  6. 使用訪問令牌訪問資源

    • 客戶端在請求頭中加入:Authorization: Bearer <access_token>

    • 資源服務器驗證JWT簽名和聲明

  7. 刷新令牌(可選)

    • 當訪問令牌過期時,使用刷新令牌獲取新的訪問令牌

JWT在OAuth中的優勢

  1. 自包含:JWT包含所有必要信息,減少數據庫查詢

  2. 可驗證性:資源服務器可以獨立驗證JWT而不需聯系授權服務器

  3. 標準化結構:易于跨語言和平臺實現

# 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 的權限檢查依賴工廠函數,主要用于實現基于權限的訪問控制。它的主要作用和功能如下:

  1. 功能概述

    • 它是一個裝飾器工廠函數,接收一個權限名稱作為參數,返回一個 FastAPI 依賴項

    • 用于檢查當前請求的用戶是否擁有指定的權限

  2. 工作流程

    • 從請求中獲取用戶 ID

    • 查詢數據庫驗證用戶是否存在

    • 檢查用戶是否是管理員角色(role_name 為 'admin'),如果是則直接通過檢查

    • 對于非管理員用戶,檢查用戶是否擁有指定的權限

    • 如果沒有權限則返回 403 錯誤

  3. 權限檢查邏輯

    • 通過連接查詢檢查用戶角色關聯的權限

    • 查詢涉及多個表:User → UserRole → RolePermission → Permission

    • 只有當用戶至少有一個角色擁有指定的權限時才會通過檢查

  4. 異常處理

    • 未認證用戶(無 user_id):返回 401

    • 用戶不存在:返回 401

    • 權限不足:返回 403

什么是中間件?

在 Web 開發中,中間件(Middleware)?是一種機制,用于在?HTTP 請求到達路由處理函數之前?或?響應返回客戶端之前?執行某些邏輯。它類似于一個“攔截器”,可以對請求和響應進行預處理或后處理。


中間件的作用

  1. 全局處理請求/響應

    • 例如:身份驗證、日志記錄、跨域處理(CORS)、請求限流、數據壓縮等。

  2. 修改請求或響應

    • 例如:添加請求頭、解析請求體、修改響應數據。

  3. 攔截非法請求

    • 例如:檢查權限(如你的?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 路由處理函數,用于實現用戶登錄認證流程。它的核心作用是:


主要功能

  1. 用戶身份認證

    • 接收前端提交的用戶名和密碼(通過?LoginRequest?模型)。

    • 驗證用戶名是否存在、密碼是否正確。

    • 返回 401 錯誤碼和提示信息(如 "user not found" 或 "password error")。

  2. 權限和角色信息收集

    • 查詢用戶的角色(如?user_roles)并檢查是否是管理員(is_admin)。

    • 查詢用戶擁有的權限列表(permissions)。

  3. 生成 JWT 令牌

    • 使用?create_access_token?生成包含用戶信息的 JWT(有效期通過?ACCESS_TOKEN_EXPIRE_MINUTES?控制)。

    • 令牌中存儲的關鍵數據:

      {"sub": "用戶ID",          # 用戶唯一標識"username": "用戶名","permissions": ["權限1", "權限2"],  # 用戶權限列表"is_admin": True/False,   # 是否是管理員"roles": ["角色1", "角色2"]       # 用戶角色列表
      }
  4. 返回登錄結果

    • 返回 200 狀態碼和用戶信息(包括令牌、權限、角色等),供前端后續使用。


詳細流程

  1. 參數接收

    • 通過?LoginRequest?模型(未展示但應包含?username?和?password?字段)接收前端提交的登錄數據。

  2. 用戶驗證

    • 調用?AuthService.select_user_info?查詢數據庫,檢查用戶名是否存在。

    • 比對數據庫中的密碼(明文比對,實際項目中應使用?哈希密碼比對,如?bcrypt)。

  3. 權限角色查詢

    • 通過?AuthService.get_user_roles?獲取用戶角色列表,并檢查是否包含?admin?角色。

    • 通過?AuthService.get_user_permissions?獲取用戶權限列表(用于前端動態路由或按鈕權限控制)。

  4. 令牌生成

    • 使用?create_access_token(可能是?fastapi-jwt?或類似庫)生成 JWT,包含用戶關鍵信息。

  5. 響應返回

    • 返回結構化響應,包含令牌和用戶信息,例如:

      {"code": 200,"message": "success","data": {"user_id": 123,"username": "john","access_token": "xxx.yyy.zzz","permissions": ["read", "write"],"roles": ["user"],"is_admin": false}
      }

關鍵點說明

  1. 安全注意事項

    • 密碼明文存儲問題:當前代碼直接比對明文密碼,實際項目應使用?密碼哈希(如?bcrypt)存儲和驗證。

    • JWT 敏感信息:令牌中存儲了?permissions?和?roles,需確保 JWT 使用 HTTPS 傳輸并設置合理有效期。

  2. 權限控制設計

    • 前端可根據返回的?permissions?和?roles?動態渲染界面。

    • 后端可通過?permission_required?中間件(如你之前提供的)進一步校驗權限。

  3. 擴展性

    • 可添加登錄日志記錄、多設備登錄限制、驗證碼等功能。


典型使用場景

  1. 前端提交登錄表單 → 后端驗證 → 返回令牌。

  2. 前端將令牌存儲在?localStorage?或?Cookie?中,后續請求通過?Authorization: Bearer <token>?頭部攜帶。

  3. 其他受保護的路由通過中間件校驗令牌和權限(如?@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_idpermissions?等)。

  • 設置令牌過期時間(默認 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_idpermissionsis_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_middlewareget_current_user
作用范圍全局(所有請求)單個路由(需顯式聲明?Depends
認證嚴格性靜默失敗(僅注入數據)嚴格失敗(401 錯誤)
典型用途日志、基礎用戶信息注入需要強認證的路由

協作流程

  1. 用戶登錄

    • 調用?create_access_token?生成令牌并返回給前端。

  2. 前端請求

    • 在請求頭中添加?Authorization: Bearer <token>

  3. 請求處理

    • auth_middleware?攔截請求,解析令牌并注入用戶信息到?request.state

    • 路由層可通過?get_current_user?進一步嚴格校驗(如需要權限的接口)。


安全注意事項

  1. 密鑰管理

    • SECRET_KEY?必須保密,且不要硬編碼在代碼中(從環境變量讀取)。

  2. 令牌有效期

    • 生產環境中?ACCESS_TOKEN_EXPIRE_MINUTES?不宜過長(通常 15-60 分鐘)。

  3. 算法選擇

    • 推薦使用?HS256(對稱加密)或?RS256(非對稱加密)。

  4. 中間件靜默失敗

    • 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. 執行流程

  1. 請求到達路由
    當客戶端訪問?GET /api/abilities?時,FastAPI 會先執行?permission_required("abilities_read")?的校驗邏輯。

  2. 權限校驗

    • 如果用戶?有權限:繼續執行?get_abilities?函數體。

    • 如果用戶?無權限:直接返回?403 Forbidden?錯誤(由?permission_required?拋出)。

  3. 處理業務邏輯
    只有通過權限校驗后,才會調用?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有效)
流程示例
  1. 用戶登錄

  1. 權限控制

    • 前端根據?state.permissions?動態渲染菜單/按鈕。

    • 每次API請求自動攜帶JWT(通過Axios攔截器)。


2.?index.js(Vuex主倉庫)

作用:全局狀態管理 + 模塊整合 + 持久化。
export default createStore({state: {userId: '',             // 全局用戶IDisAuthenticated: false  // 認證狀態},modules: {user  // 注冊user模塊(嵌套狀態)},plugins: [ /* 持久化邏輯 */ ]
})
關鍵設計
  1. 模塊化

    • 將用戶相關狀態(user模塊)與全局狀態分離,避免命名沖突。

    • 通過?namespaced: true?啟用模塊命名空間。

  2. 狀態持久化

    • 初始化時:從?localStorage?讀取?userId?恢復登錄狀態。

    • 狀態變化時:通過?store.subscribe?監聽?setUserId?突變,自動同步到?localStorage

  3. 認證狀態同步

    • isAuthenticated?由?userId?自動計算得出(!!id?轉為布爾值)。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/79327.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/79327.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/79327.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【Python實戰】飛機大戰

開發一個飛機大戰游戲是Python學習的經典實戰項目&#xff0c;尤其適合結合面向對象編程和游戲框架&#xff08;如Pygame&#xff09;進行實踐。以下是游戲設計的核心考慮因素和模塊劃分建議&#xff1a; 一、游戲設計核心考慮因素 性能優化 Python游戲需注意幀率控制&#xff…

Flowable7.x學習筆記(十八)拾取我的待辦

前言 本文從解讀源碼到實現功能&#xff0c;完整的學習Flowable的【TaskService】-【claim】方法實現的任務拾取功能。 一、概述 當調用 TaskService.claim(taskId, userId) 時&#xff0c;Flowable 會先加載并校驗任務實體&#xff0c;再判斷該任務是否已被認領&#xff1b;若…

SQL經典實例

第1章 檢索記錄 1.1 檢索所有行和列 知識點&#xff1a;使用SELECT *快速檢索表中所有列&#xff1b;顯式列出列名&#xff08;如SELECT col1, col2&#xff09;提高可讀性和可控性&#xff0c;尤其在編程場景中更清晰。 1.2 篩選行 知識點&#xff1a;通過WHERE子句過濾符合條…

HTTPcookie與session實現

1.HTTP Cookie 定義 HTTP Cookie &#xff08;也稱為 Web Cookie 、瀏覽器 Cookie 或簡稱 Cookie &#xff09;是服務器發送到 用戶瀏覽器并保存在瀏覽器上的一小塊數據&#xff0c;它會在瀏覽器之后向同一服務器再次發 起請求時被攜帶并發送到服務器上。通常&#xff0…

【算法基礎】冒泡排序算法 - JAVA

一、算法基礎 1.1 什么是冒泡排序 冒泡排序是一種簡單直觀的比較排序算法。它重復地走訪待排序的數列&#xff0c;依次比較相鄰兩個元素&#xff0c;如果順序錯誤就交換它們&#xff0c;直到沒有元素需要交換為止。 1.2 基本思想 比較相鄰元素&#xff1a;從頭開始&#xf…

0902Redux_狀態管理-react-仿低代碼平臺項目

文章目錄 1 Redux 概述1.1 核心概念1.2 基本組成1.3 工作流程1.4 中間件&#xff08;Middleware&#xff09;1.5 適用場景1.6 優缺點1.7 Redux Toolkit&#xff08;現代推薦&#xff09;1.8 與其他工具的對比1.9 總結 2 todoList 待辦事項案例3 Redux開發者工具3.1 核心功能3.2…

《ATPL地面培訓教材13:飛行原理》——第6章:阻力

翻譯&#xff1a;Leweslyh&#xff1b;工具&#xff1a;Cursor & Claude 3.7&#xff1b;過程稿 第6章&#xff1a;阻力 目錄 引言寄生阻力誘導阻力減少誘導阻力的方法升力對寄生阻力的影響飛機總阻力飛機總重量對總阻力的影響高度對總阻力的影響構型對總阻力的影響速度穩…

C++總結01-類型相關

一、數據存儲 1.程序數據段 ? 靜態&#xff08;全局&#xff09;數據區&#xff1a;全局變量、靜態變量 ? 堆內存&#xff1a;程序員手動分配、手動釋放 ? 棧內存&#xff1a;編譯器自動分配、自動釋放 ? 常量區&#xff1a;編譯時大小、值確定不可修改 2.程序代碼段 ?…

【Hot 100】94. 二叉樹的中序遍歷

目錄 引言二叉樹的中序遍歷我的解題代碼優化更清晰的表述建議&#xff1a; &#x1f64b;?♂? 作者&#xff1a;海碼007&#x1f4dc; 專欄&#xff1a;算法專欄&#x1f4a5; 標題&#xff1a;【Hot 100】94. 二叉樹的中序遍歷?? 寄語&#xff1a;書到用時方恨少&#xff…

大語言模型(LLMs)微調技術總結

文章目錄 全面總結當前大語言模型&#xff08;LLM&#xff09;微調技術1. 引言2. 為什么需要微調&#xff1f;3. 微調技術分類概覽4. 各種微調技術詳細介紹4.1 基礎微調方法4.1.1 有監督微調&#xff08;Supervised Fine-Tuning, SFT&#xff09;4.1.2 全參數微調&#xff08;F…

解決Maven項目中報錯“java不支持版本6即更高的版本 7”

錯誤背景 當Maven項目編譯或運行時出現錯誤提示 Java不支持版本6即更高的版本7&#xff0c;通常是由于項目配置的JDK版本與當前環境或編譯器設置不一致導致的。例如&#xff1a; 項目配置的Java版本為6或7&#xff0c;但實際使用的是JDK 17。Maven或IDE的編譯器未正確指定目標…

C++筆記-多態(包含虛函數,純虛函數和虛函數表等)

1.多態的概念 多態(polymorphism)的概念:通俗來說&#xff0c;就是多種形態。多態分為編譯時多態(靜態多態)和運行時多態(動態多態)&#xff0c;這里我們重點講運行時多態&#xff0c;編譯時多態(靜態多態)和運行時多態(動態多態)。編譯時多態(靜態多態)主要就是我們前面講的函…

【Unity】MVP框架的使用例子

在提到MVP之前&#xff0c;可以先看看這篇MVC的帖子&#xff1a; 【Unity】MVC的簡單分享以及一個在UI中使用的例子 MVC的不足之處&#xff1a; 在MVC的使用中&#xff0c;會發現View層直接調用了Model層的引用&#xff0c;即這兩個層之間存在著一定的耦合性&#xff0c;而MV…

前端js學算法-實踐

1、兩數之和 const twoSum (nums, target) > {const obj {}for (let m 0; m < nums.length; m) {const cur nums[m]const diff target - curif(obj.hasOwnProperty(diff)){ // 查詢對象中是否存在目標值-當前值鍵值對console.log([obj[diff], m]) // 存在則直接獲取…

《MATLAB實戰訓練營:從入門到工業級應用》趣味入門篇-用聲音合成玩音樂:MATLAB電子琴制作(超級趣味實踐版)

《MATLAB實戰訓練營&#xff1a;從入門到工業級應用》趣味入門篇-用聲音合成玩音樂&#xff1a;MATLAB電子琴制作&#xff08;超級趣味實踐版&#xff09; 開篇&#xff1a;當MATLAB遇見音樂 - 一場數字與藝術的浪漫邂逅 想象一下&#xff0c;你正坐在一臺古老的鋼琴前&#x…

實戰探討:為什么 Redis Zset 選擇跳表?

在了解了跳表的原理和實現后&#xff0c;一個常見的問題&#xff08;尤其是在面試中&#xff09;隨之而來&#xff1a;為什么像 Redis 的有序集合 (Zset) 這樣的高性能組件會選擇使用跳表&#xff0c;而不是大家熟知的平衡樹&#xff08;如紅黑樹&#xff09;呢&#xff1f; 對…

數據結構-線性結構(鏈表、棧、隊列)實現

公共頭文件common.h #define TRUE 1 #define FALSE 0// 定義節點數據類型 #define DATA_TYPE int單鏈表C語言實現 SingleList.h #pragma once#include "common.h"typedef struct Node {DATA_TYPE data;struct Node *next; } Node;Node *initList();void headInser…

高中數學聯賽模擬試題精選學數學系列第3套幾何題

△ A B C \triangle ABC △ABC 的內切圓 ⊙ I \odot I ⊙I 分別與邊 B C BC BC, C A CA CA, A B AB AB 相切于點 D D D, E E E, F F F, D D ′ DD DD′ 為 ⊙ I \odot I ⊙I 的直徑, 過圓心 I I I 作直線 A D ′ AD AD′ 的垂線 l l l, 直線 l l l 分別與 D E DE…

使用 ossutil 上傳文件到阿里云 OSS

在處理文件存儲和傳輸時&#xff0c;阿里云的對象存儲服務&#xff08;OSS&#xff09;是一個非常方便的選擇。特別是在需要批量上傳文件或通過命令行工具進行文件管理時&#xff0c;ossutil提供了強大的功能。本文將詳細說明如何使用 ossutil 上傳文件到阿里云 OSS&#xff0c…

DeepSeek與MySQL:開啟數據智能新時代

目錄 一、引言&#xff1a;技術融合的力量二、DeepSeek 與 MySQL&#xff1a;技術基石2.1 DeepSeek 技術探秘2.2 MySQL 數據庫深度解析 三、DeepSeek 與 MySQL 集成&#xff1a;從理論到實踐3.1 集成原理剖析3.2 集成步驟詳解 四、應用案例&#xff1a;實戰中的價值體現4.1 電商…