如何基于用户权限筛选对应域名的别名数据?前后端实现咨询
嘿,我来帮你梳理下这个权限控制的问题,还有你的代码是否合理,以及最优的实现方案~
问题分析与最优实现建议
核心需求很明确:用户只能访问与自身DOMAIN匹配的别名数据,所以我们要兼顾功能实现、代码复用性,更重要的是权限安全性。
你的现有代码思路是否合理?
先看你写的这段组件代码:
private getToken(): string { if (!this.token) { this.token = localStorage.getItem('usertoken'); } return this.token; } getAliasesByDomain(){ const token = this.getToken(); let user; if (token) { user = token.split('.')[1]; user = window.atob(user); user = JSON.parse(user); console.log('user z from getAliasesByDomain: '+user.domain); this.aliasesService.getAliasesByDomain(user.domain).subscribe( res => { console.log(res); this.alias = res; }, err => console.error(err) ); } }
这个思路功能上是可行的,但有两个可以优化的点:
- 你已经在认证服务里实现了
getUserDetails()方法,没必要在组件里重复写解析token的逻辑,直接复用更符合代码复用原则。 - 前端解析域名后传给API的方式存在安全隐患:用户可以通过浏览器调试工具篡改请求的
domain参数,从而访问其他域名的别名数据,这会导致权限漏洞。
最优实现方案:后端权限校验+前端逻辑复用
1. 后端层面(关键!必须做)
前端传参的方式不安全,所以后端必须从JWT Token中解析用户信息,自动提取DOMAIN并过滤别名数据,完全不依赖前端传的参数。这样即使前端篡改请求,后端也能确保返回的数据是用户有权限访问的。
修改你的控制器和添加JWT验证中间件:
import jwt from 'jsonwebtoken'; import { NextFunction } from 'express'; // JWT验证中间件:解析token并挂载用户信息到请求对象 const authenticateToken = (req: Request, res: Response, next: NextFunction) => { const authHeader = req.headers['authorization']; const token = authHeader && authHeader.split(' ')[1]; // 提取Bearer后的token if (!token) { return res.status(401).json({ message: '未授权,请先登录' }); } jwt.verify(token, process.env.SECRET_KEY as string, (err, user) => { if (err) { return res.status(403).json({ message: '无效的token' }); } req.user = user as any; // 把解析后的用户信息挂载到req上 next(); }); }; // 修改后的别名列表接口:不再依赖req.params.domain,从token中取用户的domain public async aliasesListByDomain(req: Request, res: Response): Promise<void> { const userDomain = req.user.DOMAIN; // 注意字段名要和JWT payload里的一致 const aliasesListByDomain = await pool.query('SELECT * FROM virtual_aliases WHERE domain= ?', [userDomain]); if (aliasesListByDomain.length > 0) { res.json(aliasesListByDomain); } else { res.status(404).json({ message: "该域名下无别名数据" }); } } // 路由添加中间件保护 router.get('/aliases', authenticateToken, aliasesController.aliasesListByDomain);
这样做的好处:
- 彻底杜绝前端篡改参数的权限漏洞,权限控制完全由后端保障。
- 前端请求时不需要传
domain参数,简化了前端逻辑。
2. 前端层面(优化复用与逻辑清晰)
既然后端已经处理了权限过滤,前端只需要请求通用的别名列表接口,同时确保请求带上JWT Token即可。
首先修改别名服务:
// 不需要传domain参数,直接请求通用接口 getAliases(): Observable<Alias[]> { return this.http.get<Alias[]>(`${this.API_URI}/aliases`); }
然后在组件里复用认证服务的getUserDetails()方法,调用修改后的服务:
getAliases() { const userDetails = this.authService.getUserDetails(); if (userDetails) { // 这里可以做前端展示逻辑,比如显示当前用户的域名 console.log('当前用户域名:', userDetails.DOMAIN); this.aliasesService.getAliases().subscribe( res => { this.alias = res; }, err => { console.error('获取别名失败:', err); // 处理未授权错误,比如跳转到登录页 if (err.status === 401 || err.status === 403) { this.authService.logout(); this.router.navigate(['/login']); } } ); } else { // 用户未登录,跳转到登录页 this.router.navigate(['/login']); } }
另外,建议添加一个HTTP拦截器,统一给所有需要认证的请求添加Authorization头,不用在每个服务里手动设置:
import { Injectable } from '@angular/core'; import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http'; import { Observable } from 'rxjs'; import { AuthService } from './auth.service'; @Injectable() export class AuthInterceptor implements HttpInterceptor { constructor(private authService: AuthService) {} intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> { const token = this.authService.getToken(); if (token) { request = request.clone({ setHeaders: { Authorization: `Bearer ${token}` } }); } return next.handle(request); } }
然后在app.module.ts里注册这个拦截器:
import { HTTP_INTERCEPTORS } from '@angular/common/http'; import { AuthInterceptor } from './auth.interceptor'; @NgModule({ providers: [ { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true } ] }) export class AppModule { }
总结
- 你原来的组件代码思路是对的,但存在安全隐患且重复了token解析逻辑,建议复用现有方法。
- 最优方案是后端从JWT中提取用户域名并过滤数据,前端只需要确保请求带上Token即可,既安全又简化了前端逻辑。
- 添加HTTP拦截器可以统一处理认证头,减少重复代码。
内容的提问来源于stack exchange,提问作者Defus
相关产品推荐
相关产品推荐

