package com.yunhai.acaclassroom.aspect; import com.yunhai.acaclassroom.utils.UserContext; import com.yunhai.platform.auth.BoaUserContext; import com.yunhai.platform.utils.model.BoaUser; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; /** * 把基础平台的当前登录用户({@link BoaUser})复制到自有 {@link UserContext}(ThreadLocal),请求结束清理。 *
参照 UserclassService 的 ControllerUserContextAspect:优先从控制器入参里的 {@code BoaUser} 取, * 否则回退到框架 {@code BoaUserContext#get()}。本模块为纯教师端,无视角切换。
*/ @Aspect @Component @Order(1) public class ControllerUserContextAspect { private static final Logger log = LoggerFactory.getLogger(ControllerUserContextAspect.class); /** 本地联调开关(默认关闭):无 SSO 会话时用固定开发身份填充 UserContext,便于无登录端到端联调。生产严禁开启。 */ @Value("${acaclassroom.dev-context.enabled:false}") private boolean devContextEnabled; @Around("execution(* com.yunhai.acaclassroom.controller..*.*(..))") public Object around(ProceedingJoinPoint pjp) throws Throwable { try { BoaUser boaUser = resolveBoaUser(pjp.getArgs()); UserContext.UserInfo info = new UserContext.UserInfo(); if (boaUser != null) { info.userId = boaUser.getId(); info.userName = boaUser.getName(); info.type = boaUser.getType(); if (boaUser.getCurrentRole() != null) { info.schId = boaUser.getCurrentRole().getSchId(); } else if (boaUser.getSchool() != null) { info.schId = boaUser.getSchool().getId(); } if (boaUser.getRoleIds() != null && !boaUser.getRoleIds().isEmpty()) { info.roleId = boaUser.getRoleIds().get(0); } } else if (devContextEnabled) { info.userId = 99999; info.userName = "本地开发"; info.schId = 1; log.debug("[UserContext] 无 SSO 会话,已使用本地开发身份(schId=1)"); } info.ip = clientIp(); UserContext.set(info); return pjp.proceed(); } finally { UserContext.clear(); } } /** 控制器入参中若声明了 BoaUser(框架有参数解析器注入),优先用它;否则取 ThreadLocal 门面。 */ private BoaUser resolveBoaUser(Object[] args) { if (args != null) { for (Object a : args) { if (a instanceof BoaUser) { return (BoaUser) a; } } } try { return BoaUserContext.get(); } catch (Exception e) { log.debug("[UserContext] 读取 BoaUserContext 失败: {}", e.getMessage()); return null; } } private HttpServletRequest request() { Object attrs = RequestContextHolder.getRequestAttributes(); return attrs instanceof ServletRequestAttributes ? ((ServletRequestAttributes) attrs).getRequest() : null; } private String clientIp() { HttpServletRequest req = request(); if (req == null) { return null; } String ip = req.getHeader("X-Forwarded-For"); if (ip != null && !ip.isEmpty()) { int comma = ip.indexOf(','); return comma > 0 ? ip.substring(0, comma).trim() : ip.trim(); } ip = req.getHeader("X-Real-IP"); if (ip != null && !ip.isEmpty()) { return ip.trim(); } return req.getRemoteAddr(); } }