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

Spring新手求教:如何通过注解实现JWT认证过滤器?

嘿,作为Spring新手搞JWT认证确实得一步步捋清楚,我来帮你拆解怎么实现你要的需求~

核心问题:是否仅对带特定注解的请求做JWT验证?

答案是完全可以,而且有两种常见的实现思路,你可以根据自己的业务场景选:一种是「只给需要验证的接口标记注解,过滤器只处理这些请求」,另一种是「默认所有请求都验证,只放行特定路径(比如你的/token端点)」。下面分别给你讲具体实现:

方式一:自定义注解 + JWT过滤器(精准控制需要验证的接口)

这种方式适合你想精细化控制哪些接口需要JWT验证的场景,步骤如下:

1. 先定义一个自定义注解

比如叫@JwtRequired,用来标记需要验证JWT的接口方法:

import java.lang.annotation.*;

@Target(ElementType.METHOD) // 只作用在方法上
@Retention(RetentionPolicy.RUNTIME) // 运行时能被反射读取到
public @interface JwtRequired {
}

2. 编写JWT验证过滤器

过滤器里要做的事情:跳过/token端点,然后判断当前请求的接口方法有没有@JwtRequired注解,有就执行验证,没有就直接放行:

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final RequestMappingHandlerMapping handlerMapping;
    private final JwtUtil jwtUtil; // 自己实现的JWT工具类,用来验证、解析令牌

    // 通过构造器注入依赖
    public JwtAuthenticationFilter(RequestMappingHandlerMapping handlerMapping, JwtUtil jwtUtil) {
        this.handlerMapping = handlerMapping;
        this.jwtUtil = jwtUtil;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 先放行/token端点,不需要验证
        if ("/token".equals(request.getRequestURI())) {
            filterChain.doFilter(request, response);
            return;
        }

        try {
            // 获取当前请求对应的处理器方法
            HandlerExecutionChain handlerChain = handlerMapping.getHandler(request);
            if (handlerChain != null && handlerChain.getHandler() instanceof HandlerMethod handlerMethod) {
                // 检查方法上是否有@JwtRequired注解
                if (handlerMethod.hasMethodAnnotation(JwtRequired.class)) {
                    // 从请求头里提取JWT令牌
                    String token = extractTokenFromRequest(request);
                    // 验证令牌有效性
                    if (token == null || !jwtUtil.validateToken(token)) {
                        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "无效或缺失JWT令牌");
                        return;
                    }
                    // 验证通过后,把用户信息放到请求上下文,方便后续接口使用
                    request.setAttribute("currentUser", jwtUtil.extractUserInfo(token));
                }
            }
            // 不管有没有注解,都继续执行过滤器链
            filterChain.doFilter(request, response);
        } catch (Exception e) {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "JWT认证失败");
        }
    }

    // 从Authorization头里提取Bearer令牌
    private String extractTokenFromRequest(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authorization");
        if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7); // 去掉"Bearer "前缀
        }
        return null;
    }
}

3. 注册过滤器到Spring容器

在配置类里把过滤器注册进去,让Spring管理:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

@Configuration
public class FilterConfig {

    @Bean
    public JwtAuthenticationFilter jwtAuthenticationFilter(RequestMappingHandlerMapping handlerMapping, JwtUtil jwtUtil) {
        return new JwtAuthenticationFilter(handlerMapping, jwtUtil);
    }
}

4. 在需要验证的接口上标记注解

比如你的私有接口就加上@JwtRequired,公开接口不用加:

@RestController
@RequestMapping("/api")
public class UserController {

    // 这个接口需要JWT验证
    @GetMapping("/profile")
    @JwtRequired
    public ResponseEntity<UserProfile> getCurrentUserProfile() {
        // 可以从request.getAttribute("currentUser")获取用户信息
        return ResponseEntity.ok(new UserProfile());
    }

    // 这个接口是公开的,不需要验证
    @GetMapping("/public-notice")
    public ResponseEntity<String> getPublicNotice() {
        return ResponseEntity.ok("这是公开公告,任何人都能看");
    }
}

方式二:全局验证 + 放行特定路径(更省心的通用方案)

如果你的大部分接口都需要JWT验证,只有少数几个公开接口(比如/token、公开信息接口),那这种方式更省心:默认所有请求都走JWT验证,只放行指定的路径。

修改过滤器的逻辑就行:

import java.util.Arrays;
import java.util.List;

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    // 定义不需要验证的路径列表
    List<String> allowedPaths = Arrays.asList("/token", "/api/public-notice");
    String requestUri = request.getRequestURI();

    // 如果是放行路径,直接跳过验证
    if (allowedPaths.contains(requestUri)) {
        filterChain.doFilter(request, response);
        return;
    }

    // 其他请求都执行JWT验证
    String token = extractTokenFromRequest(request);
    if (token == null || !jwtUtil.validateToken(token)) {
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "无效或缺失JWT令牌");
        return;
    }

    request.setAttribute("currentUser", jwtUtil.extractUserInfo(token));
    filterChain.doFilter(request, response);
}

额外小提醒

  • 你需要自己实现JwtUtil工具类,用JJWT库(io.jsonwebtoken:jjwt-api、jjwt-impl、jjwt-jackson)来做令牌的生成、验证、解析,比如生成token时设置过期时间、签名密钥,验证时检查签名和过期时间。
  • 如果后续用Spring Security的话,也可以用@PreAuthorize这类注解配合Security的机制,但对于新手来说,先从自定义过滤器+注解的方式入手,更容易理解底层逻辑。

内容的提问来源于stack exchange,提问作者Rajan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 09:11:03