Using MongoDB With Version 2.x of the Grails Spring Security Core Plugin
With a few customization steps it’s easy to use MongoDB to store user and role information for the spring-security-core plugin instead of using Hibernate, and after seeing this Stack Overflow question
I thought I’d write up some notes on how to do this with the current plugins. Note that much of this is based on this blog post
.
I created a demo application using Grails 2.3.3; it’s available on GitHub. The general steps were:
$ grails create-app mongoSpringSecurity
- add the plugins to
BuildConfig.groovy
$ grails s2-quickstart auth User Role
- update
DataSource.groovy
to use MongoDB - create a custom
UserDetailsService
and register it inresources.groovy
- create a test role and a user in
BootStrap.groovy
- customize the domain classes to use MongoDB
- add tags to
index.gsp
to add a login link if you’re not logged in, and show that you’re logged in if you are
One difference between what I do here and what was done in the original blog post is that the custom UserDetailsService
is not a Grails service – it’s in src/groovy
and not in grails-app/services
. It wasn’t necessary to be a real service then and isn’t now; it’s a coincidence that the Spring Security interface name ends in “Service”. See the plugin documentation for general information about customizing this bean.
You can see the source for the custom bean here. By embedding the authorities in the user domain class, the many-to-many relationship is not needed and the model is a lot simpler, so the class implementation is also – for example there’s no need for a
withTransaction
block to avoid lazy loading exceptions.
The changes for the User
class are fairly minor. You need static mapWith = 'mongo'
if you have both the Hibernate and MongoDB plugins; in this case it’s unnecessary but harmless to leave it in. The id
field should be an ObjectId
, and I retained the other customizations from the earlier blog post (the embedded roles, the addition of the email
field, extra constraints, etc.). The Role
class changes are similar.
Since we’re using a custom UserDetailsService
, we can delete the userLookup.userDomainClassName
, userLookup.authorityJoinClassName
, and authority.className
properties from Config.groovy
, and since the roles are embedded in the user class we can delete the generated UserRole
class.
You should be able to clone the repo and start the application (assuming you have MongoDB and Grails 2.3.3 already). Click the login link on the start page and after you successfully authenticate, the link should be replaced by a message.
It’s great to see a new post here! Is there any chance for weekly updates for This Week in Grails?
There won’t be any more This Week in Grails posts here. See the “Grails Diary” series at but http://grydeske.net/news/index for something very similar.
In that case thanks for all your work on “This Week in Grails”! It has been very valuable source of information about Grails/Groovy ecosystem. I used to visit your blog quite often just to catch up with latest news.
Maybe you could write a short post here so more people are informed about Grails Diary? Not everyone is reading these comments.
One more thought – I’m afraid that Grails is loosing its traction. I see some important people like you or Marc Palmer being less and less involved in the community lately…
How can we use UserDetailsService to support a custom login where additional Group/Organization must be specified in addition to username/password.
See this blog post: http://burtbeckwith.com/blog/?p=1090
The talk was about sharing information to make it easier to customize the plugin, and one example in the sample project is exactly what you’re asking about – it changes the login to support a third input element.
Thanks Burt,
The link was useful.
Hi Burt,
I haven’t been able to get the cas spring security plugin to work after upgrading to Grails 2.3.3. It appears that the default authentication handler is being called and redirecting to /login/auth rather than the CAS handler defined in the Config.
Have you seen any issues? Im using the mongo 1.3.3 plugin and have hibernate disabled.
Try the User list – I tested CAS with Hibernate before releasing the plugin and it worked fine, but another user might be able to shed some light.
Hi Burt,
Thanks for writing this post. I also enjoy how you explained different parts of the Grails framework in your book. In the post Above you said “By embedding the authorities in the user domain class, the many-to-many relationship is not needed and the model is a lot simpler, so the class implementation is also”, So do you think in future you might apply that to the actual plugin so that become the default or is it tricky since people’s code might break due to that change ? I was just curious 🙂
That would be a breaking change and would likely affect other plugins that use and/or extend the core plugin. Plus it is a fairly natural many-to-many relationship. Given how Mongo works and how it’s typically used embedding makes a lot of sense there, but in a relational database the current three-table approach works well. You also get the consistency check from the foreign key relationships – if you try to assign a non-existent role it will fail. But in Mongo you can easily add a bad role, e.g. ROLE_ADMN instead of ROLE_ADMIN, and there’s no check for that.
Thanks for explaining. So if I’m not using mangoDB, using the current model of 3 tables (User, Role, UserRole) shouldn’t cause performance issues for a website with thousands of users. What I mean is, a typical Blog website that only has 1 admin, and the rest are just users. Correct ? (I know it depends on the web application, just asking generally)
I did a talk a while back on performance issues with 1-many and many-many collections in Grails; it’s online here: http://www.infoq.com/presentations/GORM-Performance
The implementation of the many-many in the plugin explicitly maps the join table in addition to the user and role tables, that’s why the UserRole domain class that’s generated by the s2-quickstart has so much code in it. This approach is very fast and doesn’t have the unexpected problems with standard many-to-many mappings in Grails that use collections.
Great Job Burt! I watched the video, it was very helpful and I re-coded my country, state, city domain classes using that approach afterward. My model is now very simple comparing to the hasMany and belogTo model.
I don’t know if you have a post related to rememberMe portion of Spring Security Core Plugin that you can redirect me to. Because I’m having a problem with that and seems like it’s not working. I think when the remember me isn’t checked, when we close the browser and open it again, it should show the login page rather than log the user in automatically, am I correct ? In either case, whether it’s checked or not, the user gets logged in. I appreciate it if you can elaborate on that. You can remove this reply message if you can answer the question here : http://stackoverflow.com/questions/21977692/grails-spring-security-remember-me-feature-doesnt-work
Thanks!