UserLoginTokenAspect.java 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. package com.poyee.aspect;
  2. import cn.hutool.core.util.StrUtil;import com.alibaba.fastjson.JSON;
  3. import com.alibaba.fastjson.JSONObject;
  4. import com.poyee.annotation.NoLogin;
  5. import com.poyee.annotation.UserLoginToken;
  6. import com.poyee.exception.BusinessException;
  7. import com.poyee.utils.JwtUtils;
  8. import com.poyee.utils.ServletUtils;
  9. import lombok.extern.slf4j.Slf4j;
  10. import org.aspectj.lang.JoinPoint;
  11. import org.aspectj.lang.Signature;
  12. import org.aspectj.lang.annotation.Aspect;
  13. import org.aspectj.lang.annotation.Before;
  14. import org.aspectj.lang.annotation.Pointcut;
  15. import org.aspectj.lang.reflect.MethodSignature;
  16. import org.springframework.stereotype.Component;
  17. import javax.servlet.http.HttpServletRequest;
  18. import java.lang.reflect.Method;
  19. import java.nio.charset.StandardCharsets;
  20. import java.util.Arrays;
  21. import java.util.Base64;
  22. /**
  23. * 用户登录Token切面
  24. */
  25. @Slf4j
  26. @Aspect
  27. @Component
  28. public class UserLoginTokenAspect {
  29. /**
  30. * 配置织入点 - 拦截所有 Controller 方法
  31. */
  32. @Pointcut("execution(public * com.poyee.controller.*.*(..))")
  33. public void controllerPointCut() {
  34. }
  35. /**
  36. * 前置通知
  37. */
  38. @Before("controllerPointCut()")
  39. public void doBefore(JoinPoint joinPoint) {
  40. HttpServletRequest request = ServletUtils.getRequest();
  41. if (request == null) {
  42. return;
  43. }
  44. // 获取方法上的注解
  45. Signature signature = joinPoint.getSignature();
  46. MethodSignature methodSignature = (MethodSignature) signature;
  47. Method method = methodSignature.getMethod();
  48. Class<?> declaringClass = method.getDeclaringClass();
  49. // 1. 检查是否有 @NoLogin 注解(方法级别优先)
  50. NoLogin noLogin = method.getAnnotation(NoLogin.class);
  51. if (noLogin == null) {
  52. noLogin = declaringClass.getAnnotation(NoLogin.class);
  53. }
  54. // 如果有 @NoLogin 注解,直接放行
  55. if (noLogin != null) {
  56. return;
  57. }
  58. // 2. 没有 @NoLogin 注解,一律进行 Token 检查
  59. String userInfoStr = request.getHeader("X-USER-BASE64");
  60. if (StrUtil.isBlank(userInfoStr)) {
  61. userInfoStr = request.getHeader("x-user-base64");
  62. }
  63. log.info("X-USER-BASE64 >>> {}", userInfoStr);
  64. String authorization = request.getHeader("Authorization");
  65. log.info("Authorization >>> {}", authorization);
  66. // 3. 检查是否有 @UserLoginToken 注解(用于角色控制)
  67. UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
  68. if (userLoginToken == null) {
  69. userLoginToken = declaringClass.getAnnotation(UserLoginToken.class);
  70. }
  71. // 执行登录检查
  72. checkUserLoginToken(request, userInfoStr, authorization, userLoginToken);
  73. }
  74. /**
  75. * 检查用户登录Token
  76. */
  77. private void checkUserLoginToken(HttpServletRequest request, String userInfoStr,
  78. String authorization, UserLoginToken userLoginToken) {
  79. boolean isAuthorization = false;
  80. String token = userInfoStr;
  81. // 1. 尝试从 X-USER-BASE64 获取
  82. if (StrUtil.isBlank(token)) {
  83. // 2. 尝试从 Authorization 获取
  84. if (StrUtil.isNotBlank(authorization)) {
  85. if (authorization.startsWith("Bearer ")) {
  86. isAuthorization = true;
  87. token = authorization.substring(7);
  88. } else {
  89. token = authorization;
  90. }
  91. }
  92. }
  93. if (StrUtil.isBlank(token)) {
  94. throw new BusinessException(401, "未登录或登录已过期");
  95. }
  96. // 保存原始token到session
  97. ServletUtils.getSession().setAttribute("x-user-base64", token);
  98. // 解析用户信息
  99. JSONObject userInfo = parseUserInfo(token, isAuthorization);
  100. if (userInfo == null) {
  101. throw new BusinessException(402, "登录信息无效,请重新登录");
  102. }
  103. // 保存用户信息到request
  104. request.setAttribute("currentUser", userInfo);
  105. ServletUtils.getSession().setAttribute("userInfo", JSON.toJSONString(userInfo));
  106. // 检查角色权限(如果有 @UserLoginToken 注解且配置了角色)
  107. if (userLoginToken != null && userLoginToken.roles().length > 0) {
  108. boolean hasRole = checkRole(userInfo, userLoginToken.roles());
  109. if (!hasRole) {
  110. throw new BusinessException(403, "无权访问");
  111. }
  112. }
  113. log.info("用户登录验证通过: {}", userInfo.getString("username"));
  114. }
  115. /**
  116. * 解析用户信息
  117. */
  118. private JSONObject parseUserInfo(String token, boolean isAuthorization) {
  119. try {
  120. if (isAuthorization) {
  121. // JWT Token 解析
  122. return JwtUtils.getTokenUserInfo(token);
  123. } else {
  124. // Base64 编码的用户信息
  125. String jsonStr = new String(Base64.getDecoder().decode(token), StandardCharsets.UTF_8);
  126. return JSON.parseObject(jsonStr);
  127. }
  128. } catch (Exception e) {
  129. log.error("解析用户信息失败", e);
  130. return null;
  131. }
  132. }
  133. /**
  134. * 检查用户角色
  135. */
  136. private boolean checkRole(JSONObject userInfo, String[] requiredRoles) {
  137. if (userInfo == null) {
  138. return false;
  139. }
  140. String role = userInfo.getString("role");
  141. if (role == null) {
  142. return false;
  143. }
  144. return Arrays.asList(requiredRoles).contains(role);
  145. }
  146. }