Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Oauth2 - reload principals

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)?

like image 502
IgorekPotworek Avatar asked Nov 19 '15 12:11

IgorekPotworek


Video Answer


2 Answers

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");
    }

}
like image 104
Cataclysm Avatar answered Oct 12 '22 20:10

Cataclysm


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);
like image 26
hiren Avatar answered Oct 12 '22 18:10

hiren