Spring Security中如何在控制器免重复从Token获取用户邮箱?
如何在Spring Security中避免重复解析JWT Token获取用户邮箱
你已经实现了JWT过滤器完成Token校验,现在可以通过以下几种方式避免在每个控制器中重复解析Token获取用户邮箱:
方案一:直接利用Spring Security的SecurityContextHolder
你的过滤器已经将UserDetails对象存入了SecurityContextHolder的Authentication中,因此可以直接在控制器中获取认证信息:
方式1:注入Authentication对象
@GetMapping("/friends") public ResponseEntity<Set<User>> getFriends(Authentication authentication){ // 从Authentication中取出过滤器已存入的UserDetails UserDetails userDetails = (UserDetails) authentication.getPrincipal(); String email = userDetails.getUsername(); // 假设你的UserDetails实现类中username字段存储的是邮箱 User user = service.findByEmail(email); if (user == null){ throw new UserNotExistException(); } return ResponseEntity.ok(user.getFriends()); }
方式2:使用@AuthenticationPrincipal注解
Spring Security提供了@AuthenticationPrincipal注解,可以直接注入当前认证的UserDetails:
@GetMapping("/friends") public ResponseEntity<Set<User>> getFriends(@AuthenticationPrincipal UserDetails userDetails){ String email = userDetails.getUsername(); User user = service.findByEmail(email); if (user == null){ throw new UserNotExistException(); } return ResponseEntity.ok(user.getFriends()); }
如果你的User实体类直接实现了UserDetails接口,还可以直接注入User对象:
@GetMapping("/friends") public ResponseEntity<Set<User>> getFriends(@AuthenticationPrincipal User user){ return ResponseEntity.ok(user.getFriends()); }
方案二:自定义注解自动注入当前用户
如果想进一步简化代码,可以自定义一个注解,配合参数解析器自动注入当前用户:
1. 创建自定义注解
@Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) public @interface CurrentUser { }
2. 实现参数解析器
@Component public class CurrentUserArgumentResolver implements HandlerMethodArgumentResolver { private final UserService userService; public CurrentUserArgumentResolver(UserService userService) { this.userService = userService; } @Override public boolean supportsParameter(MethodParameter parameter) { // 判断参数是否带有@CurrentUser注解,且类型为User return parameter.getParameterAnnotation(CurrentUser.class) != null && User.class.isAssignableFrom(parameter.getParameterType()); } @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication == null || !(authentication.getPrincipal() instanceof UserDetails)) { throw new UserNotExistException(); } UserDetails userDetails = (UserDetails) authentication.getPrincipal(); // 通过邮箱查询User实体 return userService.findByEmail(userDetails.getUsername()); } }
3. 注册参数解析器
@Configuration public class WebMvcConfig implements WebMvcConfigurer { private final CurrentUserArgumentResolver currentUserArgumentResolver; public WebMvcConfig(CurrentUserArgumentResolver currentUserArgumentResolver) { this.currentUserArgumentResolver = currentUserArgumentResolver; } @Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) { resolvers.add(currentUserArgumentResolver); } }
4. 控制器中使用
@GetMapping("/friends") public ResponseEntity<Set<User>> getFriends(@CurrentUser User user){ return ResponseEntity.ok(user.getFriends()); }
方案三:将邮箱存入请求属性
在JWT过滤器的认证逻辑完成后,将用户邮箱存入请求属性,控制器直接获取:
修改JwtTokenFilter
在认证通过的代码块中添加:
// 认证通过后,将邮箱存入请求属性 request.setAttribute("currentUserEmail", email);
控制器中获取
@GetMapping("/friends") public ResponseEntity<Set<User>> getFriends(@RequestAttribute("currentUserEmail") String email){ User user = service.findByEmail(email); if (user == null){ throw new UserNotExistException(); } return ResponseEntity.ok(user.getFriends()); }
内容的提问来源于stack exchange,提问作者Rubix327
相关产品推荐
相关产品推荐

