3

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.

2 Answers 2

3

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.

Sign up to request clarification or add additional context in comments.

5 Comments

How the 'MyCustomUserDetailsChecker' will used by authfail(). It is still using DefaultPreAuthenticationCheck.java class
the userDetailsChecker need to be initialized in bootstrap.groovy right?
The userDetailsChecker has nothing to do with Bootstrap.groovy
The link between the detailschecker and authfail() is the exception you throw. You can create your own custom exception and throw it in the details checker. If you choose to do that, you will need to add code to authfail to handle it. If you throw an exception that authfail is already handling, then obviously no changes are necessary.
Thank you so much James. One more thing I created all these custom classes under my src/groovy resource package and now the problem is when throwing my custom exception in the authfail() of spring security's login controller it doesn't get resolved in other words I couldn't import those custom exception in my LoginController.
1

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

enter image description here

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.