AuthAspect.java 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. package com.ruoyi.system.utils;
  2. import com.ruoyi.common.core.domain.AjaxResult;
  3. import com.ruoyi.common.core.redis.RedisCache;
  4. import com.ruoyi.common.exception.ServiceException;
  5. import com.ruoyi.common.utils.MessageUtils;
  6. import com.ruoyi.system.domain.InfoUser;
  7. import com.ruoyi.system.mapper.InfoUserMapper;
  8. import org.aspectj.lang.ProceedingJoinPoint;
  9. import org.aspectj.lang.annotation.Around;
  10. import org.aspectj.lang.annotation.Aspect;
  11. import org.aspectj.lang.annotation.Pointcut;
  12. import org.aspectj.lang.reflect.MethodSignature;
  13. import org.springframework.stereotype.Component;
  14. import jakarta.annotation.Resource;
  15. import jakarta.servlet.http.HttpServletRequest;
  16. import java.util.Map;
  17. /**
  18. * Created with IntelliJ IDEA.
  19. *
  20. * @Auther: ZouTiancong
  21. * @Date: 2022/05/23/22:39
  22. * @Description:
  23. */
  24. @Component
  25. @Aspect
  26. public class AuthAspect {
  27. private HttpServletRequest request;
  28. private final RedisCache redisCache;
  29. private final InfoUserMapper infoUserMapper;
  30. public AuthAspect(RedisCache redisCache, InfoUserMapper infoUserMapper) {
  31. this.redisCache = redisCache;
  32. this.infoUserMapper = infoUserMapper;
  33. }
  34. @Resource
  35. public void setHttpServletRequest(HttpServletRequest request) {
  36. this.request = request;
  37. }
  38. @Pointcut("@annotation(com.ruoyi.system.utils.Auth)")
  39. private void authPointcut() {
  40. }
  41. @Around("authPointcut()")
  42. public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
  43. // 获取方法签名信息从而获取方法名和参数类型
  44. MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
  45. // 获取目标方法对象上注解中的属性值
  46. Auth auth = methodSignature.getMethod().getAnnotation(Auth.class);
  47. // JWT 只解析和验签一次,后续会话校验及 Controller 均复用这里提取的可信身份。
  48. if (auth.token()) {
  49. Map<String, Object> claims = JwtUtil.verifyToken(request.getHeader("token"));
  50. if (!Boolean.TRUE.equals(claims.get("valid"))) {
  51. return AjaxResult.error(401,"token已过期,请重新登录!");
  52. }
  53. Long userId = parseUserId(claims.get("id"));
  54. Object jtiValue = claims.get("jti");
  55. String jti = jtiValue instanceof String ? (String) jtiValue : null;
  56. if (auth.session() && (userId == null || jti == null || jti.isBlank()
  57. || !Boolean.TRUE.equals(redisCache.hasKey(jti)))) {
  58. // Redis 会话校验只服务商家侧账号的即时撤销;普通用户和骑手没有会话撤销用途,
  59. // 旧 token(jti 从未写入 Redis)放行,凭已验签的 JWT 通过即可。
  60. if (!isConsumerUser(userId)) {
  61. throw new ServiceException(MessageUtils.message("merchant.session.invalid"));
  62. }
  63. }
  64. request.setAttribute(AuthContext.USER_ID_ATTRIBUTE, userId);
  65. request.setAttribute(AuthContext.JTI_ATTRIBUTE, jti);
  66. }
  67. return joinPoint.proceed();
  68. }
  69. /**
  70. * 会话缺失时判断是否为无需商家会话的账号类型:0=普通用户、2=骑手。
  71. * 商家侧类型(1/3/4/5)、用户不存在或类型未知时一律不放行。
  72. */
  73. private boolean isConsumerUser(Long userId) {
  74. if (userId == null) {
  75. return false;
  76. }
  77. InfoUser user = infoUserMapper.selectById(userId);
  78. String userType = user == null ? null : user.getUserType();
  79. return "0".equals(userType) || "2".equals(userType);
  80. }
  81. private Long parseUserId(Object claim) {
  82. if (!(claim instanceof String value) || value.isBlank() || "null".equals(value)) {
  83. return null;
  84. }
  85. try {
  86. return Long.valueOf(value);
  87. } catch (NumberFormatException exception) {
  88. return null;
  89. }
  90. }
  91. }