Issue
I'm currently building a standard JwtAuthorizationFilter
. I extend the OncePerRequestFilter class
for this. Furthermore I have a JwtUtils class
, which contains all JWT methods. For example, one method validates the JWT bearer token. However, I keep getting the error that this method (and all others) cannot be invoked because this.jwtUtils
is null.
So bassicly I am trying to autowire
the JwtUtils classe. But Spring
is not giving any instance
. Instead it is giving null
Thats my error message
Cannot invoke ... jwt.JwtUtils.validateJwtToken(String)" because "this.jwtUtils" is null
JwtAuthorizationFilter class (the error is throwing here)
@Slf4j
public class JwtAuthorizationFilter extends OncePerRequestFilter {
@Autowired private JwtUtils jwtUtils;
@Autowired private UserDetailsServiceImpl userDetailsService;
@Override
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
try {
String jwt = JwtUtils.resolveToken(request);
System.out.println("before if " + jwt);
if (jwtUtils.validateJwtToken(jwt)) { // ERROR !
String username = jwtUtils.getUserNameFromJwtToken(jwt);
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (Exception e) {
JwtAuthorizationFilter.log.error("Cannot set user authentication: {}", e.getMessage());
}
filterChain.doFilter(request, response);
}
}
JwtUtils class
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.UnsupportedJwtException;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SignatureException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.crypto.SecretKey;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
@Slf4j
@Component
public class JwtUtils {
@Value("${ggg.app.jwtSecret}")
private String jwtSecret;
@Value("${ggg.app.jwtExpirationMs}")
private long jwtExpirationMs;
@Value("${ggg.app.jwtRefreshExpirationMs}")
private long jwtRefreshExpirationMs;
static String resolveToken(HttpServletRequest req) {
String bearerToken = req.getHeader("Authorization");
if (bearerToken != null && bearerToken.startsWith("Bearer ")) return bearerToken.substring(7);
return null;
}
public String generateJwtAccessToken(String username) {
return generateTokenFromUsername(username, jwtExpirationMs);
}
public String generateJwtRefreshToken(String username) {
return generateTokenFromUsername(username, jwtRefreshExpirationMs);
}
private String generateTokenFromUsername(String username, Long expiration) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date((new Date()).getTime() + expiration))
.signWith(secretKey())
.compact();
}
public String getUserNameFromJwtToken(String token) {
return Jwts.parserBuilder()
.setSigningKey(secretKey())
.build()
.parseClaimsJws(token)
.getBody()
.getSubject();
}
public boolean validateJwtToken(String token) {
try {
Jwts.parserBuilder().setSigningKey(secretKey()).build().parseClaimsJws(token);
return true;
} catch (SignatureException e) {
JwtUtils.log.error("Invalid JWT signature: {}", e.getMessage());
} catch (MalformedJwtException e) {
JwtUtils.log.error("Invalid JWT token: {}", e.getMessage());
} catch (ExpiredJwtException e) {
JwtUtils.log.error("JWT token is expired: {}", e.getMessage());
} catch (UnsupportedJwtException e) {
JwtUtils.log.error("JWT token is unsupported: {}", e.getMessage());
} catch (IllegalArgumentException e) {
JwtUtils.log.error("JWT claims string is empty: {}", e.getMessage());
}
return false;
}
private SecretKey secretKey() {
return Keys.hmacShaKeyFor(jwtSecret.getBytes());
}
}
WebSecurityConfig
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired private UserDetailsServiceImpl userDetailsService;
@Bean
private static PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(12);
}
@Bean
private static JwtAuthorizationFilter authenticationJwtTokenFilter() {
return new JwtAuthorizationFilter();
}
// @Override
// public void configure(AuthenticationManagerBuilder authenticationManagerBuilder)
// throws Exception {
// authenticationManagerBuilder
// .userDetailsService(userDetailsService)
// .passwordEncoder(WebSecurityConfig.passwordEncoder());
// }
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// Disable CSRF (cross site request forgery)
http.cors()
.and()
.csrf()
.disable()
// No session will be created or used by spring security
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/v1/auth/**", "/confirm-account")
.permitAll()
.anyRequest()
.authenticated();
// Apply JWT
http.addFilterBefore(
WebSecurityConfig.authenticationJwtTokenFilter(),
UsernamePasswordAuthenticationFilter.class);
}
}
Problem solver ?
@Bean
public JwtAuthorizationFilter authenticationJwtTokenFilter() {
return new JwtAuthorizationFilter();
}
I found out that the private static JwtAuthorizationFilter authenticationJwtTokenFilter() method is calling the issue. If i change that to a public method it is working, although passwordEncoder is privat static and is used in my auth service . Unfortunately it is my plugin which is causing that problem. It changes the code automatically. Does somebody know what to do ?
AuthService class
@Service
public class AuthService implements AuthServiceRepository {
@Autowired private RoleRepository roleRepository;
@Autowired private UserRepository userRepository;
@Autowired private PasswordEncoder encoder;
@Autowired private JwtUtils jwtUtils;
@Autowired private AuthenticationManager authenticationManager;
@Autowired private UserService userService;
@Autowired private EmailService emailService;
@Autowired private ConfirmationTokenRepository confirmationTokenRepository;
@Override
public JwtResponse signUpUser(SignUpRequest signUpRequest, String siteURL)
throws MessagingException, UnsupportedEncodingException {
String accessToken = jwtUtils.generateJwtAccessToken(signUpRequest.getUsername());
String refreshToken = jwtUtils.generateJwtRefreshToken(signUpRequest.getUsername());
Set<Role> roles = SignUpRequest.getRoles(signUpRequest, roleRepository);
AppUser user =
new AppUser(
signUpRequest.getUsername(),
signUpRequest.getEmail(),
encoder.encode(signUpRequest.getPassword()));
user.setRoles(roles);
user.setEnabled(false);
AppUser newUser = userService.saveUser(user);
ConfirmationToken confirmationToken = new ConfirmationToken(user);
confirmationTokenRepository.save(confirmationToken);
emailService.sendVerificationEmail(user, siteURL, confirmationToken.getConfirmationToken());
List<String> userRoles =
newUser.getRoles().stream().map(role -> role.getName().name()).collect(Collectors.toList());
return new JwtResponse(
accessToken,
refreshToken,
user.getId(),
signUpRequest.getUsername(),
signUpRequest.getEmail(),
userRoles);
} ... }
Solution
That's because JwtUtils and WebSecurityConfig are not registered as Spring Beans. Try to add a @Component annotation to the classes.
More about dependency injection in Spring Boot: https://docs.spring.io/spring-boot/docs/current/reference/html/using.html#using.spring-beans-and-dependency-injection
Answered By - user12231094
Answer Checked By - Katrina (JavaFixing Volunteer)