BCS STS Grails Authentication

Invoke the SpringSource Tool Suite and make sure the Grails perspective is active.  Create a new grails project and name it BCSBookStore.
Invoke the Grails command prompt and issue the following command
install-plugin spring-security-core

This action installed the spring security core.  Now we are ready to invoke the quick start by issuing the following command
s2-quickstart com.bcsbooks User Role

Three new domains were created in com.bcsbooks.

package com.bcsbooks
 
class Role {
 
	String authority
 
	static mapping = {
		cache true
	}
 
	static constraints = {
		authority blank: false, unique: true
	}
}
package com.bcsbooks
 
class User {
 
	String username
	String password
	boolean enabled
	boolean accountExpired
	boolean accountLocked
	boolean passwordExpired
 
	static constraints = {
		username blank: false, unique: true
		password blank: false
	}
 
	static mapping = { password column: '`password`' }
 
	Set<Role> getAuthorities() {
		UserRole.findAllByUser(this).collect { it.role } as Set
	}
}
package com.bcsbooks
 
import org.apache.commons.lang.builder.HashCodeBuilder
 
class UserRole implements Serializable {
 
	User user
	Role role
 
	boolean equals(other) {
		if (!(other instanceof UserRole)) {
			return false
		}
 
		other.user?.id == user?.id &&
				other.role?.id == role?.id
	}
 
	int hashCode() {
		def builder = new HashCodeBuilder()
		if (user) builder.append(user.id)
		if (role) builder.append(role.id)
		builder.toHashCode()
	}
 
	static UserRole get(long userId, long roleId) {
		find 'from UserRole where user.id=:userId and role.id=:roleId',
				[userId: userId, roleId: roleId]
	}
 
	static UserRole create(User user, Role role, boolean flush = false) {
		new UserRole(user: user, role: role).save(flush: flush, insert: true)
	}
 
	static boolean remove(User user, Role role, boolean flush = false) {
		UserRole instance = UserRole.findByUserAndRole(user, role)
		instance ? instance.delete(flush: flush) : false
	}
 
	static void removeAll(User user) {
		executeUpdate 'DELETE FROM UserRole WHERE user=:user', [user: user]
	}
 
	static void removeAll(Role role) {
		executeUpdate 'DELETE FROM UserRole WHERE role=:role', [role: role]
	}
 
	static mapping = {
		id composite: ['role', 'user']
		version false
	}
}
It also creates some UI controllers and GSPs:
  • grails-app/controllers/LoginController.groovy
  • grails-app/controllers/LogoutController.groovy
  • grails-app/views/auth.gsp
  • grails-app/views/denied.gsp
The script has edited grails-app/conf/Config.groovy and added the configuration for your domain classes. Make sure that the changes are correct.
Create a controller that will be restricted by role.
create-controller com.bcsbooks.Secure
package com.bcsbooks
 
class SecureController {
 
	def index = { }
}
Modify the controller so it produces output.
package com.bcsbooks
 
class SecureController {
 
	def index = { render 'Secure access only' }
}
Start the server
runn-app

Before you secure the page, navigate to http://localhost:8080/bookstore/secure to verify that you can see the page without being logged in.

Shut down the app (using CTRL-C) and edit grails-app/conf/BootStrap.groovy to add the security objects that you need.

import com.bcsbooks.Role
import com.bcsbooks.User
import com.bcsbooks.UserRole
class BootStrap {
	def springSecurityService
	def init = { servletContext ->
		def adminRole = new Role(authority: 'ROLE_ADMIN').save(flush: true)
		def userRole = new Role(authority: 'ROLE_USER').save(flush: true)
		String password = springSecurityService.encodePassword('password')
		def testUser = new User(username: 'me', enabled: true, password: password)
		testUser.save(flush: true)
		UserRole.create testUser, adminRole, true
		assert User.count() == 1
		assert Role.count() == 2
		assert UserRole.count() == 1
	}
}

Some things to note about the preceding BootStrap.groovy:

  • springSecurityService is used to encrypt the password.
  • The example does not use a traditional GORM many-to-many mapping for the User<->Role relationship; instead you are mapping the join table with the UserRole class. This performance optimization helps significantly when many users have one or more common roles.

We explicitly flushed the creates because BootStrap does not run in a transaction or OpenSessionInView.

Mr. Arch Brooks, Software Engineer, Brooks Computing Systems authored this article.