Reputation: 650
Iam using spring-security-core 2.0 RC5 in my grails application and I added an extra column in the user table named 'approved_fl' I just want check this flag while login, if this flag is P then i need to show a message like 'sorry your account is not yet approved by admin', if this flag is R then i need to show 'Sorry your account is rejected by admin'. How to configure my spring security plugin for this.
Upvotes: 3
Views: 1688
Reputation: 650
Finally I got this.
I done the following changes in my application.
In my resources.groovy
import com.custom.auth.MyUserDetailsService
import com.custom.auth.MyPreAuthenticationChecks
beans = {
preAuthenticationChecks(MyPreAuthenticationChecks)
userDetailsService(MyUserDetailsService)
}
I created following groovy classes under src/groovy package
Here is the MyPreAuthenticationChecks.groovy
package com.custom.auth
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsChecker;
import com.custom.exception.AccountNotApprovedException
import com.custom.exception.AccountRejectedException
/**
* Copy of the private class in AbstractUserDetailsAuthenticationProvider
* to make subclassing or replacement easier.
*
* @author <a href='mailto:[email protected]'>Burt Beckwith</a>
*/
public class MyPreAuthenticationChecks implements UserDetailsChecker {
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
protected final Logger log = LoggerFactory.getLogger(getClass());
public void check(UserDetails user) {
if (!user.isAccountNonLocked()) {
log.debug("User account is locked");
throw new LockedException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.locked",
"User account is locked"));
}
if (!user.isEnabled()) {
log.debug("User account is disabled");
throw new DisabledException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.disabled",
"User is disabled"));
}
if (!user.isAccountNonExpired()) {
log.debug("User account is expired");
throw new AccountExpiredException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.expired",
"User account has expired"));
}
if (user.approveFl=='P') {
log.debug("User account is not approved");
throw new AccountNotApprovedException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.notapproved",
"User account not yet approved"));
}
if (user.approveFl=='R') {
log.debug("User account is rejected");
throw new AccountRejectedException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.rejected",
"User account has rejected"));
}
}
}
Here is the MyUserDetails.groovy
package com.custom.auth
import grails.plugin.springsecurity.userdetails.GrailsUser
import org.springframework.security.core.GrantedAuthority
class MyUserDetails extends GrailsUser {
final char approveFl
MyUserDetails(String username, String password, boolean enabled,
boolean accountNonExpired, boolean credentialsNonExpired,
boolean accountNonLocked,
Collection<GrantedAuthority> authorities,
long id, char approveFl) {
super(username, password, enabled, accountNonExpired,
credentialsNonExpired, accountNonLocked, authorities, id)
this.approveFl = approveFl
}
}
Here is the MyUserDetailsService.groovy
package com.custom.auth
import com.domain.auth.User
import grails.plugin.springsecurity.SpringSecurityUtils
import grails.plugin.springsecurity.userdetails.GrailsUserDetailsService
import grails.plugin.springsecurity.userdetails.NoStackUsernameNotFoundException
import grails.transaction.Transactional
import org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.core.userdetails.UsernameNotFoundException
class MyUserDetailsService implements GrailsUserDetailsService {
/**
* Some Spring Security classes (e.g. RoleHierarchyVoter) expect at least
* one role, so we give a user with no granted roles this one which gets
* past that restriction but doesn't grant anything.
*/
static final List NO_ROLES = [new SimpleGrantedAuthority(SpringSecurityUtils.NO_ROLE)]
UserDetails loadUserByUsername(String username, boolean loadRoles)
throws UsernameNotFoundException {
return loadUserByUsername(username)
}
@Transactional(readOnly=true, noRollbackFor=[IllegalArgumentException, UsernameNotFoundException])
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = User.findByUsername(username)
if (!user) throw new NoStackUsernameNotFoundException()
def roles = user.authorities
// or if you are using role groups:
// def roles = user.authorities.collect { it.authorities }.flatten().unique()
def authorities = roles.collect {
new SimpleGrantedAuthority(it.authority)
}
return new MyUserDetails(user.username, user.password, user.enabled,
!user.accountExpired, !user.passwordExpired,
!user.accountLocked, authorities ?: NO_ROLES, user.id,
user.approveFl)
}
}
Here is the AccountNotApprovedException.groovy
package com.custom.exception
import org.springframework.security.core.AuthenticationException
class AccountNotApprovedException extends AuthenticationException {
public AccountNotApprovedException(String message, Throwable t) {
super(message, t)
}
public AccountNotApprovedException(String message) {
super(message)
}
public AccountNotApprovedException(String message, Object extraInformation) {
super(message, extraInformation)
}
}
Here is the AccountRejectedException.groovy
package com.custom.exception
import org.springframework.security.core.AuthenticationException
class AccountRejectedException extends AuthenticationException {
public AccountRejectedException(String message, Throwable t) {
super(message, t)
}
public AccountRejectedException(String message) {
super(message)
}
public AccountRejectedException(String message, Object extraInformation) {
super(message, extraInformation)
}
}
Simply copy your existing spring-security-core LoginController to groovy controller package (this will override the existing one)
Now update the authfail() of LoginController like follows
def authfail() {
String msg = ''
def exception = session[WebAttributes.AUTHENTICATION_EXCEPTION]
if (exception) {
if (exception instanceof AccountExpiredException) {
msg = message(code: 'springSecurity.errors.login.expired')
}
else if (exception instanceof CredentialsExpiredException) {
msg = message(code: 'springSecurity.errors.login.passwordExpired')
}
else if (exception instanceof DisabledException) {
msg = message(code: 'springSecurity.errors.login.disabled')
}
else if (exception instanceof LockedException) {
msg = message(code: 'springSecurity.errors.login.locked')
}
else if (exception instanceof AccountNotApprovedException) {
msg = g.message(code: "springSecurity.errors.login.notapproved")
}
else if (exception instanceof AccountRejectedException) {
msg = g.message(code: "springSecurity.errors.login.rejected")
}
else if (exception instanceof SessionAuthenticationException){
msg = exception.getMessage()
}
else {
msg = message(code: 'springSecurity.errors.login.fail')
}
}
Now add the corresponding g:messages (which need to be shown when the exception will throw) to your spring-security-core.properties file
Upvotes: 1
Reputation: 12228
You will need to implement your own custom user details service in order to put that property on the user details (principal)
http://grails-plugins.github.io/grails-spring-security-core/v3/index.html#userDetailsService
Then you will need extend the org.springframework.security.authentication.AccountStatusUserDetailsChecker
with your own implementation that checks the property on the user details.
You can register that implementation with a bean in resources.groovy like so:
userDetailsChecker(MyCustomUserDetailsChecker)
Then if you want to display a custom message to the user you will need to override the def authfail() {
action of the LoginController
to look for the exception you throw.
Upvotes: 3