Spring Controller全局添加请求属性:单方法实现登录用户名全局显示?
嘿,这个需求在Spring生态里完全可以轻松实现,而且有几种成熟的方案,我给你详细拆解下:
方案1:使用@ControllerAdvice + @ModelAttribute(最推荐)
这是Spring官方主推的全局Model属性注入方式,完美适配你这种“所有请求都要加属性”的场景。
你只需要创建一个全局控制器增强类,里面定义一个带@ModelAttribute的方法——这个方法会在所有Controller的请求处理逻辑执行前自动调用,把指定属性塞进Model里:
import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; @ControllerAdvice public class GlobalUserAttributeAdvice { @ModelAttribute("currentUsername") public String injectCurrentUsername() { // 从Spring Security上下文拿当前登录用户(如果用了Spring Security的话) Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth != null && auth.isAuthenticated() && !"anonymousUser".equals(auth.getPrincipal())) { // 这里默认Principal是UserDetails,你可以根据自己的登录逻辑调整 return auth.getName(); } return "Guest"; // 未登录时的默认值,避免空指针 } }
关键说明:
@ControllerAdvice:让这个类成为全局控制器增强器,作用于所有带@RequestMapping的方法。@ModelAttribute("currentUsername"):指定属性名,之后在模板里直接用${currentUsername}就能访问到用户名。- 如果没用Spring Security,把获取用户的逻辑换成你自己的实现就行(比如从HttpSession里取自定义的登录用户对象)。
方案2:使用HandlerInterceptor拦截器(更灵活)
如果你需要更精细的控制(比如只给特定路径的请求加属性,或者在请求处理的不同阶段做操作),拦截器是更好的选择:
首先定义拦截器类:
import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.ModelAndView; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; public class UsernameInjectInterceptor implements HandlerInterceptor { @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // 跳过没有ModelAndView的请求(比如异步接口) if (modelAndView != null) { String username = getCurrentUser(); modelAndView.addObject("currentUsername", username); } } private String getCurrentUser() { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); return auth != null && auth.isAuthenticated() ? auth.getName() : "Guest"; } }
然后把拦截器注册到Spring配置里:
import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new UsernameInjectInterceptor()) .addPathPatterns("/**") // 拦截所有请求 .excludePathPatterns("/login", "/static/**"); // 排除登录接口、静态资源 } }
这个方案的优势是可以精准控制拦截范围,还能在请求处理的不同阶段(preHandle/postHandle/afterCompletion)做额外操作,适合复杂场景。
额外小技巧:模板引擎直接访问(比如Thymeleaf)
如果你用的是Thymeleaf模板,其实不用手动加Model属性,直接用Spring Security的表达式就能在模板里获取用户名:
<span th:text="${#authentication.name}"></span>
不过这种方式只限于模板层面,如果需要在Controller或其他业务逻辑里也用到用户名,还是推荐前两种方案。
最后注意点:
- 确保用户信息获取逻辑是线程安全的:Spring Security的
SecurityContextHolder默认用ThreadLocal存储,天生线程安全;如果是自定义Session存储,也要保证线程安全。 - 一定要处理未登录的情况:给默认值避免空指针异常。
内容的提问来源于stack exchange,提问作者Vadym Borys
相关产品推荐
相关产品推荐

