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
相关产品推荐
相关产品推荐

