VIP权限系统划分简单实现
概述
本系统通过拦截器和切面机制实现了VIP权限控制,只有VIP用户才能访问被特定注解标记的API接口。主要通过Spring拦截器、自定义注解,AOP和服务层检验实现。
运行原理:在 controller 上面➕VIP 注解那么就走拦截器,就说明这一整个功能都需要此 vip权限,如果是这个一个接口下面的具体的方法需要 vip 权限,那么就会走 aop 切面的这个类
核心组件
1. VIP权限拦截器 (VipInterceptor
)
拦截器实现了Spring的HandlerInterceptor
接口,主要负责在请求到达控制器方法前进行VIP权限校验。
package com.yixueji.interceptor;
import com.yixueji.annotation.VipRequired;
import com.yixueji.context.BaseContext;
import com.yixueji.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class VipInterceptor implements HandlerInterceptor {
@Autowired
private UserService userService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 检查当前请求是否需要VIP权限
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
// 判断方法上是否有VipRequired注解
if (handlerMethod.hasMethodAnnotation(VipRequired.class)) {
Long userId = getCurrentUserId();
// 检查当前用户是否为VIP
if (!userService.isVip(userId)) {
response.sendError(HttpStatus.FORBIDDEN.value(), "VIP功能,请升级会员");
return false;
}
}
}
return true;
}
/**
* 获取当前登录用户的ID
* 从BaseContext中获取当前线程存储的用户ID
* @return 当前用户ID
*/
private Long getCurrentUserId() {
return BaseContext.getCurrentId();
}
}
2. VIP注解 (@VipRequired
)
自定义注解,用于标记需要VIP权限的控制器方法。
package com.yixueji.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface VipRequired {
}
3. 切面 (AOP)
用于拦截标注了@VipRequired注解的Service方法
package com.yixueji.aspect;
import com.yixueji.constant.MessageConstant;
import com.yixueji.context.BaseContext;
import com.yixueji.exception.VipRequiredException;
import com.yixueji.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* VIP功能校验切面
* 用于拦截标注了@VipRequired注解的Service方法
*/
@Aspect
@Component
@Slf4j
public class VipRequiredAspect {
@Autowired
private UserService userService;
/**
* 定义切点:拦截service包下所有标注了@VipRequired注解的方法
*/
@Pointcut("execution(* com.yixueji.service..*.*(..))" +
"&& @annotation(com.yixueji.annotation.VipRequired)")
public void vipRequiredPointcut() {}
/**
* 前置通知,在方法执行前进行VIP校验
*
* @param joinPoint 切点
*/
@Before("vipRequiredPointcut()")
public void beforeVipCheck(JoinPoint joinPoint) {
log.info("AOP拦截到需要VIP权限的方法: {}", joinPoint.getSignature().getName());
// 获取当前登录用户ID
Long userId = BaseContext.getCurrentId();
// 检查当前用户是否为VIP
if (!userService.isVip(userId)) {
// 如果不是VIP用户,抛出自定义异常
throw new VipRequiredException(MessageConstant.VIP_REQUIRED);
}
log.info("VIP校验通过,用户ID: {}", userId);
}
}
4. 用户服务 (UserService
)
提供isVip
方法用于检查用户是否具有VIP权限。
工作流程
- 请求进入系统后,首先经过拦截器处理
- 拦截器检查控制器方法是否标记了
@VipRequired
注解 - 如有标记,从
BaseContext
获取当前用户ID - 调用
userService.isVip(userId)
判断用户VIP状态 - 如非VIP用户,返回403禁止访问错误
- 如是VIP用户,允许请求继续
使用方法
配置拦截器:在Spring配置中注册
VipInterceptor
标记VIP方法:在需要VIP权限的控制器方法上添加
@VipRequired
注解,也可以加在 service 层中具体的方法中@GetMapping("/premium-content") @VipRequired public ResponseEntity<?> getPremiumContent() { // 只有VIP用户可访问的内容 }
用户身份传递:确保在处理请求前,通过
BaseContext
设置当前用户ID
技术优势
- 低侵入性:通过注解方式标记,不影响业务逻辑
- 可扩展性:可轻松扩展更多权限检查逻辑
- 集中管理:权限检查逻辑集中在拦截器中,便于维护
- 线程安全:使用ThreadLocal存储用户信息,确保线程安全
注意事项
- 确保
BaseContext
中的用户ID在请求开始时正确设置 - VIP状态检查逻辑可根据业务需求在
UserService
中扩展
使用拦截器和切面结合的原因
系统实现了两种不同层面的VIP权限控制机制,分别是拦截器(Interceptor)和切面(Aspect),这算是一种"防御纵深"的安全设计策略:
拦截器(VipInterceptor):
- 作用于Web层,拦截HTTP请求
- 在控制器方法执行前进行权限检查
- 直接返回HTTP 403错误响应给客户端
- 适用于标记了@VipRequired注解的Controller方法
- 与前端交互直接相关,负责拦截外部请求
切面(VipRequiredAspect):
- 作用于服务层,拦截内部方法调用
- 专门针对service包下的方法
- 通过抛出VipRequiredException异常来拒绝访问
- 保护核心业务逻辑,不依赖控制器层的拦截
- 可以捕获内部系统中的越权调用
采用双重实现的主要原因:
- 多层防御:即使绕过了拦截器,服务层仍有权限检查
- 不同应用场景:有些VIP功能可能没有对应的API但在内部被调用
- 解耦安全与业务:将权限控制和业务逻辑分离
- 更精细的控制:不同层次可以有不同的权限控制策略
- 系统健壮性:一种机制失效时,另一种仍能保护系统
这种双重实现确保了VIP权限检查的可靠性和全面性
评论区