我正在进行一个Spring Boot项目,在那里我实现了基于JWT的身份验证。我有一个/api/game/save端点,它应该接受带有有效JWT的POST请求。尽管在Authorization标头中包含了令牌,但我仍然收到403 Forbidden响应:
安全配置:
package com.clicker.configuration;
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.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/register", "/api/auth/login").permitAll()
.requestMatchers("/api/game/**").authenticated()
.anyRequest().authenticated());
http.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
return http.build();
}
}
JWT实用程序(JwtUtil):
package com.clicker.component;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;
import java.util.Date;
import static io.jsonwebtoken.Jwts.builder;
@Component
public class JwtUtil {
private static final String SECRET_KEY = "secret_key"; // Not my real secret key ofc
private static final long EXPIRATION_TIME = 1000 * 60 * 60; // 10 hours
public String generateToken(String username) {
return builder()
.setSubject(username)
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
public boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token);
return true;
} catch (JwtException | IllegalArgumentException e) {
return false;
}
}
public String extractUsername(String token) {
return Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody()
.getSubject();
}
}
控制器终结点:
package com.clicker.controller;
import com.clicker.service.GameDataService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/game")
public class GameDataController {
@Autowired
private GameDataService gameDataService;
@PostMapping("/save")
public ResponseEntity<?> saveGameData(@RequestHeader("Authorization") String token,
@RequestBody String gameDataJson) {
return gameDataService.saveGameData(token, gameDataJson);
}
@PostMapping("/load")
public ResponseEntity<?> loadGameData(@RequestHeader("Authorization") String token) {
return gameDataService.loadGameData(token);
}
}
服务:
package com.clicker.service;
import com.clicker.component.JwtUtil;
import com.clicker.entity.GameData;
import com.clicker.entity.User;
import com.clicker.repository.GameDataRepository;
import com.clicker.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
@Service
public class GameDataService {
@Autowired
private GameDataRepository gameDataRepository;
@Autowired
private UserRepository userRepository;
@Autowired
private JwtUtil jwtUtil;
public ResponseEntity<?> saveGameData(String token, String gameDataJson) {
String username = jwtUtil.extractUsername(token.substring(7));
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new RuntimeException("User not found!"));
GameData gameData = gameDataRepository.findByUser(user)
.orElse(new GameData());
gameData.setUser(user);
gameData.setData(gameDataJson);
gameDataRepository.save(gameData);
return ResponseEntity.ok("Game data saved successfully!");
}
public ResponseEntity<?> loadGameData(String token) {
String username = jwtUtil.extractUsername(token.substring(7));
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new RuntimeException("User not found!"));
return gameDataRepository.findByUser(user)
.map(gameData -> ResponseEntity.ok(gameData.getData()))
.orElse(ResponseEntity.ok("{\n" +
" \"playerStats\": { \"solarEnergy\": 0, \"multiplyCostData\": 200, \"solarEnergyPerClick\": 1, \"solarEnergyPerSecond\": 0},\n" +
" \"planets\": {\n" +
" \"mercury\": { \"unlocked\": false, \"cost\": 500 },\n" +
" \"venus\": { \"unlocked\": false, \"cost\": 2500 },\n" +
" \"earth\": { \"unlocked\": false, \"cost\": 12500 },\n" +
" \"moon\": { \"unlocked\": false, \"cost\": 62500 }\n" +
" }\n" +
"}"));
}
public boolean validateToken(String token) {
return jwtUtil.validateToken(token);
}
}
application.properties:
spring.application.name=Solar-Planet-Clicker
spring.datasource.url=jdbc:mysql://localhost:3306/solar_planet_clicker
spring.datasource.username=root
spring.datasource.password=
spring.jpa.hibernate.ddl-auto=update
spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
当我使用有效的令牌和正文向/api/game/save发送POST请求时,我得到了403 Forbidden响应。