In my spring boot app, I am generating a JWT token during login/signup when login or signup endpoints are hit. Now, I want to use this same jwt token to be passed to all other requests in my project, otherwise without it I get forbidden – access denied message. I’m not able to implement it. Please guide.
This is my Login controller
@Controller
@RequestMapping("/")
public class LoginSignupController {
@Autowired
private UserRepository userRepository;
@Autowired
private AuthenticationService authenticationService;
@Autowired
private JwtService jwtService;
@GetMapping("/")
public String showLoginForm(Model theModel) {
return "login-form";
}
@PostMapping("/processLoginForm")
public String processLoginForm(@Valid @ModelAttribute("user") User user, BindingResult bindingResult,
Model model) {
AuthenticationRequest request = new AuthenticationRequest();
request.setUsername(user.getUsername());
request.setPassword(user.getPassword());
ResponseEntity<AuthenticationResponse> response;
try {
response = ResponseEntity.ok(authenticationService.login(request));
System.out.println(response.getBody().getJwt());
return "redirect:/customer/list";
} catch (Exception e) {
model.addAttribute("error","Sorry! Bad credentials.");
return "login-form";
}
}
@GetMapping("/showSignUpForm")
public String showSignupForm(Model theModel) {
User user = new User();
theModel.addAttribute("user", user);
return "sign-up-form";
}
@PostMapping("/processSignUpForm")
public String processSignUpForm(@Valid @ModelAttribute("user") User user, BindingResult bindingResult,
Model model) {
// validation errors from user Entity
if (bindingResult.hasErrors()) {
for (FieldError error : bindingResult.getFieldErrors()) {
System.out.println(error.getDefaultMessage());
model.addAttribute("error", error.getDefaultMessage());
}
return "sign-up-form";
}
try {
user.setRole("USER");
System.out.println(user.toString());
Optional<User> existingUser = userRepository.findByUsername(user.getUsername());
if(existingUser.isPresent() && existingUser.get() != null){
model.addAttribute("error", "OOPS!! This username is already taken. Please use some other username");
return "sign-up-form";
}
Optional<User> existingUserByEmail = userRepository.findByEmail(user.getEmail());
if(existingUserByEmail.isPresent() && existingUserByEmail.get() != null){
model.addAttribute("error", "OOPS!! There already exists an account with this email.");
return "sign-up-form";
}
String rawPassword = user.getPassword();
if (rawPassword.length() < 6 || rawPassword.length() > 12) {
model.addAttribute("error", "Password must be between 6 and 12 characters");
return "sign-up-form";
}
AuthenticationResponse response = authenticationService.register(user);
model.addAttribute("accountCreated", true);
return "success ";
} catch (Exception e) {
System.out.println("Exception: " + e.getMessage());
model.addAttribute("error", "There already exists an account with this email");
return "sign-up-form";
}
}
}
This is my Customer controller
@Controller
@RequestMapping("/customer")
public class CustomerController {
@Autowired
private CustomerService customerService;
@GetMapping("/list")
public String listCustomer(Model theModel) {
List<Customer> theCustomers = customerService.findAll();
theModel.addAttribute("customers",theCustomers);
return "list-customers";
}
@GetMapping("/showFormForAdd")
public String showFormForAdd(Model theModel) {
Customer theCustomer = new Customer();
theModel.addAttribute("customer",theCustomer);
return "customer-form";
}
@GetMapping("/showFormForDelete")
public String showFormForDelete(@RequestParam("customerId") int theId, Model theModel) {
Optional<Customer> theCustomer = customerService.findById(theId);
if (theCustomer.isPresent()) {
theModel.addAttribute("customer", theCustomer.get());
} else {
theModel.addAttribute("error", "Customer not found");
}
return "delete-form";
}
@PostMapping("/saveCustomer")
public String saveCustomer(@ModelAttribute("customer") Customer theCustomer, Model model) {
String email = theCustomer.getEmail();
Customer existingCustomer = customerService.findByEmail(email);
if(existingCustomer != null && !(existingCustomer.getId()==theCustomer.getId())) {
model.addAttribute("error", "OOPS!! A user with this email already exists.");
return "customer-form";
}
customerService.save(theCustomer);
return "redirect:/customer/list";
}
@GetMapping("/showFormForUpdate")
public String showFormForUpdate(@RequestParam("customerId") int theId, Model theModel) {
Optional<Customer> theCustomer = customerService.findById(theId);
if (theCustomer.isPresent()) {
theModel.addAttribute("customer", theCustomer.get());
} else {
theModel.addAttribute("error", "Customer not found");
}
return "customer-form";
}
@PostMapping("/delete")
public String deleteCustomer(@ModelAttribute("customer") Customer theCustomer) {
customerService.deleteById(theCustomer.getId());
return "redirect:/customer/list";
}
}
This is my security config
@Configuration
@EnableWebSecurity
public class SecurityFilter {
@Autowired
private AuthenticationProvider authenticationProvider;
@Autowired
private JwtAuthenticationFilter jwtAuthenticationFilter;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrfConfig -> csrfConfig.disable())
.sessionManagement(sessionMangConfig -> sessionMangConfig.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authenticationProvider(authenticationProvider)
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeHttpRequests( authConfig -> {
authConfig.requestMatchers("/resources/**").permitAll();
authConfig.requestMatchers(HttpMethod.POST, "/auth/authenticate").permitAll();
authConfig.requestMatchers(HttpMethod.POST, "/auth/register").permitAll();
authConfig.requestMatchers("/error").permitAll();
authConfig.requestMatchers(HttpMethod.GET, "/customer/**").hasAuthority("ROLE_USER");
authConfig.requestMatchers("/**").permitAll();
authConfig.anyRequest().authenticated();
});
return http.build();
}
}
This is my JWTService
@Service
public class JwtService {
@Value("${security.jwt.expiration-minutes}")
private String EXPIRATION_MINUTES;
@Value("${security.jwt.secret-key}")
private String SECRET_KEY;
public String generateToken(User user, Map<String, Object> extraClaims) {
Date issuedAt = new Date(System.currentTimeMillis());
Date expiration = new Date(issuedAt.getTime() + (Long.valueOf(EXPIRATION_MINUTES) * 60 * 1000));
return Jwts.builder()
.setClaims(extraClaims)
.setSubject(user.getUsername())
.setIssuedAt(issuedAt)
.setExpiration(expiration)
.signWith(generateKey(), SignatureAlgorithm.HS256)
.compact();
}
private Key generateKey(){
byte[] secreateAsBytes = Decoders.BASE64.decode(SECRET_KEY);
return Keys.hmacShaKeyFor(secreateAsBytes);
}
public String extractUsername(String jwt) {
return extractAllClaims(jwt).getSubject();
}
private Claims extractAllClaims(String jwt) {
return Jwts.parserBuilder().setSigningKey(generateKey()).build()
.parseClaimsJws(jwt).getBody();
}
}
This is my jwt filter
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private JwtService jwtService;
@Autowired
private UserRepository userRepository;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// 1 obtain header that contains jwt
String authHeader = request.getHeader("Authorization"); // Bearer jwt
if(authHeader == null || !authHeader.startsWith("Bearer ")){
filterChain.doFilter(request, response);
return;
}
// 2 obtain jwt token
String jwt = authHeader.split(" ")[1];
// 3 obtain subject/username in jwt
String username = jwtService.extractUsername(jwt);
// 4 set authenticate object inside our secuirty context
User user = userRepository.findByUsername(username).get();
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
username, null, user.getAuthorities()
);
SecurityContextHolder.getContext().setAuthentication(authToken);
// 5 execute rest of the filters
filterChain.doFilter(request, response);
}
}
I’ve been recommended to use java script code to add authorization headers, but since all my apis are called from controller backend, i don’t want to use javascript in my project.