I have a problem implementing login service test based on Spring Security in Spring Boot. I tried to implement login method in AuthService and then wrote its service method but I got the issue.
Here is the issue shown below
java.lang.NullPointerException: Cannot invoke "org.springframework.security.core.Authentication.getPrincipal()" because "auth" is null
How can I fix it?
Here is JwtUserDetails class shown below
@RequiredArgsConstructor
public class JwtUserDetails implements UserDetails {
private final User user;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
UserRole roles = user.getUserRole();
List<SimpleGrantedAuthority> authories = new ArrayList<>();
authories.add(new SimpleGrantedAuthority(roles.name()));
return authories;
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
public long getId(){
return user.getId();
}
public String getEmail(){
return user.getEmail();
}
}
Here is AuthService shown below
@Service
@RequiredArgsConstructor
public class AuthService{
private final AuthenticationManager authenticationManager;
private final JwtTokenProvider jwtTokenProvider;
private final PasswordEncoder passwordEncoder;
private final RefreshTokenService refreshTokenService;
@Override
@Transactional
public AuthResponse login(AdminLoginRequest loginRequest) {
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword());
Authentication auth = authenticationManager.authenticate(authToken);
SecurityContextHolder.getContext().setAuthentication(auth);
JwtUserDetails userDetails = (JwtUserDetails) auth.getPrincipal();
String accessToken = jwtTokenProvider.generateJwtToken(auth);
List<String> roles = userDetails.getAuthorities().stream().map(item -> item.getAuthority())
.collect(Collectors.toList());
RefreshToken refreshToken = refreshTokenService.createRefreshToken(userDetails.getId());
Date expiryDate = new Date(new Date().getTime() + EXPIRES_IN);
return AuthResponse.builder()
.username(userDetails.getUsername())
.accessToken("Bearer " + accessToken)
.roles(roles)
.refreshToken(refreshToken.getToken())
.message("success")
.expireDate(expiryDate.getTime())
.build();
}
Here is the AuthServiceTest shown below
@ExtendWith(MockitoExtension.class)
class AuthServiceTest{
@Mock
private UserRepository userRepository;
@Mock
private OrganizationRepository organizationRepository;
@InjectMocks
private AuthServiceImpl authService;
@Mock
private PasswordEncoder passwordEncoder;
@Mock
private UserMapper userMapper;
@Mock
private RefreshTokenService refreshTokenService;
@Mock
private JwtTokenProvider jwtTokenProvider;
@Mock
private AuthenticationManager authenticationManager;
@Test
void shouldLogin() {
// Given
AdminLoginRequest loginRequest = getLoginRequest();
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword());
Authentication auth = authenticationManager.authenticate(authToken);
SecurityContextHolder.getContext().setAuthentication(auth);
JwtUserDetails userDetails = (JwtUserDetails) auth.getPrincipal();
AuthResponse authResponse = AuthResponse.builder()
.expireDate(new Date().getTime() + 120000)
.refreshToken("refreshToken")
.message("success")
.accessToken("Bearer access-token")
.roles(List.of("ADMIN","SUPER_ADMIN"))
.username("adminUsername")
.build();
RefreshToken refreshToken = RefreshToken.builder()
.token(authResponse.getAccessToken())
.build();
// when
when(authenticationManager.authenticate(any())).thenReturn(authToken);
when(jwtTokenProvider.generateJwtToken(any())).thenReturn(authResponse.getAccessToken());
when(refreshTokenService.createRefreshToken(eq(userDetails.getId()))).thenReturn(refreshToken);
AuthResponse authResponseActual = authService.login(loginRequest);
// then
assertThat(authResponse).isNotNull();
assertEquals(authResponseActual.getAccessToken(), authResponse.getAccessToken());
assertEquals(authResponseActual.getUsername(), authResponse.getUsername());
assertEquals(authResponseActual.getRefreshToken(), authResponse.getRefreshToken());
assertEquals(authResponseActual.getRefreshToken(), authResponse.getRefreshToken());
assertEquals(authResponseActual.getMessage(), authResponse.getMessage());
}
Here is the getLoginRequest shown below
public AdminLoginRequest getLoginRequest() {
return AdminLoginRequest.builder()
.username("adminUsername")
.password("adminPassword")
.build();
}
Revised
Here is the test method shown below
@Test
void shouldLogin() {
// Given
AdminLoginRequest loginRequest = new UserBuilder().getLoginRequest();
User user = User.builder()
.username(loginRequest.getUsername())
.password(loginRequest.getPassword())
.userRole(UserRole.ROLE_ADMIN)
.build();
user.setId(1L);
UsernamePasswordAuthenticationToken authToken =
new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword());
SecurityContextHolder.getContext().setAuthentication(auth);
AuthResponse authResponse = AuthResponse.builder()
.expireDate(new Date().getTime() + 120000)
.refreshToken("refreshToken")
.message("success")
.accessToken("Bearer access-token")
.roles(List.of("ADMIN","SUPER_ADMIN"))
.username("adminUsername")
.build();
RefreshToken refreshToken = RefreshToken.builder()
.token(authResponse.getAccessToken())
.build();
// when
when(authenticationManager.authenticate(eq(authToken))).thenReturn(authToken);
when(auth.getPrincipal()).thenReturn(jwtUserDetails);
when(jwtTokenProvider.generateJwtToken(eq(auth))).thenReturn(authResponse.getAccessToken());
when(refreshTokenService.createRefreshToken(eq(jwtUserDetails.getId()))).thenReturn(refreshToken);
AuthResponse authResponseActual = authService.login(loginRequest);
// then
assertThat(authResponse).isNotNull();
assertEquals(authResponseActual.getAccessToken(), authResponse.getAccessToken());
assertEquals(authResponseActual.getUsername(), authResponse.getUsername());
assertEquals(authResponseActual.getRefreshToken(), authResponse.getRefreshToken());
assertEquals(authResponseActual.getRefreshToken(), authResponse.getRefreshToken());
assertEquals(authResponseActual.getMessage(), authResponse.getMessage());
}
Here is the issue shown below
java.lang.ClassCastException: class java.lang.String cannot be cast to class com.ays.backend.user.security.JwtUserDetails (java.lang.String is in module java.base of loader 'bootstrap'; com.ays.backend.user.security.JwtUserDetails is in unnamed module of loader 'app')
Here is the answer shown below
@Test
void shouldLogin() {
// Given
AdminLoginRequest loginRequest = new UserBuilder().getLoginRequest();
User user = User.builder()
.username(loginRequest.getUsername())
.password(loginRequest.getPassword())
.userRole(UserRole.ROLE_ADMIN)
.build();
user.setId(1L);
UsernamePasswordAuthenticationToken authToken =
new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword());
JwtUserDetails jwtUserDetails = new JwtUserDetails(user);
Authentication auth = new UsernamePasswordAuthenticationToken(jwtUserDetails, null);
SecurityContextHolder.getContext().setAuthentication(auth);
JwtUserDetails userDetails = (JwtUserDetails) auth.getPrincipal();
AuthResponse authResponse = AuthResponse.builder()
.expireDate(new Date().getTime() + 120000)
.refreshToken("refreshToken")
.message("success")
.accessToken("access-token")
.roles(List.of("ADMIN","SUPER_ADMIN"))
.username("adminUsername")
.build();
RefreshToken refreshToken = RefreshToken.builder()
.token(authResponse.getRefreshToken())
.build();
// when
when(authenticationManager.authenticate(eq(authToken)))
.thenReturn(auth);
when(jwtTokenProvider.generateJwtToken(eq(auth))).thenReturn(authResponse.getAccessToken());
when(refreshTokenService.createRefreshToken(eq(userDetails.getId()))).thenReturn(refreshToken);
AuthResponse authResponseActual = authService.login(loginRequest);
// then
assertThat(authResponse).isNotNull();
assertEquals(authResponseActual.getAccessToken().substring(7), authResponse.getAccessToken());
assertEquals(authResponseActual.getUsername(), authResponse.getUsername());
assertEquals(authResponseActual.getRefreshToken(), authResponse.getRefreshToken());
assertEquals(authResponseActual.getMessage(), authResponse.getMessage());
}