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

SpringBoot无认证场景下REST API的Bearer Token实现方案咨询

在SpringBoot中直接生成并验证Bearer Token保护无登录API

完全可以跳过用户登录流程,直接生成Bearer Token供客户端使用,通过Spring Security实现Token验证逻辑。以下是几种实用方案:

方案1:预生成固定Token(适合固定客户端调用)

实现步骤

  1. 添加依赖
    在pom.xml中引入Spring Security依赖:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    
  2. 配置固定Token
    在application.yml中定义合法Token:

    app:
      api-token: "your-secure-fixed-token-12345"
    
  3. 编写Security配置类
    配置拦截规则并添加自定义Token验证过滤器:

    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.config.http.SessionCreationPolicy;
    import org.springframework.security.web.SecurityFilterChain;
    import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
    
    @Configuration
    @EnableWebSecurity
    public class SecurityConfig {
    
        @Value("${app.api-token}")
        private String validToken;
    
        @Bean
        public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
            http
                .csrf(csrf -> csrf.disable())
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authorizeHttpRequests(auth -> auth
                    .anyRequest().authenticated()
                )
                .addFilterBefore(new BearerTokenFilter(validToken), UsernamePasswordAuthenticationFilter.class);
    
            return http.build();
        }
    }
    
  4. 自定义Token验证过滤器
    提取请求头中的Token并与配置值对比:

    import jakarta.servlet.FilterChain;
    import jakarta.servlet.ServletException;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
    import org.springframework.security.core.context.SecurityContextHolder;
    import org.springframework.web.filter.OncePerRequestFilter;
    
    import java.io.IOException;
    import java.util.Collections;
    
    public class BearerTokenFilter extends OncePerRequestFilter {
    
        private final String validToken;
    
        public BearerTokenFilter(String validToken) {
            this.validToken = validToken;
        }
    
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
            String authHeader = request.getHeader("Authorization");
    
            if (authHeader != null && authHeader.startsWith("Bearer ")) {
                String token = authHeader.substring(7);
                if (validToken.equals(token)) {
                    UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
                            "api-client", null, Collections.emptyList());
                    SecurityContextHolder.getContext().setAuthentication(authToken);
                } else {
                    response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "无效的Bearer Token");
                    return;
                }
            } else {
                response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "需要携带Bearer Token");
                return;
            }
    
            filterChain.doFilter(request, response);
        }
    }
    

方案2:动态生成临时Token(适合临时访问场景)

如果需要生成带过期时间的临时Token,可以结合Redis存储Token状态:

  1. 添加Redis依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    
  2. 编写Token生成接口

    import org.springframework.data.redis.core.StringRedisTemplate;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.UUID;
    import java.util.concurrent.TimeUnit;
    
    @RestController
    public class TokenController {
    
        private final StringRedisTemplate redisTemplate;
    
        public TokenController(StringRedisTemplate redisTemplate) {
            this.redisTemplate = redisTemplate;
        }
    
        @GetMapping("/api/generate-token")
        public String generateToken() {
            String token = UUID.randomUUID().toString();
            // 设置1小时过期时间
            redisTemplate.opsForValue().set("api-token:" + token, "valid", 1, TimeUnit.HOURS);
            return "Bearer " + token;
        }
    }
    
  3. 修改过滤器验证逻辑
    从Redis中检查Token是否存在且有效:

    import jakarta.servlet.FilterChain;
    import jakarta.servlet.ServletException;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    import org.springframework.data.redis.core.StringRedisTemplate;
    import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
    import org.springframework.security.core.context.SecurityContextHolder;
    import org.springframework.web.filter.OncePerRequestFilter;
    
    import java.io.IOException;
    import java.util.Collections;
    import java.util.concurrent.TimeUnit;
    
    public class DynamicBearerTokenFilter extends OncePerRequestFilter {
    
        private final StringRedisTemplate redisTemplate;
    
        public DynamicBearerTokenFilter(StringRedisTemplate redisTemplate) {
            this.redisTemplate = redisTemplate;
        }
    
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
            String authHeader = request.getHeader("Authorization");
    
            if (authHeader != null && authHeader.startsWith("Bearer ")) {
                String token = authHeader.substring(7);
                String key = "api-token:" + token;
                if (redisTemplate.hasKey(key)) {
                    UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
                            "api-client", null, Collections.emptyList());
                    SecurityContextHolder.getContext().setAuthentication(authToken);
                    // 刷新Token过期时间
                    redisTemplate.expire(key, 1, TimeUnit.HOURS);
                } else {
                    response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Token无效或已过期");
                    return;
                }
            } else {
                response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "需要携带Bearer Token");
                return;
            }
    
            filterChain.doFilter(request, response);
        }
    }
    

方案3:JWT无状态Token(适合分布式场景)

用JWT生成自包含Token,无需存储,直接验证签名和过期时间:

  1. 添加JWT依赖

    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-api</artifactId>
        <version>0.11.5</version>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-impl</artifactId>
        <version>0.11.5</version>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-jackson</artifactId>
        <version>0.11.5</version>
        <scope>runtime</scope>
    </dependency>
    
  2. 配置JWT参数

    app:
      jwt-secret: "your-strong-jwt-secret-key-32-chars-minimum"
      jwt-expiration-ms: 3600000 # 1小时
    
  3. 生成JWT接口

    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.SignatureAlgorithm;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.Date;
    
    @RestController
    public class JwtTokenController {
    
        @Value("${app.jwt-secret}")
        private String jwtSecret;
    
        @Value("${app.jwt-expiration-ms}")
        private long jwtExpirationMs;
    
        @GetMapping("/api/generate-jwt")
        public String generateJwtToken() {
            return Jwts.builder()
                    .setSubject("api-client")
                    .setIssuedAt(new Date())
                    .setExpiration(new Date(System.currentTimeMillis() + jwtExpirationMs))
                    .signWith(SignatureAlgorithm.HS512, jwtSecret)
                    .compact();
        }
    }
    
  4. JWT验证过滤器

    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.security.Keys;
    import jakarta.servlet.FilterChain;
    import jakarta.servlet.ServletException;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
    import org.springframework.security.core.context.SecurityContextHolder;
    import org.springframework.web.filter.OncePerRequestFilter;
    
    import javax.crypto.SecretKey;
    import java.io.IOException;
    import java.util.Collections;
    
    public class JwtValidationFilter extends OncePerRequestFilter {
    
        @Value("${app.jwt-secret}")
        private String jwtSecret;
    
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
            String authHeader = request.getHeader("Authorization");
    
            if (authHeader != null && authHeader.startsWith("Bearer ")) {
                String token = authHeader.substring(7);
                try {
                    SecretKey key = Keys.hmacShaKeyFor(jwtSecret.getBytes());
                    Jwts.parserBuilder()
                            .setSigningKey(key)
                            .build()
                            .parseClaimsJws(token);
    
                    UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
                            "api-client", null, Collections.emptyList());
                    SecurityContextHolder.getContext().setAuthentication(authToken);
                } catch (Exception e) {
                    response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "JWT无效或已过期");
                    return;
                }
            } else {
                response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "需要携带Bearer Token");
                return;
            }
    
            filterChain.doFilter(request, response);
        }
    }
    

注意事项

  • 固定Token适合信任度高的固定客户端,需严格保密Token内容
  • 动态Token和JWT更适合临时或多客户端场景,能有效降低泄露风险
  • 生产环境务必使用HTTPS传输Token,避免明文泄露

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 19:24:27