package com.ruoyi.system.utils; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.redis.RedisCache; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.MessageUtils; import com.ruoyi.system.domain.InfoUser; import com.ruoyi.system.mapper.InfoUserMapper; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletRequest; import java.util.Map; /** * Created with IntelliJ IDEA. * * @Auther: ZouTiancong * @Date: 2022/05/23/22:39 * @Description: */ @Component @Aspect public class AuthAspect { private HttpServletRequest request; private final RedisCache redisCache; private final InfoUserMapper infoUserMapper; public AuthAspect(RedisCache redisCache, InfoUserMapper infoUserMapper) { this.redisCache = redisCache; this.infoUserMapper = infoUserMapper; } @Resource public void setHttpServletRequest(HttpServletRequest request) { this.request = request; } @Pointcut("@annotation(com.ruoyi.system.utils.Auth)") private void authPointcut() { } @Around("authPointcut()") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { // 获取方法签名信息从而获取方法名和参数类型 MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); // 获取目标方法对象上注解中的属性值 Auth auth = methodSignature.getMethod().getAnnotation(Auth.class); // JWT 只解析和验签一次,后续会话校验及 Controller 均复用这里提取的可信身份。 if (auth.token()) { Map claims = JwtUtil.verifyToken(request.getHeader("token")); if (!Boolean.TRUE.equals(claims.get("valid"))) { return AjaxResult.error(401,"token已过期,请重新登录!"); } Long userId = parseUserId(claims.get("id")); Object jtiValue = claims.get("jti"); String jti = jtiValue instanceof String ? (String) jtiValue : null; if (auth.session() && (userId == null || jti == null || jti.isBlank() || !Boolean.TRUE.equals(redisCache.hasKey(jti)))) { // Redis 会话校验只服务商家侧账号的即时撤销;普通用户和骑手没有会话撤销用途, // 旧 token(jti 从未写入 Redis)放行,凭已验签的 JWT 通过即可。 if (!isConsumerUser(userId)) { throw new ServiceException(MessageUtils.message("merchant.session.invalid")); } } request.setAttribute(AuthContext.USER_ID_ATTRIBUTE, userId); request.setAttribute(AuthContext.JTI_ATTRIBUTE, jti); } return joinPoint.proceed(); } /** * 会话缺失时判断是否为无需商家会话的账号类型:0=普通用户、2=骑手。 * 商家侧类型(1/3/4/5)、用户不存在或类型未知时一律不放行。 */ private boolean isConsumerUser(Long userId) { if (userId == null) { return false; } InfoUser user = infoUserMapper.selectById(userId); String userType = user == null ? null : user.getUserType(); return "0".equals(userType) || "2".equals(userType); } private Long parseUserId(Object claim) { if (!(claim instanceof String value) || value.isBlank() || "null".equals(value)) { return null; } try { return Long.valueOf(value); } catch (NumberFormatException exception) { return null; } } }