Issue
We have login page where user will enter user credentials and internally calling one more authenticate service where need to store this token and pass to all REST controllers.I tried configuring bean scope within this class and but getting below exception .we are using spring 5.x;
com.config.CustomAuthenticationProvider sessionScopedBean CustomAuthenticationProvider UserDetails !!!null Jun 20, 2020 11:52:37 AM org.apache.catalina.core.StandardWrapperValve invoke
java.lang.ClassCastException: org.springframework.beans.factory.support.NullBean cannot be cast to com.utils.UserDetails
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
private Logger logger = Logger.getLogger(getClass().getName());
private UserDetails userDetails;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String userName = authentication.getName();
String passWord = authentication.getCredentials().toString();
Result response;
try {
response = CustomClient.authenticate(userName, passWord);
} catch (Exception e) {
throw new BadCredentialsException("system authentication failed");
}
if (response != null && response.getToken() != null) {
//need to store this response.getToken() in session
logger.info("Token: " + response.getToken());
userDetails= new UserDetails();
userDetails.setToken(response.getToken());
logger.info("Authentication SUCCESS !!!");
return new UsernamePasswordAuthenticationToken(userName, passWord, Collections.emptyList());
} else {
logger.info("Authentication FAILED...");
throw new BadCredentialsException("authentication failed");
}
}
@Bean
@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public UserDetails sessionScopedBean() {
logger.info(" UserDetails !!!"+userDetails);
return userDetails;
}
@Override
public boolean supports(Class<?> auth) {
return auth.equals(UsernamePasswordAuthenticationToken.class);
}
}
Solution
Why do you want to create session scoped UserDetails
bean in the first place? You can already achieve it by doing the following:
@GetMapping("/abc")
public void getUserProfile(@AuthenticationPrincipal UserDetails user ) {
...
}
or
@GetMapping("/abc")
public void getUserProfile() {
SecurityContext securityContext = SecurityContextHolder.getContext();
UserDetails user = (UserDetails) securityContext.getAuthentication().getPrincipal();
}
Note:
Behind the scene, spring uses HttpSessionSecurityContextRepository
to store your SecurityContext
in http session and restore it back on every request
And the Updated CustomAuthenticationProvider
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
private Logger logger = Logger.getLogger(getClass().getName());
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String userName = authentication.getName();
String passWord = authentication.getCredentials().toString();
Result response;
try {
response = CustomClient.authenticate(userName, passWord);
} catch (Exception e) {
throw new BadCredentialsException("system authentication failed");
}
if (response != null && response.getToken() != null) {
//need to store this response.getToken() in session
logger.info("Token: " + response.getToken());
UserDetails userDetails= new UserDetails();
userDetails.setToken(response.getToken());
logger.info("Authentication SUCCESS !!!");
return new UsernamePasswordAuthenticationToken(userDetails, passWord, Collections.emptyList());
} else {
logger.info("Authentication FAILED...");
throw new BadCredentialsException("authentication failed");
}
}
@Override
public boolean supports(Class<?> auth) {
return auth.equals(UsernamePasswordAuthenticationToken.class);
}
}
Answered By - Kavithakaran Kanapathippillai