Skip to content

Instantly share code, notes, and snippets.

@liling
Created October 19, 2013 06:23
Show Gist options
  • Select an option

  • Save liling/7052246 to your computer and use it in GitHub Desktop.

Select an option

Save liling/7052246 to your computer and use it in GitHub Desktop.
Shiro CAS Realm of grails-shiro 1.1.4 plugin. This one use CAS as authentication method and use database store authorization data. When user logging into the system for the first time, an user record is created for him automatically and a default role is added to user.
import base.shiro.ShiroRole
import base.shiro.ShiroUser
import org.apache.shiro.cas.CasAuthenticationException
import org.apache.shiro.authc.AccountException
import org.apache.shiro.authc.SimpleAuthenticationInfo
import org.apache.shiro.util.StringUtils
import org.apache.shiro.util.CollectionUtils
import org.apache.shiro.subject.PrincipalCollection
import org.apache.shiro.subject.SimplePrincipalCollection
import org.jasig.cas.client.authentication.AttributePrincipal
import org.jasig.cas.client.validation.*
/**
* Simple realm that authenticates users against an CAS server.
*/
class ShiroCasRealm {
static authTokenClass = org.apache.shiro.cas.CasToken
private def ticketValidator = null
def grailsApplication
def shiroPermissionResolver
def grailsEvents
def authenticate(authToken) {
log.info "Attempting to authenticate ${authToken.userId} in CAS realm..."
def appConfig = grailsApplication.config
def serverUrl = appConfig.security.shiro.cas.serverUrl
if (!serverUrl) {
log.error(" the security.shiro.cas.serverUrl can not be empty.it should be https://host:port/cas")
}
def serviceUrl = appConfig.security.shiro.cas.serviceUrl
if (!serviceUrl) {
log.error(" the security.shiro.cas.serviceUrl can not be empty.it should be http://host:port/mycontextpath/shiro-cas")
}
if (authToken == null) {
throw new AccountException('cas tokens are not be null in this realm.')
}
String ticket = (String)authToken.getCredentials()
if (!StringUtils.hasText(ticket)) {
throw new AccountException('wrong cas token ticket:'+ticket)
}
if (ticketValidator==null) {
ticketValidator = new Cas20ServiceTicketValidator(serverUrl)
}
try {
// contact CAS server to validate service ticket
Assertion casAssertion = ticketValidator.validate(ticket, serviceUrl)
// get principal, user id and attributes
AttributePrincipal casPrincipal = casAssertion.principal
String userId = casPrincipal.name
if (log.debugEnabled) {
log.debug("Validate ticket : ${ticket} in CAS server : ${serverUrl} to retrieve user : ${userId}")
}
def pluginConfig = grailsApplication.getConfig().plugin.baseShiro
// Automatically create user account and grant default role
if (pluginConfig.cas.autoCreateUser) {
def user = ShiroUser.findByUsername(userId)
if (user == null) {
ShiroUser.withTransaction {
user = new ShiroUser()
user.username = userId
user.fullname = userId
user.save()
String defaultRole = pluginConfig.cas.defaultUserRole
if (defaultRole) {
ShiroRole role = ShiroRole.findByName(defaultRole)
if (role) {
user.addToRoles(role)
}
}
}
}
}
grailsEvents.event('security', 'afterUserLogin', userId)
Map<String, Object> attributes = casPrincipal.attributes
// refresh authentication token (user id + remember me)
authToken.setUserId(userId)
String rememberMeStringValue = (String)attributes.get('longTermAuthenticationRequestTokenUsed')
boolean isRemembered = rememberMeStringValue?.toBoolean()
if (isRemembered) {
authToken.setRememberMe(true)
}
// create simple authentication info
List<Object> principals = CollectionUtils.asList(userId, attributes)
PrincipalCollection principalCollection = new SimplePrincipalCollection(principals, this.toString())
return new SimpleAuthenticationInfo(principalCollection, ticket)
} catch (TicketValidationException e) {
throw new CasAuthenticationException("Unable to validate ticket [" + ticket + "]", e);
} catch (Exception e) {
e.printStackTrace();
}
}
def hasRole(principal, roleName) {
def roles = ShiroUser.withCriteria {
roles {
eq("name", roleName)
}
eq("username", principal)
}
return roles.size() > 0
}
def hasAllRoles(principal, roles) {
def r = ShiroUser.withCriteria {
roles {
'in'("name", roles)
}
eq("username", principal)
}
return r.size() == roles.size()
}
def isPermitted(principal, requiredPermission) {
// Does the user have the given permission directly associated
// with himself?
//
// First find all the permissions that the user has that match
// the required permission's type and project code.
def user = ShiroUser.findByUsername(principal, [cache: true])
if (user == null) return false
def permissions = user.permissions
// Try each of the permissions found and see whether any of
// them confer the required permission.
def retval = permissions?.find { permString ->
// Create a real permission instance from the database
// permission.
def perm = shiroPermissionResolver.resolvePermission(permString)
// Now check whether this permission implies the required
// one.
if (perm.implies(requiredPermission)) {
// User has the permission!
return true
}
else {
return false
}
}
if (retval != null) {
// Found a matching permission!
return true
}
// If not, does he gain it through a role?
//
// Get the permissions from the roles that the user does have.
def results = ShiroUser.executeQuery("select distinct p from ShiroUser as user join user.roles as role join role.permissions as p where user.username = ?", [principal], [cache: true])
// There may be some duplicate entries in the results, but
// at this stage it is not worth trying to remove them. Now,
// create a real permission from each result and check it
// against the required one.
retval = results.find { permString ->
// Create a real permission instance from the database
// permission.
def perm = shiroPermissionResolver.resolvePermission(permString)
// Now check whether this permission implies the required
// one.
if (perm.implies(requiredPermission)) {
// User has the permission!
return true
}
else {
return false
}
}
if (retval != null) {
// Found a matching permission!
return true
}
else {
return false
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment