Spring Boot 3.2.1中JWT认证返回Forbidden问题排查求助
JWT令牌认证返回"Forbidden"问题排查
开发学习管理系统(LMS)后端时遇到JWT认证问题:用户名密码登录成功后能正常获取JWT令牌,但使用该令牌请求需要认证的接口时,持续收到"Forbidden"错误。
以下是相关配置与代码:
安全配置类(SecurityConfig)
@Configuration @RequiredArgsConstructor public class SecurityConfig { private final AccUserDetailsService userService; private final JwtAuthenticationTokenFilter filterService; @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception { return authenticationConfiguration.getAuthenticationManager(); } @Bean protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests(authorize -> authorize .requestMatchers("/api/admin/**").hasRole("ADMIN") .requestMatchers("api/staff/**").hasAnyRole("STAFF","ADMIN") .requestMatchers("api/student/**").hasRole("STUDENT") .anyRequest().authenticated()); http.addFilterBefore(filterService, UsernamePasswordAuthenticationFilter.class); return http.build(); } @Bean public WebSecurityCustomizer webSecurityCustomizer() { return web -> web.ignoring().requestMatchers("/logging", "/welcome","api/signup","/api/authenticate"); } }
JWT配置类(JwtAuthenticationProvider)
@Configuration public class JwtAuthenticationProvider { @Value("${api.jwtSecret}") private String secret; private final int TOKEN_VALIDITY = 5 * 60 * 60; public String generateToken(String userDetails) { Map<String, Object> claims = new HashMap<>(); return doGenerateToken(claims, userDetails); } private String doGenerateToken(Map<String, Object> claims, String subject) { return Jwts.builder() .setClaims(claims) .setSubject(subject) .setIssuedAt(new Date(System.currentTimeMillis())) .setExpiration(new Date(System.currentTimeMillis() + TOKEN_VALIDITY * 1000)) .signWith(getSigningKey(),SignatureAlgorithm.HS256).compact(); } public String getUsernameFromToken(String token) { return getClaimFromToken(token, Claims::getSubject); } private Key getSigningKey() { byte[] keyBytes = Decoders.BASE64.decode(secret); return Keys.hmacShaKeyFor(keyBytes); } private Boolean isTokenExpired(String token) { final Date expiration = getExpirationDateFromToken(token); return expiration.before(new Date()); } private Date getExpirationDateFromToken(String token) { return getClaimFromToken(token, Claims::getExpiration); } private <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) { final Claims claims = getAllClaimsFromToken(token); return claimsResolver.apply(claims); } private Claims getAllClaimsFromToken(String token) { return Jwts.parserBuilder().setSigningKey(getSigningKey()).build().parseClaimsJws(token).getBody(); } public Boolean validateToken(String token, UserDetails userDetails) { final String username = getUsernameFromToken(token); return (username.equals(userDetails.getUsername()) && !isTokenExpired(token)); } }
JWT认证过滤器(JwtAuthenticationTokenFilter)
@Component @RequiredArgsConstructor public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { private final JwtAuthenticationProvider tokenService; private final AccUserDetailsService accUserDetailsService; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { final String authorizationHeader = request.getHeader("Authorization"); String username = null; String jwt = null; if(authorizationHeader != null && authorizationHeader.startsWith("Bearer ")){ jwt = authorizationHeader.substring(7); username = tokenService.getUsernameFromToken(jwt); } if(username != null && SecurityContextHolder.getContext().getAuthentication() == null){ UserDetails userDetails = this.accUserDetailsService.loadUserByUsername(username); if(tokenService.validateToken(jwt,userDetails)){ UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken( userDetails,null,userDetails.getAuthorities()); usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken); } } filterChain.doFilter(request,response); } }
控制器类(TestController)
@RestController @RequiredArgsConstructor public class TestController { private final AccountService accountService; private final AuthService authService; @GetMapping("/api/admin/") public String admin(){ return ("<h1>Welcome admin<h1>"); } @PostMapping("/api/signup") public String createAccount(@RequestBody SignUpRequest signUpRequest){ accountService.signup(signUpRequest); return "test"; } @PostMapping("/api/authenticate") public ResponseEntity<APIResponse<?>> createAccount(@RequestBody LoginRequest loginRequest) throws AuthenticationException { LoginResponse loginResponse = authService.userAuthenticate(loginRequest); return ResponseEntity.ok(new APIResponse<>(ResponseCode.SUCCESS_LOGIN,loginResponse)); } }
pom.xml配置
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.2.1</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.dsm</groupId> <artifactId>dsm</artifactId> <version>0.0.1-SNAPSHOT</version> <name>dsm</name> <description>Driving </description> <properties> <java.version>17</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> </dependency> <dependency> <groupId>org.mariadb.jdbc</groupId> <artifactId>mariadb-java-client</artifactId> <version>3.1.2</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <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> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <version>0.11.5</version> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project>
内容的提问来源于stack exchange,提问作者Buddhima Chamaranga
相关产品推荐
相关产品推荐

