im trying to do simple JWT authorization application like here: https://github.com/eazybytes/spring-security/tree/main/section9/springsecsection9
but my authentication in JWTTokengeneratorFilter is null and I don't have a idea why.
I can do it the old way with attemptAuthentication methods etc. and it works great, but I wanted to do it like in this tutorial and I can't se what am I doing wrong here.
Can some one tell me what am I missing here?
AuthSecurityConfig.class
@Configuration
public class AuthSecurityConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain defaultFilterChain(HttpSecurity http) throws Exception {
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.cors().configurationSource(request -> {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(Collections.singletonList("http://localhost:3000"));
config.setAllowedMethods(Collections.singletonList("*"));
config.setAllowCredentials(true);
config.setAllowedHeaders(Collections.singletonList("*"));
config.setExposedHeaders(List.of("Authorization"));
config.setMaxAge(3600L);
return config;
}).and().csrf().disable()
.addFilterBefore(new JWTTokenValidatorFilter(), BasicAuthenticationFilter.class)
.addFilterAfter(new JWTTokenGeneratorFilter(), BasicAuthenticationFilter.class)
.authorizeHttpRequests((auth) -> auth
.antMatchers( "/login").permitAll()
.antMatchers("/register").authenticated()
.antMatchers("/user").permitAll()
).httpBasic(Customizer.withDefaults());
return http.build();
}
}
JWTTokenGeneratorFilter.class
public class JWTTokenGeneratorFilter extends OncePerRequestFilter {
@Override
public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); // here authentication is null
if (null != authentication) {
SecretKey key = Keys.hmacShaKeyFor(SecurityConstants.JWT_KEY.getBytes(StandardCharsets.UTF_8));
String jwt = Jwts.builder().setIssuer("Pollongz").setSubject("JWT Token")
.claim("username", authentication.getName())
.claim("authorities", populateAuthorities(authentication.getAuthorities()))
.setIssuedAt(new Date())
.setExpiration(new Date((new Date()).getTime() + 300000000))
.signWith(key).compact();
response.setHeader(SecurityConstants.JWT_HEADER, jwt);
}
chain.doFilter(request, response);
}
@Override
protected boolean shouldNotFilter(HttpServletRequest request) {
return !request.getServletPath().equals("/login");
}
private String populateAuthorities(Collection<? extends GrantedAuthority> collection) {
Set<String> authoritiesSet = new HashSet<>();
for (GrantedAuthority authority : collection) {
authoritiesSet.add(authority.getAuthority());
}
return String.join(",", authoritiesSet);
}
}
JWTTokenValidatorFilter.class
public class JWTTokenValidatorFilter extends OncePerRequestFilter {
@Override
public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
String jwtToken = request.getHeader(SecurityConstants.JWT_HEADER);
if (null != jwtToken) {
try {
SecretKey key = Keys.hmacShaKeyFor(
SecurityConstants.JWT_KEY.getBytes(StandardCharsets.UTF_8));
Claims claims = Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(jwtToken)
.getBody();
String username = String.valueOf(claims.get("username"));
String authorities = (String) claims.get("authorities");
Authentication auth = new UsernamePasswordAuthenticationToken(username,null,
AuthorityUtils.commaSeparatedStringToAuthorityList(authorities));
SecurityContextHolder.getContext().setAuthentication(auth);
} catch (Exception e) {
throw new BadCredentialsException("Invalid Token received!");
}
}
chain.doFilter(request, response);
}
@Override protected boolean shouldNotFilter(HttpServletRequest request) {
return request.getServletPath().equals("/login"); }
}
AuthUserDetails.class
@Service
public class AuthUserDetails implements UserDetailsService {
private final UserRepository userRepository;
@Autowired
public AuthUserDetails(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByEmail(username);
if (user == null) {
throw new UsernameNotFoundException("User details not found for the user : " + username);
}
return new UserDetailsSecurity(user);
}
}
UserDetailsSecurity.class
public class UserDetailsSecurity implements UserDetails {
private final User user;
public UserDetailsSecurity(User user) {
this.user = user;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Stream.of(user.getRoles().split(","))
.map(role -> "ROLE_" + role.toUpperCase())
.collect(Collectors.toList())
.stream()
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getEmail();
}
//.. other override methods
}
AuthApplication.class
@SpringBootApplication
@ComponentScans({ @ComponentScan("com.vue.auth.controller"), @ComponentScan("com.vue.auth.config")})
@EnableJpaRepositories("com.vue.auth.repository")
@EntityScan("com.vue.auth.model")
@EnableWebSecurity(debug = true)
public class AuthApplication {
public static void main(String[] args) {
SpringApplication.run(AuthApplication.class, args);
}
}