Hi, I'm doing something like this right now, and thought I'd share what i've got working right now.
Short answer is by doing
Code:
SecurityContextHolder.getContext().getAuthentication().getPrincipal()
you get the currently authenticated user. However the users are pre-defined on an XML file. So what to do?
First thing is getting the user from the database. To do that I create an entity that will hold users. I also have now Roles mapped to an enum, as I won't be changing them for now and add a finder to locate an user by its username
Code:
entity --class ~.domain.Person
field string --fieldName username --notNull
field string --fieldName password
field boolean --fieldname enabled
enum type --class ~.domain.PersonRole
enum constant ROLE_USER
enum constant ROLE_ADMIN
field enum --fieldName role --type ~.domain.PersonRole --enumType STRING --class ~.domain.Person
finder add findPeopleByUsername
the open the Role enum and let it implement the GrantedAuthority Interface
Code:
public enum PersonRole implements GrantedAuthority {
ROLE_USER,
ROLE_ADMIN;
@Override
public String getAuthority() {
return name();
}
}
and get the Person class to implement UserDetails
Code:
@Entity
@RooJavaBean
@RooEntity(finders = { "findPeopleByUsername" })
public class People implements UserDetails {
@NotNull
@Size(min = 4, max = 30)
private String username;
@NotNull
private String password;
@Enumerated(EnumType.STRING)
private PersonRole role;
private Boolean enabled;
@Override
public Collection<GrantedAuthority> getAuthorities() {
return Arrays.asList((GrantedAuthority) role);
}
@Override
public boolean isAccountNonExpired() {
return enabled;
}
@Override
public boolean isAccountNonLocked() {
return enabled;
}
@Override
public boolean isCredentialsNonExpired() {
return enabled;
}
@Override
public boolean isEnabled() {
return enabled;
}
the last thing here is implement a class that implements UserDetailsService
Code:
@Component("userDetailsService")
public class JpaUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException, DataAccessException {
try {
return (UserDetails) Person.findPeopleByUsername(username).getSingleResult();
} catch (EntityNotFoundException ex) {
throw new UsernameNotFoundException(ex.getMessage());
}
}
}
and enable it on applicationContext-security
replace
Code:
<authentication-manager alias="authenticationManager">
<!-- SHA-256 values can be produced using 'echo -n your_desired_password | sha256sum' (using normal *nix environments) -->
<authentication-provider>
<password-encoder hash="sha-256"/>
<user-service>
<user name="admin" password="8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918" authorities="ROLE_ADMIN"/>
<user name="user" password="04f8996da763b7a969b1028ee3007569eaf3a635486ddab211d512c85b9df8fb" authorities="ROLE_USER"/>
</user-service>
</authentication-provider>
</authentication-manager>
by
Code:
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="userDetailsService"/>
</authentication-manager>
Up to here you will have the authentication working on the database instead of the pre-defined users on the xml file. It would be a great moment to load users and test it to make sure the auth is working.
So the second part is obtaining the user data. You can load as I told at the beggining by doing that static method call, however static method feel seem un-springful to me, so I created a helper class that I inject through spring
Code:
public class ContextUserDetails {
private final SecurityContextHolderStrategy holderStrategy;
public ContextUserDetails(SecurityContextHolderStrategy holderStrategy) {
this.holderStrategy = holderStrategy;
}
public Person getPerson() {
Object principal = holderStrategy.getContext().getAuthentication().getPrincipal();
if (principal instanceof Person) {
return (Person) principal;
}
// principal object is either null or represents anonymous user -
// neither of which our domain User object can represent - so return null
return null;
}
}
and define it on applicationContext.xml
Code:
<!--Gets the active person object-->
<bean id="contextUserDetails" class="com.jeduan.util.ContextUserDetails">
<constructor-arg>
<bean class="org.springframework.security.core.context.SecurityContextHolder" factory-method="getContextHolderStrategy"/>
</constructor-arg>
</bean>
so now when you want to use it just
Code:
@Autowired
private ContextUserDetails contextUser;
public someMethod() {
Person person = contextUser.getPerson()
}
Whoa, so that was long, but hopefully you will get the hold of it rather promptly.
Kind regards and best of lucks with your application!
EDIT:
Forgot the important part, what you asked.
So, after all this setup, you then could do something like
Code:
@RequestMapping
public String addToCart(Long itemId, ModelMap modelMap) {
Item item = Item.findItem(itemId);
item.setShopper(contextUser.getPerson);
item.persist()
}
or setting up finders to find by the person you just got from the context, you get the idea.
Best Regards