如何实现SvelteKit受保护的shadow endpoints?避免冗余代码且保障安全
核心思路:用中间件/拦截器统一处理校验
把token验证逻辑抽成独立的中间件(或拦截器),指定需要保护的shadow endpoints必须经过这个中间件校验。验证通过才放行到业务逻辑,失败直接返回未授权响应——这样不用每个接口都重复写校验代码,完全解耦权限逻辑和业务逻辑。
不同技术栈的实现示例
1. Node.js + Express
先写通用的token校验中间件:
const jwt = require('jsonwebtoken'); const validateAccessToken = (req, res, next) => { // 从Cookie取token const accessToken = req.cookies.access_token; if (!accessToken) { return res.status(401).json({ error: '未提供访问令牌' }); } try { // 校验token签名、过期时间等核心属性 const decoded = jwt.verify(accessToken, process.env.JWT_SECRET); // 把解析后的用户信息挂载到req,后续接口直接用 req.user = decoded; next(); // 验证通过,放行到业务接口 } catch (err) { return res.status(401).json({ error: '令牌无效或已过期' }); } };
给shadow endpoints批量绑定中间件(用路由分组更高效):
const express = require('express'); const app = express(); const shadowRouter = express.Router(); // 给整个shadow路由组绑定校验中间件 shadowRouter.use(validateAccessToken); // 具体的shadow接口,不用再写校验代码 shadowRouter.get('/data', (req, res) => { res.send(`仅授权用户${req.user.username}可见内容`); }); shadowRouter.post('/update', (req, res) => { res.send('更新操作已执行'); }); // 挂载路由组 app.use('/shadow', shadowRouter); // 公开接口无需校验 app.get('/public', (req, res) => res.send('公开内容'));
2. Python + FastAPI
用FastAPI的Depends实现依赖注入式校验,本质也是统一逻辑:
from fastapi import FastAPI, Depends, HTTPException, status from jose import JWTError, jwt from fastapi.requests import Request app = FastAPI() # 从Cookie提取token的依赖函数 def get_token_from_cookie(request: Request): token = request.cookies.get("access_token") if not token: raise HTTPException(status_code=401, detail="未找到访问令牌") return token # 通用的token校验依赖 async def validate_token(token: str = Depends(get_token_from_cookie)): try: # 校验签名、过期时间、受众等 payload = jwt.decode(token, "your-secret-key", algorithms=["HS256"]) username = payload.get("sub") if not username: raise HTTPException(status_code=401, detail="令牌无效") return {"username": username} except JWTError: raise HTTPException(status_code=401, detail="令牌无效或已过期") # shadow接口自动触发校验 @app.get("/shadow/data") async def get_shadow_data(current_user: dict = Depends(validate_token)): return {"content": f"仅{current_user['username']}可访问"} # 公开接口 @app.get("/public") async def public(): return {"content": "任何人都能看"}
3. Java Spring Boot
用拦截器批量拦截shadow路径:
import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; @Component public class AccessTokenInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 从Cookie获取token String accessToken = null; Cookie[] cookies = request.getCookies(); if (cookies != null) { for (Cookie cookie : cookies) { if ("access_token".equals(cookie.getName())) { accessToken = cookie.getValue(); break; } } } if (accessToken == null) { response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "未提供访问令牌"); return false; } // 校验token的签名、过期时间等 if (!validateToken(accessToken)) { response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "令牌无效或已过期"); return false; } // 把用户信息存入request,后续接口可直接获取 request.setAttribute("currentUser", parseUserFromToken(accessToken)); return true; } // 实际项目中替换为真实的token校验逻辑 private boolean validateToken(String token) { // 校验签名、exp、iss等字段 return true; } private Object parseUserFromToken(String token) { // 解析token中的用户信息 return new Object(); } }
注册拦截器,指定拦截shadow路径:
import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class WebConfig implements WebMvcConfigurer { private final AccessTokenInterceptor accessTokenInterceptor; public WebConfig(AccessTokenInterceptor accessTokenInterceptor) { this.accessTokenInterceptor = accessTokenInterceptor; } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(accessTokenInterceptor) .addPathPatterns("/shadow/**") // 拦截所有shadow开头的接口 .excludePathPatterns("/public/**"); // 排除公开接口 </think_never_used_51bce0c785ca2f68081bfa7d91973934>
相关产品推荐
相关产品推荐

