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

Spring Boot REST API:POST接口Bearer Token认证问题求助

解决Spring Boot中/api/interests接口的JWT身份认证问题

要实现Bearer Token的校验,核心是通过Spring Security的过滤器链拦截请求,解析并验证JWT,然后将认证信息注入到安全上下文。以下是针对你的场景的具体修改步骤:

1. 编写JWT认证过滤器

创建一个继承OncePerRequestFilter的过滤器,负责从请求头提取Token、验证Token有效性,并设置用户认证信息:

@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final JwtUtil jwtUtil;
    private final UserDetailsService userDetailsService;

    public JwtAuthenticationFilter(JwtUtil jwtUtil, UserDetailsService userDetailsService) {
        this.jwtUtil = jwtUtil;
        this.userDetailsService = userDetailsService;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 从请求头获取Authorization字段
        String authHeader = request.getHeader("Authorization");
        String token = null;
        String username = null;

        // 提取Bearer Token
        if (authHeader != null && authHeader.startsWith("Bearer ")) {
            token = authHeader.substring(7);
            username = jwtUtil.extractUsername(token); // 从Token中获取用户名/邮箱
        }

        // 如果Token有效且当前上下文未认证
        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            UserDetails userDetails = userDetailsService.loadUserByUsername(username);
            // 验证Token有效性
            if (jwtUtil.validateToken(token, userDetails)) {
                // 创建认证对象
                UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                // 设置到安全上下文
                SecurityContextHolder.getContext().setAuthentication(authToken);
            }
        }

        filterChain.doFilter(request, response);
    }
}

2. 修改Spring Security配置类

将JWT过滤器添加到过滤链中,同时配置/api/interests的POST请求需要认证,放开注册、登录接口的权限:

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    private final JwtAuthenticationFilter jwtAuthFilter;
    private final AuthenticationProvider authenticationProvider;

    public SecurityConfig(JwtAuthenticationFilter jwtAuthFilter, AuthenticationProvider authenticationProvider) {
        this.jwtAuthFilter = jwtAuthFilter;
        this.authenticationProvider = authenticationProvider;
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .csrf(csrf -> csrf.disable())
                .authorizeHttpRequests(auth -> auth
                        // 放开注册、登录接口
                        .requestMatchers("/api/auth/register", "/api/auth/login").permitAll()
                        // /api/interests的POST请求需要认证
                        .requestMatchers(HttpMethod.POST, "/api/interests").authenticated()
                        // 其他请求按需配置
                        .anyRequest().authenticated()
                )
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authenticationProvider(authenticationProvider)
                // 将JWT过滤器加到用户名密码过滤器之前
                .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }

    // 配置基于数据库的用户认证提供者
    @Bean
    public AuthenticationProvider authenticationProvider(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) {
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
        authProvider.setUserDetailsService(userDetailsService);
        authProvider.setPasswordEncoder(passwordEncoder);
        return authProvider;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

3. 完善JwtUtil工具类的关键方法

确保你的JwtUtil包含以下核心方法,用于提取用户信息和验证Token:

@Component
public class JwtUtil {

    // 建议从配置文件读取密钥和过期时间
    private String secretKey = "your-strong-secret-key-here";
    private long jwtExpirationMs = 86400000; // 24小时过期

    // 从Token中提取用户名/邮箱
    public String extractUsername(String token) {
        return extractClaim(token, Claims::getSubject);
    }

    // 通用提取Claims方法
    public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = extractAllClaims(token);
        return claimsResolver.apply(claims);
    }

    private Claims extractAllClaims(String token) {
        return Jwts.parserBuilder()
                .setSigningKey(getSignInKey())
                .build()
                .parseClaimsJws(token)
                .getBody();
    }

    // 验证Token是否有效(匹配用户信息+未过期)
    public boolean validateToken(String token, UserDetails userDetails) {
        final String username = extractUsername(token);
        return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    }

    private boolean isTokenExpired(String token) {
        return extractExpiration(token).before(new Date());
    }

    private Date extractExpiration(String token) {
        return extractClaim(token, Claims::getExpiration);
    }

    private Key getSignInKey() {
        byte[] keyBytes = Decoders.BASE64.decode(secretKey);
        return Keys.hmacShaKeyFor(keyBytes);
    }

    // 登录时生成Token的方法
    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        return createToken(claims, userDetails.getUsername());
    }

    private String createToken(Map<String, Object> claims, String subject) {
        return Jwts.builder()
                .setClaims(claims)
                .setSubject(subject)
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + jwtExpirationMs))
                .signWith(getSignInKey(), SignatureAlgorithm.HS256)
                .compact();
    }
}

4. 优化兴趣接口(可选)

如果需要关联兴趣到当前用户,可以在接口中获取认证用户信息,同时添加注解确保只有认证用户能访问:

@RestController
@RequestMapping("/api/interests")
public class InterestController {

    private final InterestService interestService;

    public InterestController(InterestService interestService) {
        this.interestService = interestService;
    }

    @PostMapping
    @PreAuthorize("isAuthenticated()")
    public ResponseEntity<Interest> createInterest(@RequestBody InterestRequest request) {
        // 获取当前登录用户的用户名/邮箱
        UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        Interest interest = interestService.createInterest(request.getDescription(), userDetails.getUsername());
        return ResponseEntity.status(HttpStatus.CREATED).body(interest);
    }
}

验证步骤

  1. 调用注册接口创建用户,再调用登录接口获取Bearer Token。
  2. 调用POST /api/interests时,在请求头中添加:Authorization: Bearer <你的Token>。
  3. Token有效则正常创建兴趣,无效/过期则返回401 Unauthorized。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 16:07:40