Adding additional details to principal object stored in spring security context

JavaSpringSpring Security

Java Problem Overview


I am using Spring 3.0 and Spring Security 3. I am able to authenticate a user against a database using Spring Security. Using:

SecurityContextHolder.getContext().getAuthentication().getPrincipal()

I am able to retrieve username of the current logged in user. I wish to add additional details like user id and the module accesses to the principal object stored in Spring Security context so that I can retrieve it later. How can I add additional details to the principal object and then how can I retrieve it later on a jsp or java class. Please provide an appropriate code snippet if possible.

Edit: I am using JDBC to access my database.

Thanks in advance.

Java Solutions


Solution 1 - Java

Here is what you need:

  1. Extend spring User (org.springframework.security.core.userdetails.User) class and what ever properties you need.
  2. Extend spring UserDetailsService (org.springframework.security.core.userdetails.UserDetailsService) and fill the above object. Override loadUserByUsername and return your extended user class
  3. Set your custom UserDetailsService in AuthenticationManagerBuilder

For example

public class CurrentUser extends User{

   //This constructor is a must
	public CurrentUser(String username, String password, boolean enabled, boolean accountNonExpired,
			boolean credentialsNonExpired, boolean accountNonLocked,
			Collection<? extends GrantedAuthority> authorities) {
		super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
	}
    //Setter and getters are required
    private String firstName;
    private String lastName;

}

The Custom userdetails could be:

@Service("userDetailsService")
public class CustomUserDetailsService implements UserDetailsService {

@Override
public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException {

    //Try to find user and its roles, for example here we try to get it from database via a DAO object
   //Do not confuse this foo.bar.User with CurrentUser or spring User, this is a temporary object which holds user info stored in database
	foo.bar.User user = userDao.findByUserName(username);
    
    //Build user Authority. some how a convert from your custom roles which are in database to spring GrantedAuthority
	List<GrantedAuthority> authorities = buildUserAuthority(user.getUserRole());

    //The magic is happen in this private method !
	return buildUserForAuthentication(user, authorities);

}


//Fill your extended User object (CurrentUser) here and return it
private User buildUserForAuthentication(foo.bar.User user, 
List<GrantedAuthority> authorities) {
	String username = user.getUsername();
	String password = user.getPassword();
	boolean enabled = true;
	boolean accountNonExpired = true;
	boolean credentialsNonExpired = true;
	boolean accountNonLocked = true;

	return new CurrentUser(username, password, enabled, accountNonExpired, credentialsNonExpired,
			accountNonLocked, authorities);
   //If your database has more information of user for example firstname,... You can fill it here 
  //CurrentUser currentUser = new CurrentUser(....)
  //currentUser.setFirstName( user.getfirstName() );
  //.....
  //return currentUser ;
}

private List<GrantedAuthority> buildUserAuthority(Set<UserRole> userRoles) {

	Set<GrantedAuthority> setAuths = new HashSet<GrantedAuthority>();

	// Build user's authorities
	for (UserRole userRole : userRoles) {
		setAuths.add(new SimpleGrantedAuthority(userRole.getRole()));
	}

	return new ArrayList<GrantedAuthority>(setAuths);
}

}

Configure the spring security context

@Configuration
@EnableWebSecurity
@PropertySource("classpath://configs.properties")
public class SecurityContextConfig extends WebSecurityConfigurerAdapter {

	@Autowired
	@Qualifier("userDetailsService")
	private UserDetailsService userDetailsService;
	
	@Autowired
	public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
		auth.userDetailsService(userDetailsService);
	}

It's all done !

You can call (CurrentUser)getAuthentication().getPrincipal() to get you newly CurrentUser or set some properties.

Solution 2 - Java

In order to add more details to the authenticated user. You need to first create your own implementation of the User object which should extend the spring security User object. After that you can add the properties you want to add to the authenticated user. Once this is done you need to return your implementation of the user object in UserDetailService (If you are not using LDAP for authentication). This link provides the details for adding more details to the authenticated user--

http://javahotpot.blogspot.com/2013/12/spring-security-adding-more-information.html

Solution 3 - Java

(I will assume you have a basic Spring Security configuration working and know how the basic components work together)

The most "correct" way would be providing your own implementation of AuthenticationProvider, that return a custom Authentication implementation. Then you can fill in this Authentication instance with everything you need. For example:

public class MyAuthentication extends UsernamePasswordAuthenticationToken implements Authentication {

    public MyAuthentication(Object principal, Object credentials, int moduleCode) {
        super(principal, credentials);
        this.moduleCode = moduleCode;
    }

    public MyAuthentication(Object principal, Object credentials,  Collection<? extends GrantedAuthority> authorities,int moduleCode) {
        super(principal, credentials, authorities);
        this.moduleCode = moduleCode;
    }

    private int moduleCode;

    public getModuleCode() {
        return moduleCode;
    }   
}


public class MyAuthenticationProvider extends DaoAuthenticationProvider {

    private Collection<GrantedAuthority> obtainAuthorities(UserDetails user) {
        // return granted authorities for user, according to your requirements
    }

    private int obtainModuleCode(UserDetails user) {
        // return moduleCode for user, according to your requirements
    }

    @Override
    public Authentication createSuccessAuthentication(Object principal, Authentication authentication, UserDetails user) {
        // Suppose this user implementation has a moduleCode property
        MyAuthentication result = new MyAuthentication(authentication.getPrincipal(),
                                                       authentication.getCredentials(),
                                                       obtainAuthorities(user),
                                                       obtainModuleCode(user));
        result.setDetails(authentication.getDetails());
        return result;
    }
}

And then, in applicationContext.xml:

<authentication-manager>
    <authentication-provider ref="myAuthenticationProvider">
</authentication-manager>

<bean id="myAuthenticationProvider" class="MyAuthenticationProvider" scope="singleton">
    ...
</bean>

I guess you could get it working by providing custom implementations of AuthenticationDetails and AuthenticationDetailsSource, but I think that would be a less clean approach.

Solution 4 - Java

The "only" things you need to do is create your own UserDetailsService implementation which returns your own implementation of a UserDetails object.

See here for a tutorial which implements a JPA based UserDetailsService.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionManeetKView Question on Stackoverflow
Solution 1 - JavaAlireza FattahiView Answer on Stackoverflow
Solution 2 - JavaYogenView Answer on Stackoverflow
Solution 3 - JavagpecheView Answer on Stackoverflow
Solution 4 - JavaM. DeinumView Answer on Stackoverflow