I have implemented OAuth2 password grant with spring security module. I add own implementation of UserDetails and UserDetailsService (jdbc). I inject User to my controllers with:
@AuthenticationPrincipal User user
where User is my implementation of UserDetails . Now I want to add posibility of changing User data without refreshing token.
I try to refresh principals with:
User updatedUser = ...
Authentication newAuth = new UsernamePasswordAuthenticationToken(updatedUser, updatedUser.getPassword(), updatedUser.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(newAuth);
But it doesn't work, when I invoke another controller method it returns old User object.
Is there any way to change User data without refreshing token? Is any solution to make spring security to always load user data from database (not from Cache)?
I found a way to achieve this from this answer. I created HttpSessionSecurityContextRepository
@Bean
public ReloadUserPerRequestHttpSessionSecurityContextRepository userDetailContextRepository() {
return new ReloadUserPerRequestHttpSessionSecurityContextRepository();
}
and add this to my HttpSecurity
from the class of WebSecurityConfigurerAdapter
as
.and()
.csrf()
.csrfTokenRepository(csrfTokenRepository())
.and()
.securityContext()
.securityContextRepository(userDetailContextRepository())
Below is sample codes to fetch updated user informations at every requests. You can fetch from your API server or user-info-url
of your oauth2 configuration.
public class ReloadUserPerRequestHttpSessionSecurityContextRepository extends HttpSessionSecurityContextRepository {
@Override
public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {
SecurityContext context = super.loadContext(requestResponseHolder);
Authentication auth = context.getAuthentication();
if (auth != null && auth instanceof OAuth2Authentication) {
OAuth2Authentication oldAuth = (OAuth2Authentication) auth;
if (oldAuth.getPrincipal() instanceof LinkedHashMap) {
createNewUserDetailsFromPrincipal(oldAuth.getPrincipal());
}
context.setAuthentication(oldAuth);
}
return context;
}
@SuppressWarnings("unchecked")
private void createNewUserDetailsFromPrincipal(Object principal) {
LinkedHashMap<String, Object> userDetailInfo = (LinkedHashMap<String, Object>) principal;
// fetch User informations from your API server or your database or
// 'user-info-url' of oauth2 endpoint
userDetailInfo.put("name", "EDITED_USER_NAME");
}
}
I am not sure but this is just a guess, I think you you also need to clear SecurityContext
.
First clear security context with SecurityContextHolder.clearContext();
then your code to set authentication
Authentication newAuth = new UsernamePasswordAuthenticationToken(updatedUser, updatedUser.getPassword(), updatedUser.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(newAuth);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With