I have a problem with using JWT tokens in Spring Security. I have configured a Spring configuration file using a JWT filter in it. But the difficulty arises in that I can only register a user and receive a token from him, with which I can then make other requests. But if I don’t create a user, but just want to authenticate via login/password to get a token, then I’m getting weird errors in POSTMAN
Configuration file:
@Configuration
@EnableWebSecurity
public class SpringConfiguration {
private final PersonUserDetailsService personUserDetailsService;
private final JWTFilter jwtFilter;
@Autowired
public SpringConfiguration(PersonUserDetailsService personUserDetailsService, JWTFilter jwtFilter) {
this.personUserDetailsService = personUserDetailsService;
this.jwtFilter = jwtFilter;
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf((csrf) -> csrf.disable())
.cors((cors) -> cors.disable())
.authorizeHttpRequests((request) -> request
.requestMatchers("/auth", "/registration", "/test").permitAll()
.requestMatchers("/admin").hasRole("ADMIN")
.anyRequest().hasAnyRole("USER", "ADMIN")
)
.formLogin((login) -> login
.loginPage("/login").loginProcessingUrl("/auth")
)
.logout((logout) -> logout.permitAll().logoutUrl("/logout"))
.sessionManagement((sessionManagement) -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(personUserDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
public static PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
}
JWT Filter class
@Component
public class JWTFilter extends OncePerRequestFilter {
private final JWTUtil jwtUtil;
private final PersonUserDetailsService personUserDetailsService;
@Autowired
public JWTFilter(JWTUtil jwtUtil, PersonUserDetailsService personUserDetailsService) {
this.jwtUtil = jwtUtil;
this.personUserDetailsService = personUserDetailsService;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String authHeader = request.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
String jwt = authHeader.substring(7);
if (jwt.isBlank()) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Невалидный JWT токен");
} else {
try {
String username = jwtUtil.validateToken(jwt);
UserDetails userDetails = personUserDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
if (SecurityContextHolder.getContext().getAuthentication() == null) {
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
} catch (JWTVerificationException e) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Невалидный JWT токен");
}
}
}
filterChain.doFilter(request, response);
}
}
Controller class Auth:
@RestController
public class AuthController {
private final JWTUtil jwtUtil;
private final AuthenticationManager authenticationManager;
@Autowired
public AuthController(JWTUtil jwtUtil, AuthenticationManager authenticationManager) {
this.jwtUtil = jwtUtil;
this.authenticationManager = authenticationManager;
}
@PostMapping("/auth")
public ResponseEntity<?> login(@RequestBody PersonDto personDto) {
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(personDto.getUsername(), personDto.getPassword());
try {
authenticationManager.authenticate(authenticationToken);
} catch (BadCredentialsException e) {
return new ResponseEntity<>(new BodyResponse(HttpStatus.BAD_REQUEST.toString(), "Неправильный логин или пароль"), HttpStatus.BAD_REQUEST);
}
String jwt = "Token: " + jwtUtil.generateToken(personDto.getUsername());
return new ResponseEntity<>(new BodyResponse(HttpStatus.OK.toString(), jwt), HttpStatus.OK);
}
}
And registration class:
@RestController
public class CrudController {
private final PersonService personService;
private final JWTUtil jwtUtil;
private final ModelMapper modelMapper;
@Autowired
public CrudController(PersonService personService, JWTUtil jwtUtil, ModelMapper modelMapper) {
this.personService = personService;
this.jwtUtil = jwtUtil;
this.modelMapper = modelMapper;
}
@GetMapping("/test")
public String mainPage() {
return "test";
}
@GetMapping("/admin")
public String adminPage() {
return "admin response";
}
@PostMapping("/registration")
public ResponseEntity<?> registration(@RequestBody PersonDto personDto) {
Person person = convertPersonDtoToPerson(personDto);
personService.create(person);
String jwt = "Token: " + jwtUtil.generateToken(person.getUsername());
return new ResponseEntity<>(new BodyResponse(HttpStatus.CREATED.toString(), jwt), HttpStatus.CREATED);
}
public Person convertPersonDtoToPerson(PersonDto personDto) {
return modelMapper.map(personDto, Person.class);
}
}
This is the error I get when I try to authenticate
enter image description here
Also, if you want to reproduce, you can get it from git: https://github.com/slizokav/CrudSecurityRestApi.git