Spring Boot中解决静态上下文调用非静态方法报错问题
解决Spring Boot中JWT工具类非静态方法调用的静态上下文错误
问题背景
在Spring Boot项目中调用TokenUtils的createToken和getAuthentication方法时,触发错误:
Non-static method 'getAuthentication(java.lang.String)' cannot be referenced from a static context
代码中无静态字段,但无法将目标方法设为静态——因为jwt_secret需要Spring注入,静态字段无法被Spring正常赋值。
相关代码
TokenUtils类
package com.XX.ZZ.security; import io.jsonwebtoken.Claims; import io.jsonwebtoken.JwtException; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.security.Keys; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.stereotype.Component; import java.util.*; @Component public class TokenUtils { @Autowired @Value("${custom.data.jwt.secret}") private final String jwt_secret; public TokenUtils(String jwtSecret) { jwt_secret = jwtSecret; } public String createToken(String name, String email){ long expirationTime = 86400 * 1000; Date expirationDate = new Date(System.currentTimeMillis() + expirationTime); Map<String, Object> extra = new HashMap<>(); extra.put("name", name); return Jwts.builder() .setSubject(email) .setExpiration(expirationDate) .addClaims(extra) .signWith(Keys.hmacShaKeyFor(jwt_secret.getBytes())) .compact(); } public UsernamePasswordAuthenticationToken getAuthentication(String token){ try { Claims claims = Jwts.parserBuilder() .setSigningKey(jwt_secret.getBytes()) .build() .parseClaimsJws(token) .getBody(); String email = claims.getSubject(); return new UsernamePasswordAuthenticationToken(email, null, Collections.emptyList()); } catch (JwtException e){ return null; } } }
JWTAuthorizationFilter类(错误调用位置)
package com.XX.ZZ.security; 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.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; import java.io.IOException; @Component public class JWTAuthorizationFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String bearerToken = request.getHeader("Authorization"); if(bearerToken != null && bearerToken.startsWith("Bearer ")){ String token = bearerToken.replace("Bearer ", ""); UsernamePasswordAuthenticationToken usernamePAT = TokenUtils.getAuthentication(token); SecurityContextHolder.getContext().setAuthentication(usernamePAT); } filterChain.doFilter(request, response); } }
JWTAuthenticationFilter类(错误调用位置)
package com.XX.ZZ.security; import com.fasterxml.jackson.databind.ObjectMapper; 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.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import java.io.IOException; import java.util.Collections; public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter { @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { AuthCredentials authCredentials = new AuthCredentials(); try { authCredentials = new ObjectMapper().readValue(request.getReader(), AuthCredentials.class); } catch (IOException e){ } UsernamePasswordAuthenticationToken usernamePAT = new UsernamePasswordAuthenticationToken( authCredentials.getEmail(), authCredentials.getPassword(), Collections.emptyList() ); return getAuthenticationManager().authenticate(usernamePAT); } @Override protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { UserDetailsImpl userDetails = (UserDetailsImpl) authResult.getPrincipal(); String token = TokenUtils.createToken(userDetails.getName(), userDetails.getUsername()); response.addHeader("Authorization", "Bearer " + token); response.getWriter().flush(); super.successfulAuthentication(request, response, chain, authResult); } }
解决方案
错误根源是直接通过类名调用非静态方法(静态上下文访问非静态成员),核心解决思路是通过Spring注入TokenUtils实例,用实例调用方法,同时修正TokenUtils的注入逻辑确保配置值能正常赋值。
1. 修正TokenUtils的注入方式
final字段必须通过构造器初始化,结合@Value实现构造器注入,替代错误的字段注入写法:
package com.XX.ZZ.security; import io.jsonwebtoken.Claims; import io.jsonwebtoken.JwtException; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.security.Keys; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.stereotype.Component; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Map; @Component public class TokenUtils { private final String jwt_secret; // 构造器注入配置值,Spring自动解析${custom.data.jwt.secret} public TokenUtils(@Value("${custom.data.jwt.secret}") String jwtSecret) { this.jwt_secret = jwtSecret; } public String createToken(String name, String email){ long expirationTime = 86400 * 1000; Date expirationDate = new Date(System.currentTimeMillis() + expirationTime); Map<String, Object> extra = new HashMap<>(); extra.put("name", name); return Jwts.builder() .setSubject(email) .setExpiration(expirationDate) .addClaims(extra) .signWith(Keys.hmacShaKeyFor(jwt_secret.getBytes())) .compact(); } public UsernamePasswordAuthenticationToken getAuthentication(String token){ try { Claims claims = Jwts.parserBuilder() .setSigningKey(jwt_secret.getBytes()) .build() .parseClaimsJws(token) .getBody(); String email = claims.getSubject(); return new UsernamePasswordAuthenticationToken(email, null, Collections.emptyList()); } catch (JwtException e){ return null; } } }
2. 给JWTAuthorizationFilter注入TokenUtils实例
利用构造器注入TokenUtils,替换原来的静态调用:
package com.XX.ZZ.security; 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.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; import java.io.IOException; @Component public class JWTAuthorizationFilter extends OncePerRequestFilter { private final TokenUtils tokenUtils; // 构造器注入TokenUtils实例 public JWTAuthorizationFilter(TokenUtils tokenUtils) { this.tokenUtils = tokenUtils; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String bearerToken = request.getHeader("Authorization"); if(bearerToken != null && bearerToken.startsWith("Bearer ")){ String token = bearerToken.replace("Bearer ", ""); // 用实例调用方法,替代静态调用 UsernamePasswordAuthenticationToken usernamePAT = tokenUtils.getAuthentication(token); SecurityContextHolder.getContext().setAuthentication(usernamePAT); } filterChain.doFilter(request, response); } }
3. 处理JWTAuthenticationFilter的注入逻辑
该类未被Spring管理(无@Component),需在Security配置类中手动创建实例并传入TokenUtils:
第一步:修改JWTAuthenticationFilter添加构造器
package com.XX.ZZ.security; import com.fasterxml.jackson.databind.ObjectMapper; 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.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import java.io.IOException; import java.util.Collections; public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter { private final TokenUtils tokenUtils; // 构造器注入TokenUtils public JWTAuthenticationFilter(TokenUtils tokenUtils) { this.tokenUtils = tokenUtils; } @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { AuthCredentials authCredentials = new AuthCredentials(); try { authCredentials = new ObjectMapper().readValue(request.getReader(), AuthCredentials.class); } catch (IOException e){ } UsernamePasswordAuthenticationToken usernamePAT = new UsernamePasswordAuthenticationToken( authCredentials.getEmail(), authCredentials.getPassword(), Collections.emptyList() ); return getAuthenticationManager().authenticate(usernamePAT); } @Override protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { UserDetailsImpl userDetails = (UserDetailsImpl) authResult.getPrincipal(); // 用实例调用方法,替代静态调用 String token = tokenUtils.createToken(userDetails.getName(), userDetails.getUsername()); response.addHeader("Authorization", "Bearer " + token); response.getWriter().flush(); super.successfulAuthentication(request, response, chain, authResult); } }
第二步:在SecurityConfig中配置过滤器
package com.XX.ZZ.security; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; 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 { private final TokenUtils tokenUtils; private final JWTAuthorizationFilter jwtAuthorizationFilter; public SecurityConfig(TokenUtils tokenUtils, JWTAuthorizationFilter jwtAuthorizationFilter) { this.tokenUtils = tokenUtils; this.jwtAuthorizationFilter = jwtAuthorizationFilter; } @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception { return authenticationConfiguration.getAuthenticationManager(); } @Bean public JWTAuthenticationFilter jwtAuthenticationFilter(AuthenticationManager authenticationManager) { JWTAuthenticationFilter filter = new JWTAuthenticationFilter(tokenUtils); filter.setAuthenticationManager(authenticationManager); return filter; } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .csrf(csrf -> csrf.disable()) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(auth -> auth .requestMatchers("/login").permitAll() .anyRequest().authenticated() ) .addFilterBefore(jwtAuthenticationFilter(http.getSharedObject(AuthenticationManager.class)), UsernamePasswordAuthenticationFilter.class) .addFilterBefore(jwtAuthorizationFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); } }
内容的提问来源于stack exchange,提问作者LautaroColella
相关产品推荐
相关产品推荐

