I am working on the Spring boot rest API with JWT Token to authenticate the requests. I have made multiple entries of Admin in DB and passing name as username, password as password in Basic Auth, but ends up getting SecurityContextHolder.getContext().getAuthentication() as null.
@PostMapping("/admins/login")
public ResponseEntity<Admin> getLoggedInAdminDetailsHandler(Authentication auth) {
Admin admin = adminDao.findByEmail(auth.getName())
.orElseThrow(() -> new BadCredentialsException("Invalid Username or password"));
return new ResponseEntity<>(admin, HttpStatus.ACCEPTED);
}
with Configuration as :
package com.olivestays.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
@Configuration
public class AppConfig {
@Bean
public SecurityFilterChain springSecurityConfiguration(HttpSecurity http) throws Exception {
http
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf().disable()
.authorizeHttpRequests()
.requestMatchers(HttpMethod.POST, "/staywell/customers/register").permitAll()
.requestMatchers(HttpMethod.POST, "/staywell/admins/register").permitAll()
.requestMatchers(HttpMethod.POST, "/staywell/hotels/register").permitAll()
.requestMatchers(HttpMethod.POST, "/staywell/customers/login").permitAll()
.requestMatchers(HttpMethod.POST, "/staywell/admins/login").permitAll()
.requestMatchers(HttpMethod.POST, "/staywell/hotels/login").permitAll()
.requestMatchers(HttpMethod.POST, "/staywell/rooms/add").hasRole("HOTEL")
.requestMatchers(HttpMethod.PUT, "/staywell/customers/update").hasRole("CUSTOMER")
.requestMatchers(HttpMethod.GET, "/staywell/customers/getToBeDeletedAccounts").hasRole("ADMIN")
.anyRequest()
.authenticated()
.and()
.addFilterAfter(new JwtTokenGeneratorFilter(), BasicAuthenticationFilter.class)
.addFilterBefore(new JwtTokenValidatorFilter(), BasicAuthenticationFilter.class)
.formLogin()
.and()
.httpBasic();
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
I have made multiple entries in DB for Admin, and when I am passing Username and Password in Basic Auth, it is showing 401 Unauthorized.
Admin request:
package com.olivestays.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;
import com.olivestays.enums.Role;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
public class Admin {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) @JsonProperty(access = Access.READ_ONLY)
private Integer adminId;
@NotNull @NotEmpty @NotBlank
private String name;
@NotNull @NotEmpty @NotBlank
@Column(unique = true) @Email
private String email;
@NotNull @NotEmpty @NotBlank
private String mobile;
@NotNull @NotEmpty @NotBlank
@JsonProperty(access = Access.WRITE_ONLY)
private String password;
@JsonIgnore
@Enumerated(EnumType.STRING)
private Role role;
}
What should I do to resolve this issue?
Answers
It seems like you are trying to authenticate users using Basic Authentication and then retrieve the authenticated user details in your /admins/login
endpoint. However, the issue you're facing is that SecurityContextHolder.getContext().getAuthentication()
is returning null
in your controller method.
The reason for this is that your /admins/login
endpoint is not protected by Spring Security, so the Authentication
object is not populated with the authenticated user's details.
To resolve this issue, you need to make sure that your /admins/login
endpoint is protected by Spring Security so that the Authentication
object is populated after successful authentication. You can do this by removing the .permitAll()
configuration for this endpoint in your security configuration and ensuring that Basic Authentication is enforced.
Here's the updated security configuration:
@Bean
public SecurityFilterChain springSecurityConfiguration(HttpSecurity http) throws Exception {
http
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf().disable()
.authorizeHttpRequests()
.requestMatchers(HttpMethod.POST, "/staywell/customers/register").permitAll()
.requestMatchers(HttpMethod.POST, "/staywell/admins/register").permitAll()
.requestMatchers(HttpMethod.POST, "/staywell/hotels/register").permitAll()
.requestMatchers(HttpMethod.POST, "/staywell/customers/login").permitAll()
.requestMatchers(HttpMethod.POST, "/staywell/admins/login").authenticated() // Require authentication
.requestMatchers(HttpMethod.POST, "/staywell/hotels/login").permitAll()
.requestMatchers(HttpMethod.POST, "/staywell/rooms/add").hasRole("HOTEL")
.requestMatchers(HttpMethod.PUT, "/staywell/customers/update").hasRole("CUSTOMER")
.requestMatchers(HttpMethod.GET, "/staywell/customers/getToBeDeletedAccounts").hasRole("ADMIN")
.anyRequest()
.authenticated()
.and()
.addFilterAfter(new JwtTokenGeneratorFilter(), BasicAuthenticationFilter.class)
.addFilterBefore(new JwtTokenValidatorFilter(), BasicAuthenticationFilter.class)
.formLogin()
.and()
.httpBasic();
return http.build();
}
With this configuration, your /admins/login
endpoint will require authentication, and the Authentication
object should be populated with the authenticated user's details when the request is made.
Additionally, ensure that your Basic Authentication credentials (username and password) are being passed correctly in the request headers.