You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 18:47:01