Apache Shiro+Spring WebMvc:如何在控制器中最优获取当前登录用户?
在Spring WebMvc中用Apache Shiro获取已登录用户信息的最佳方式
1. 直接调用Shiro的SecurityUtils工具类
这是最直接的实现方式,在控制器方法里直接获取Subject对象,进而拿到用户信息:
import org.apache.shiro.SecurityUtils; import org.apache.shiro.subject.Subject; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class UserController { @GetMapping("/current-user") public Object getCurrentUser() { Subject currentUser = SecurityUtils.getSubject(); // 获取登录用户名(若登录时以用户名作为Principal) String username = (String) currentUser.getPrincipal(); // 若用户信息是自定义实体类,直接强转即可 // CustomUser user = (CustomUser) currentUser.getPrincipal(); return username; } }
- 优点:无需额外配置,上手快,适合简单场景。
- 注意:未登录时
currentUser.getPrincipal()会返回null,需自行判断登录状态。
2. 用@Autowired注入Subject(推荐)
通过Spring依赖注入获取Subject,比直接调用SecurityUtils更利于单元测试:
import org.apache.shiro.subject.Subject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class UserController { private final Subject subject; @Autowired public UserController(Subject subject) { this.subject = subject; } @GetMapping("/current-user") public Object getCurrentUser() { if (subject.isAuthenticated()) { return subject.getPrincipal(); } return "未登录"; } }
- 优点:符合Spring依赖注入的设计理念,单元测试时可轻松Mock Subject对象。
3. 自定义参数解析器,直接在方法参数中获取用户信息
如果想让代码更简洁,可自定义参数解析器,让控制器方法直接接收用户实体:
步骤1:自定义注解
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) public @interface CurrentUser { }
步骤2:实现HandlerMethodArgumentResolver
import org.apache.shiro.SecurityUtils; import org.apache.shiro.subject.Subject; import org.springframework.core.MethodParameter; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; public class CurrentUserArgumentResolver implements HandlerMethodArgumentResolver { @Override public boolean supportsParameter(MethodParameter parameter) { return parameter.hasParameterAnnotation(CurrentUser.class); } @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { Subject subject = SecurityUtils.getSubject(); return subject.getPrincipal(); } }
步骤3:注册参数解析器到Spring MVC
import org.springframework.context.annotation.Configuration; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import java.util.List; @Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) { resolvers.add(new CurrentUserArgumentResolver()); } }
步骤4:在控制器中使用
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class UserController { @GetMapping("/current-user") public Object getCurrentUser(@CurrentUser CustomUser user) { // 直接拿到自定义用户实体,无需手动获取Subject return user.getUsername(); } }
- 优点:代码最简洁,控制器方法专注业务逻辑,无需重复编写获取用户的代码。
- 注意:要确保
getPrincipal()返回的是自定义用户实体类,否则需在解析器中做类型转换。
4. 结合Shiro的@RequiresAuthentication注解
如果方法仅允许已登录用户访问,可先添加@RequiresAuthentication注解,再获取用户信息,确保进入方法时用户已登录:
import org.apache.shiro.authz.annotation.RequiresAuthentication; import org.apache.shiro.subject.Subject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class UserController { private final Subject subject; @Autowired public UserController(Subject subject) { this.subject = subject; } @GetMapping("/user/profile") @RequiresAuthentication public Object getUserProfile() { // 此时subject.isAuthenticated()一定为true,无需额外判断 CustomUser user = (CustomUser) subject.getPrincipal(); return user; } }
- 优点:自动拦截未登录请求,无需手动处理未登录场景,代码更安全。
内容的提问来源于stack exchange,提问作者Helge1977
相关产品推荐
相关产品推荐

