I have a problem about getting user entity values from the authenticated process. After I sign in, I want to logout.
I have a problem to get authenticated user value as it is null.
I got this error when I logout
java.lang.NullPointerException: Cannot invoke "com.app.modal.User.getId()" because "this.user" is null
How can I fix it?
Here is my CurrentUser class shown below.
@Target({ElementType.PARAMETER, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@AuthenticationPrincipal
public @interface CurrentUser {
}
Here is my AuthController class shown below.
@RestController
@RequestMapping("/api/auth")
public class AuthController {
UserService userService;
RoleService roleService;
RefreshTokenService refreshTokenService;
AuthenticationManager authenticationManager;
PasswordEncoder encoder;
JwtUtils jwtUtils;
@Autowired
public AuthController(UserService userService, RoleService roleService, RefreshTokenService refreshTokenService,
AuthenticationManager authenticationManager, PasswordEncoder encoder, JwtUtils jwtUtils) {
this.userService = userService;
this.roleService = roleService;
this.refreshTokenService = refreshTokenService;
this.authenticationManager = authenticationManager;
this.encoder = encoder;
this.jwtUtils = jwtUtils;
}
@PostMapping("/signin")
public ResponseEntity<?> authenticateUser(@RequestBody LoginRequest loginRequest) {
LOGGER.info("AuthController | authenticateUser is started");
String username = loginRequest.getUsername();
String password = loginRequest.getPassword();
LOGGER.info("AuthController | authenticateUser | username : " username);
LOGGER.info("AuthController | authenticateUser | password : " password);
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(username,password);
LOGGER.info("AuthController | authenticateUser | usernamePasswordAuthenticationToken : " usernamePasswordAuthenticationToken.toString());
Authentication authentication = authenticationManager.authenticate(usernamePasswordAuthenticationToken);
SecurityContextHolder.getContext().setAuthentication(authentication);
CustomUserDetails userDetails = (CustomUserDetails) authentication.getPrincipal();
String jwt = jwtUtils.generateJwtToken(userDetails);
LOGGER.info("AuthController | authenticateUser | jwt : " jwt);
List<String> roles = userDetails.getAuthorities().stream().map(item -> item.getAuthority())
.collect(Collectors.toList());
LOGGER.info("AuthController | authenticateUser | roles : " roles.toString());
RefreshToken refreshToken = refreshTokenService.createRefreshToken(userDetails.getId());
LOGGER.info("AuthController | authenticateUser | refreshToken : " refreshToken.toString());
JWTResponse jwtResponse = new JWTResponse();
jwtResponse.setEmail(userDetails.getEmail());
jwtResponse.setUsername(userDetails.getUsername());
jwtResponse.setId(userDetails.getId());
jwtResponse.setToken(jwt);
jwtResponse.setRefreshToken(refreshToken.getToken());
jwtResponse.setRoles(roles);
LOGGER.info("AuthController | authenticateUser | jwtResponse : " jwtResponse.toString());
return ResponseEntity.ok(jwtResponse);
}
@PostMapping("/logout")
public ResponseEntity<?> logoutUser(@CurrentUser CustomUserDetails user) {
LOGGER.info("AuthController | logoutUser is started");
int userId = user.getId();
int deletedValue = refreshTokenService.deleteByUserId(userId);
if(deletedValue == 1){
return ResponseEntity.ok(new MessageResponse("Log out successful!"));
}else{
return ResponseEntity.ok(new MessageResponse("You're already logout"));
}
}
}
Here is my CustomDetails class shown below.
public class CustomUserDetails implements UserDetails {
private User user;
public CustomUserDetails(User user) {
this.user = user;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Set<Role> roles = user.getRoles();
List<SimpleGrantedAuthority> authories = new ArrayList<>();
for (Role role : roles) {
authories.add(new SimpleGrantedAuthority(role.getName().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 int getId(){
return user.getId();
}
public String getEmail(){
return user.getEmail();
}
}
Here is my CustomUserDetailsService class shown below.
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
UserService userService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userService.findByUsername(username)
.orElseThrow(() -> new UserNotFoundException("User Name " username " not found"));
return new CustomUserDetails(user);
}
}
Here is my WebSecurityConfig class shown below.
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(
prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
CustomUserDetailsService customUserDetailsService;;
private AuthEntryPointJwt unauthorizedHandler;
JwtUtils jwtUtils;
@Autowired
public WebSecurityConfig(CustomUserDetailsService customUserDetailsService, AuthEntryPointJwt unauthorizedHandler,JwtUtils jwtUtils) {
this.customUserDetailsService = customUserDetailsService;
this.unauthorizedHandler = unauthorizedHandler;
this.jwtUtils = jwtUtils;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(customUserDetailsService).passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and()
.csrf().disable()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests().antMatchers("/api/auth/**").permitAll()
.antMatchers("/api/pages/**").permitAll()
.anyRequest().authenticated();
http.addFilterBefore(authenticationJwtTokenFilter(jwtUtils,customUserDetailsService), UsernamePasswordAuthenticationFilter.class);
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public AuthTokenFilter authenticationJwtTokenFilter(JwtUtils jwtUtils, CustomUserDetailsService customUserDetailsService) {
return new AuthTokenFilter(jwtUtils, customUserDetailsService);
}
}
CodePudding user response:
Just one change based on your repo : https://github.com/Rapter1990/SpringBootRefreshTokenJWT
- AuthController.java
import java.security.Principal;
import springfox.documentation.annotations.ApiIgnore;
...
@PostMapping("/logout")
public ResponseEntity<?> logoutUser(@ApiIgnore Principal principalUser) {
LOGGER.info("AuthController | logoutUser is started");
//Since in AuthFilter You are setting UsernamePasswordAuthenticationToken, we will retrieve and cast
Object principal = ((UsernamePasswordAuthenticationToken) principalUser).getPrincipal();
CustomUserDetails user = (CustomUserDetails) principal;
int userId = user.getId();
int deletedValue = refreshTokenService.deleteByUserId(userId);
if(deletedValue == 1){
return ResponseEntity.ok(new MessageResponse("Log out successful!"));
}else{
return ResponseEntity.ok(new MessageResponse("You're already logout"));
}
}