Table of Contents
IntroductionSetting up the RaceTrack sample application
Installing the Grails Acegi plugin
Creating the Acegi Security components
Configuring Acegi Security to secure the application
Testing
Introduction
This article discusses the integration of the grails-acegi plugin with a sample Grails application. As part of this integration, there are three major components which will be used – Groovy, Grails and Acegi Security.
Groovy is a powerful, high level language for the Java platform which compiles down to Java bytecode. It is similar in concept to Ruby or Python, however it is tightly integrated with the Java platform. This allows you to utilize a powerful and concise coding syntax while at the same time allowing you to stay on the JVM and protect your investment in the existing Java platform and surrounding libraries.
Grails is a full stack framework implemented in Groovy. Grails attempts to solve many pieces of the web development puzzle through both the Grails core technology and associated plug-ins. Out-of-the-box capabilities include:
- An Object/Relational Mapping (ORM) layer built on Hibernate
- An expressive view technology called Groovy Server Pages (GSP)
- A controller layer built on Spring MVC
- A command line scripting environment built on Gant, a Groovy-based version of Ant
- An embedded Jetty container which is configured for on-the-fly reloading of resources
- Dependency injection via the built-in Spring container
- Support for internationalization (i18n) built through Spring's MessageSource API
- A transactional service layer which utilizes Spring transaction management
- Extensive use of Domain-Specific Languages (DSLs)
Acegi Security is a powerful, flexible security solution for enterprise software, with a particular emphasis on applications that use Spring. Acegi provides comprehensive authentication, authorization, instance-based access control, channel security and human user detection capabilities.
This article assumes that you have completed the Grails tutorial found in Getting Started with Grails by Jason Rudolph, and have implemented the RaceTrack sample application. The grails-acegi plugin will then be integrated with RaceTrack to provide security for your application. Utilizing the grails-acegi plugin avoids the overhead of having to implement Grails interceptors in your application, provides more flexibility than interceptors, and also saves reimplementing your own security system by leveraging Acegi.
Setting up the RaceTrack sample application
First, you will need to download Grails 1.0, the grails-acegi-0.2 plugin and Java SE JDK 5.0 or greater.
At this point, you are assumed to have implemented most of the RaceTrack application as described in Getting Started with Grails. However, you do not have to complete the entire tutorial in order to test the grails-acegi plugin – all which is required are the domain classes and controllers, and scaffolding of the controllers is good enough for testing.
Figure 1 – Directory structure after creating the racetrack application
Your RaceTrack application directory should look like the one shown in Figure 1 above. Now, open up the \grails-app\domain folder:
Figure 2 – domain classes directory
As you can see in Figure 2, the "domain" directory only contains 2 domain classes: Race and Registration. Now, open up the \grails-app\controllers folder and confirm that it has a controller for each domain class as shown in Figure 3 below:
Figure 3 – Controllers directory
These controllers can be empty, scaffold controllers, e.g.:
class RaceController { def scaffold = Race }
class RegistrationController { def scaffold = Registration }
This is sufficient to get the application up and running. After you start the RaceTrack application, you should be able to see the 2 controllers you created in the controller list as in Figure 4 below:
Figure 4 – Index page before Grails-Acegi Plugin
Installing the Grails Acegi plugin
Next is the installation of the grails-acegi plugin so that the RaceTrack take advantage of the role-based security this plugin provides. In the command prompt , navigate to "racetrack" directory and run this command:
grails install-plugin [path-to]/grails-acegi-0.2.zip
This command creates a plugin directory under the "racetrack" directory as in Figure 5 below:
Figure 5 – plugins directory created after installing the plugin
Creating the Acegi Security components
The next step is to create domain classes that will represent User Accounts and Roles. To begin this process, run the following command:
grails create-auth-domains AuthUser Role
This will create two domain classes (AuthUser and Role), set up the AcegiConfig class, and create the Login and Logout controllers. The AuthUser domain class will represent your users, so for every new user will create a new record in the Auth_User table. The Role domain class will represent security roles that every user is allowed to have – Roles will be assigned to AuthUsers. Both of these classes are shown in Figure 6 below.
The AcegiConfig class (Figure 7) defines the security configuration for your application. It contains the names of the user domain class (in this case AuthUser) and the role domain class (in this case Role), whether to use dynamic vs. static secured urls, and how to setup email alerts (turns them off or on).
Figure 6 – AuthUser.groovy, Role.groovy and Requestmap.groovy (used in AcegiConfig)
Figure 7 – AcegiConfig.groovy created
In order to create new AuthUsers, create new Roles and assign Roles to AuthUsers, we will have to run two commands. One will generate the CRUD controls for domains:
grails generate-manager
and the second to generate registration for controllers and domains:
grails generate-registration
These commands give the user the ability to register and create their username and password, and the default security role will be assigned to that user. The set of controllers generated can be seen in Figure 8 below:
Figure 8 – CRUD controllers (Login and Logout controllers are created when the Auth and Role domains are created)
Figure 9 – Controllers available after grails-acegi-plugin installation
Go to the RaceTrack homepage – it should look similar to Figure 9 above.
Configuring Acegi Security to secure the application
We will now create a User role and a Manager role – to do this, first click on RoleController, and then enter a Role Name of ‘user' and some sort of role description (Figure 10). Note that the RoleController will take care of converting ‘user' to ‘ROLE_USER', which is what it is called in the database and by Acegi's configuration. Repeat the same steps to create a manager role.
Figure 10 – Creating a user role
Go back to the home page, and click on UserController. Now, create a user with the ‘user' role, and another user with a ‘manager' role, as in Figure 11 below:
Figure 11 – Create a standard user, enable the account and assign the 'user' role
Our Roles and Users are now sufficiently configured, so the next step is securing the RaceTrack application. There are two ways to how you can secure your URLs: One is dynamically via RequestmapController, and the other is through editing the AcegiConfig.groovy file directly. The dynamic configuration is the recommended choice, so we will proceed with that.
Before we secure the application, we need to think about the access rules that should be present in the application. A manager is allowed read/write access to any page in the application, which means:
/race/*
/registration/*
A user is allowed read-only access to some of the pages, including:
/race/list/*
/race/show/*
/registration/list/*
/registration/show/*
These rules now need to be translated into entries in the Acegi request map using the RequestmapController. From the RaceTrack homepage, click on RequestmapController, and go to the ‘create a new requestmap' page. In the URL field enter "/race/**", and in the Role field enter "manager" (Figure 12) – this will create a rule which allows any user with the manager role to access all URLs under /race. Do the same thing for registration (URL: /registration/**).
Figure 12 – Manager access rules
As a note, a good practice is to grant all user rights to the manager role as well. Next the access rules for the user role will be created -- in the URL field enter "/race/list/**", and in the Role field enter "user, manager" (Figure 13 - note that roles are separated with a comma). This will create an access rule which allows both users and managers to access the race list page. Note that you will needed to specify both roles -- if you only assign this URL to the user role, it will override the previous rule for manager and will only allow the user role to access the page. Repeat the previous step for the rest of the rules defined previously – this will create all of the access rules for the race and registration pages.
Figure 13 – Creating rule for /race/list/** page – give access to users and managers
Testing
From the RaceTrack homepage, click on either RaceController or RegistrationController (the controllers for the views we secured). You will now notice that you have been automatically redirected to the Login page. If you first log in as a user with the manager role, notice that you will be able to view, create, update and delete anything on the race and registration pages.
Figure 14 – Logging in as a user
Return to the RaceTrack homepage and click on the LogoutController -- this will invalidate your user session and log you out. Click on LoginController again, but this time log in as a user with the user role.If you then go to the /race/list subpage (either by going directly to http://localhost:8080/racetrack/race/list or by navigating through the controller interface), you should be able to see the race/list view (Figure 15).
Remember that the access rules allow only managers to create new records, and users can only read data through the List and Show views – this means that if you try to click on New Race (http://localhost:8080/racetrack/race/create) while logged in as a user with the user role, Acegi will prevent you from navigating to that page, which prevents creation of a new record.
How does this work? Recall that we granted access to /race/* to a manager, but only granted access to /race/list/* and /race/show/* to a user. When a user with the 'user' role attempts to access the /race/create page, Acegi looks at the set of roles for the user and sees that they only have the 'user' role – since our Request map said that the user must have the 'manager' role to be granted access to this page, permission to access the page is denied.
As a side note, in a real application you will probably want to display a better error page than the default one (Figure 16).
Figure 15 – Race List view
Figure 16 – Access denied error page
Congratulations – you now have a fully secured instance of the RaceTrack application!
Community comments
Problem with acegi when configuring pooled=true in Datasource.groovy
by Franz Schmid,
Re: Problem with acegi when configuring pooled=true in Datasource.groovy
by Stefan Maiwald,
Are you missing the AuthenticateService object?
by ed young,
Re: Are you missing the AuthenticateService object?
by Fadi Shami,
Re: Are you missing the AuthenticateService object?
by Chen Jianfeng,
Re: Are you missing the AuthenticateService object?
by Chen Jianfeng,
Use lowercase in the RequestMapper URL patterns
by John Lindwall,
Re: Use lowercase in the RequestMapper URL patterns
by Franz Schmid,
Authentication with Active Directory
by Cross World,
BeanCreationException
by Heidi Nuhm,
How can I add new fields to AuthUser / User? Why 2 types of users?
by Bob Bowen,
Re: How can I add new fields to AuthUser / User? Why 2 types of users?
by yang poplar,
UrlMapping to redirect Error to a custom view throws IllegalStateException
by Stefan Maiwald,
Using Existing Acegi Components with Grails acegi plug in
by Chawki Mguedmini,
Some tips on Acegi and OpenId
by Rajiv Narula,
Dynamic Redirection issue
by Pooja bawa,
Problem with acegi when configuring pooled=true in Datasource.groovy
by Franz Schmid,
Your message is awaiting moderation. Thank you for participating in the discussion.
Hi,
first of all thank you very much for your (even for a newbie like me) easily understandable explanation.
First I got the example to work without problems. But to get better performance i wanted to have my database connections pooled. So I configured pooled=true in Datasource.groovy. As soon as i did this I could only call a few pages before the application stopped responding. After looking at debug output i think the reason is that acegi is waiting for getting another connection from connection pool.
Here are the last debug output statements:
org.acegisecurity.ui.WebAuthenticationDetails@255f8: RemoteIpAddress: 127.0.0.1; SessionId: 1raoz7gc3n0mf; Granted Authorities: ROLE_ANONYMOUS'
org.acegisecurity.providers.anonymous.AnonymousProcessingFilter Populated SecurityContextHolder with anonymous token: 'org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken@90572420: Username: anonymousUser; Password: [PROTECTED]; Authenticated: true; Details: org.acegisecurity.ui.WebAuthenticationDetails@255f8: RemoteIpAddress: 127.0.0.1; SessionId: 1raoz7gc3n0mf; Granted Authorities: ROLE_ANONYMOUS'
org.acegisecurity.util.FilterChainProxy /customer/list at position 7 of 8 in additional filter chain; firing Filter: 'org.acegisecurity.ui.ExceptionTranslationFilter@697087'
org.acegisecurity.util.FilterChainProxy /customer/list at position 7 of 8 in additional filter chain; firing Filter: 'org.acegisecurity.ui.ExceptionTranslationFilter@697087'
org.acegisecurity.util.FilterChainProxy /customer/list at position 8 of 8 in additional filter chain; firing Filter: 'org.acegisecurity.intercept.web.FilterSecurityInterceptor@5acf13'
org.acegisecurity.util.FilterChainProxy /customer/list at position 8 of 8 in additional filter chain; firing Filter: 'org.acegisecurity.intercept.web.FilterSecurityInterceptor@5acf13'
org.hibernate.impl.SessionImpl opened session at timestamp: 4933924451647488
org.hibernate.jdbc.AbstractBatcher about to open PreparedStatement (open PreparedStatements: 0, globally: 0)
org.hibernate.jdbc.ConnectionManager opening JDBC connection
I seached and posted in grails user maillist, unfortunately without result.
Do you know about this problem?
Are you missing the AuthenticateService object?
by ed young,
Your message is awaiting moderation. Thank you for participating in the discussion.
I went through the article and put it to use in several apps.
I think the plugin is missing the AuthenticateService.groovy file. Because of this, you can't register a new user.
Am I missing something?
Thanks
Re: Are you missing the AuthenticateService object?
by Fadi Shami,
Your message is awaiting moderation. Thank you for participating in the discussion.
Not sure about the problem that your are having , but the plugin has the AuthenticateService.groovy. You should be able to find it in
%your application dir%\plugins\acegi-0.2\grails-app\services
Use lowercase in the RequestMapper URL patterns
by John Lindwall,
Your message is awaiting moderation. Thank you for participating in the discussion.
I loosely followed this excellent tutorial but instead of using the race/register controllers I used my own. The URL of my controller includes an uppercase letter, e.g. "purchaseOrder".
If you configure the Requestmap using mixed-case then the Acegi will fail to patern-match that URL, and hence fail to protect those pages.
e.g. /purchaseOrder/**
By enabling Acegi debugging I could see that Acegi was configured to convert all URLs to lowercase before attempting pattern matching. To fix this problem, I used lowercase URL patterns in the Requestmap and it worked great.
e.g. /purchaseorder/**
Thanks for the excellent tutorial!
Re: Use lowercase in the RequestMapper URL patterns
by Franz Schmid,
Your message is awaiting moderation. Thank you for participating in the discussion.
Thanks for the help. AuthenticateService.groovy is there in plugins directory.
I tried some more things and found out that if I use static requestMapString instead of useRequestMapDomainClass it works fine with my pooled datasource.
So it seems to me that there is a problem in data access (perhaps release of connections not correctly implemented) when loading database contents of table requestmap.
Re: Are you missing the AuthenticateService object?
by Chen Jianfeng,
Your message is awaiting moderation. Thank you for participating in the discussion.
Under %your application dir%\plugins\acegi-0.2\grails-app\services,I find no
AuthenticateService.groovy.But now I can register new users.
When trying to grails generate-registration,I can't get connection to Internet because I use the compus net and download jars then put them into lib file.First time I also could not register users.
And now I am confused.
Authentication with Active Directory
by Cross World,
Your message is awaiting moderation. Thank you for participating in the discussion.
Hi,
Thank you very much for your easily understandable tutorial.
Is there also any solution, to authenticate users from an Active Directory.
Can you tell me how to inplement that with grails or do you know any tutorials?
greets
Re: Are you missing the AuthenticateService object?
by Chen Jianfeng,
Your message is awaiting moderation. Thank you for participating in the discussion.
I have found the AuthenticateService.groovy in plugin directory.
Thanks!
BeanCreationException
by Heidi Nuhm,
Your message is awaiting moderation. Thank you for participating in the discussion.
Hi,
I've installed acegi in my application and have generated the domains, managers und registration. But when I try to compile and restart my Application, I got a BeanCreationException. I had to import the AuthenticateService manual. And there are errors in my Logincontroller like it doesn't find the imported packages.
Can anybody help me?
How can I add new fields to AuthUser / User? Why 2 types of users?
by Bob Bowen,
Your message is awaiting moderation. Thank you for participating in the discussion.
I am trying to add some additional fields to my user forms. I did so by editing AuthUser.groovy and adding them. I then ran grails generate-all AuthUser and it created the new create, and edit gsps.
But the problem is:
1. In the tutorial UserController is used, NOT AuthUserController. When I go to create a new User using UserController my new fields aren't there. How can I get Grails to generate my gsp's for user?
2. If I instead use the AuthUser controller, my new fields show up but the password doesn't save correctly. This is because AuthUserController doesn't have hardly any of the code UserController has, such as ciphering the password. Also, roles don't show up in the page, and I imagine won't be saved if I stick them there by hand.
I am not sure what the difference is between AuthUser and User but there is only one domain object, AuthUser, and only one table, auth_user. So if I want to add additional fields do I edit the AuthUser domain object but then edit by hand user/create.gsp, user/edit.gsp?
Thanks,
Bob
UrlMapping to redirect Error to a custom view throws IllegalStateException
by Stefan Maiwald,
Your message is awaiting moderation. Thank you for participating in the discussion.
First of all, thanks for that helpful tutorial.
As far as described, I was able to follow and to make it work. Even the basic tutorial at the grails.org site works.
The next step I try to redirect to a more user friendly view when the HTTP Error 403 comes via UrlMappings.groovy like:
But this leads to an Exception:
...
Nested in org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request
...
Seems that there's something missing?
Cheers
Stefan
Re: Problem with acegi when configuring pooled=true in Datasource.groovy
by Stefan Maiwald,
Your message is awaiting moderation. Thank you for participating in the discussion.
Hello Franz,
I discovered the same problem in a similar environment.
Seems like a bug.
Cheers
S.
Re: How can I add new fields to AuthUser / User? Why 2 types of users?
by yang poplar,
Your message is awaiting moderation. Thank you for participating in the discussion.
after you modify your authUser, use "grails generate-manager" to complete your user contoll.
Using Existing Acegi Components with Grails acegi plug in
by Chawki Mguedmini,
Your message is awaiting moderation. Thank you for participating in the discussion.
First of all, thank you for this excellent tutorial.
I want to know if is't possible to use an already existing Acegi Components with the Grails acegi plugin and how.
Thanks in advance
Chawki
Some tips on Acegi and OpenId
by Rajiv Narula,
Your message is awaiting moderation. Thank you for participating in the discussion.
I faced some issues while implementing OpenId with Acegei
See my post below
www.rajivnarula.com/blog/2009/11/16/grails-and-...
HTH
Dynamic Redirection issue
by Pooja bawa,
Your message is awaiting moderation. Thank you for participating in the discussion.
Hi,
I am stuck on dynamically redirecting user to original page after redirect to Login page, please let through example.
Regards