After we have fun with previous post Beginning Grails 2.0 with MongoDB , now it’s time to move on to create secure Grails application using spring security core to work with MongoDB.
Pre-requisites
– Grails 2.0.3
– MongoDB 2.0.6
1. Create new app and install mongodb plugin and remove hibernate
grails create-app example
grails install-plugin mongodb
grails uninstall-plugin hibernate
plugins { //runtime ":hibernate:$grailsVersion" runtime ":jquery:1.7.1" runtime ":resources:1.1.6" build ":tomcat:$grailsVersion" }
2. Edit configuration file in conf as you need
3. Install spring security core plugin and run the “s2-quickstart” script to initialize Spring Security and create your domain classes
grails s2-quickstart com.example User Role
4. Edit User domain class to look like this
package com.example import org.bson.types.ObjectId class User { ObjectId id def springSecurityService String username String password String email boolean enabled boolean accountExpired boolean accountLocked boolean passwordExpired Set authorities static transients = ["springSecurityService"] static embedded = ['authorities'] static constraints = { username blank: false, unique: true,size: 2..32,matches: "[a-zA-Z0-9_]+" email blank: false, unique:true,email:true password blank: false,size:6..64 } static mapping = { password column: '`password`' } def beforeInsert() { encodePassword() } def beforeUpdate() { if (isDirty('password')) { encodePassword() } } protected void encodePassword() { password = springSecurityService.encodePassword(password) } }
5. create MongoUserDetailService.groovy at /example/grails-app/services/ and add following code
import org.codehaus.groovy.grails.plugins.springsecurity.GrailsUser import org.springframework.security.core.GrantedAuthority import org.springframework.security.core.userdetails.UserDetails import org.springframework.security.core.authority.GrantedAuthorityImpl import org.springframework.security.core.userdetails.UsernameNotFoundException import org.codehaus.groovy.grails.plugins.springsecurity.SpringSecurityUtils import org.apache.log4j.Logger import org.codehaus.groovy.grails.plugins.springsecurity.GrailsUserDetailsService import com.example.User import com.example.UserRole class MongoUserDetailsService implements GrailsUserDetailsService { private Logger log = Logger.getLogger(getClass()) /** * 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 GrantedAuthorityImpl(SpringSecurityUtils.NO_ROLE)] @Override UserDetails loadUserByUsername(String username, boolean loadRoles) { if(log.debugEnabled) { log.debug("Attempted user logon: $username") } User.withTransaction { status -> def user = User.findByUsername(username) if (!user) { log.warn("User not found: $username") throw new UsernameNotFoundException('User not found', username) } if(log.debugEnabled) { log.debug("User found: $username") } def roles = NO_ROLES if (loadRoles) { def authorities = user.authorities?.collect {new GrantedAuthorityImpl(it.authority)} if(authorities) { roles = authorities } } if(log.debugEnabled) { log.debug("User roles: $roles") } return createUserDetails(user, roles) } } @Override UserDetails loadUserByUsername(String username) { return loadUserByUsername(username, true) } protected UserDetails createUserDetails(user, Collection authorities) { new GrailsUser(user.username, user.password, user.enabled, !user.accountExpired, !user.passwordExpired, !user.accountLocked, authorities, user.id) } }
6. Edit /example/grails-app/conf/spring/resource.groovy file to look like this
// Place your Spring DSL code here beans = { userDetailsService(MongoUserDetailsService) }
7. Edit BootStrap.groovy to create new Role and new User for testing
import com.example.Role import com.example.User class BootStrap { def init = { servletContext -> def userRole = new Role(authority:"ROLE_USER").save(flush:true) def user = new User() user.username = "example" user.password = "password" user.email = "example@email.com" user.enabled = true user.accountExpired = false user.accountLocked = false user.passwordExpired = false user.authorities = [userRole] user.save(flush:true) } def destroy = { } }
8. Add following tag to index.gsp
<sec:ifLoggedIn> Welcome Back! </sec:ifLoggedIn> <sec:ifNotLoggedIn> <g:link controller='login' action='auth'>Login</g:link> </sec:ifNotLoggedIn>
9. Hooray! grails run-app to try our example application
Fork this project on github https://github.com/etcpe9/example.git
References
Spring security core plugin document
Gorm for Mongo document
I’m working through this tutorial and am about to give it a run now that all the code is in. Before I do, I wanted to point out that you lost some code in your step 8 code block. It lost the sec:ifLoggedIn and sec:ifNotLoggedIn opening and closing tags..
Thanks, updated.
After a bit of work, I got the thing to work. I had used a nonstandard name for User, which caused some problems in the MongoUserDetailsService. Once I replaced “User” with “SpringUser,” everything works great. Thank you for putting this together. I looked for a similar tutorial about a year ago and didn’t find anything. I thought I was going to have to go with dual datasources just to use Spring Security! :-O
Great Article. In Step 5, You are asking to create the service as MongoUserDetailService.groovy whereas the name of the class in code is MongoUserDetailsService i.e. there is an additional “s” in Detail. I had to struggle a bit to make it work. Thanks for this great post
s2 plugin contains a lot of raw SQL that may not work in MongoDB
Pingback: An Army of Solipsists » Blog Archive » Using MongoDB With Version 2.x of the Grails Spring Security Core Plugin
Pingback: Spring Security Core and mongoDB Doesn't Authenticate - MongoDB Solutions - Developers Q & A