Archive for December 1st, 2009

SpringOne 2GX Sample Apps – Spring Security Basic Auth Login

Tuesday, December 01st, 2009

This is the third in a series of posts making the demo applications that I used for my SpringOne 2GX presentations available. I’ll describe here how to create a Grails application using the Spring Security plugin that authenticates users with HTTP Basic Auth. This was used in the Demystifying Spring Security in Grails talk (you can download the presentation here).

Also refer to the plugin documentation for other tutorials here.
(more…)

SpringOne 2GX Sample Apps – Spring Security LDAP Login

Tuesday, December 01st, 2009

This is the second in a series of posts making the demo applications that I used for my SpringOne 2GX presentations available. I’ll describe here how to create a Grails application using the Spring Security plugin that authenticates users from LDAP. This is based on topics from the Demystifying Spring Security in Grails talk (you can download the presentation here) but wasn’t shown there since I ran out of time.

Also refer to the plugin documentation for other tutorials here.


To create an application that authenticates users from LDAP, run

grails create-app springone2gx_ldap
cd springone2gx_ldap

To make classpath management simpler in Eclipse/STS I create a grails-app/conf/BuildConfig.groovy (in Grails 1.1 apps; in 1.2 this is done for you) with the line

grails.project.plugins.dir='plugins'

to keep plugins in the project root like in 1.0.x but this is optional.

Next install the plugin:

grails install-plugin acegi

Run the create-auth-domains script to generate the person, authority, and request map domain classes and also grails-app/conf/SecurityConfig.groovy:

grails create-auth-domains com.burtbeckwith.springone2gx.User com.burtbeckwith.springone2gx.Role com.burtbeckwith.springone2gx.Requestmap

The other two scripts that the plugin provides are optional and create CRUD pages (generate-manager) and basic user registration (generate-registration). It’s a good idea to run generate-manager; run generate-registration if it’s useful to you.

grails generate-manager

As with the previous post we’ll use annotated controllers, so we’ll need to configure that, and we can delete the request map class and CRUD pages. The plugin scripts currently asssume you’ll be using request maps, so we have to run generate-manager and generate-registration before deleting these.

  • delete grails-app/domain/com/burtbeckwith/springone2gx/Requestmap.groovy
  • delete grails-app/controller/RequestmapController.groovy
  • delete the grails-app/views/requestmap directory and its GSPs
  • remove the com.burtbeckwith.springone2gx.Requestmap import from grails-app/controller/RoleController.groovy
  • in grails-app/conf/SecurityConfig.groovy, disable requestmaps (useRequestMapDomainClass = false) and enable annotations (useControllerAnnotations = true), and remove the requestMapClass property:
    security {
    
       active = true
    
       loginUserDomainClass = "com.burtbeckwith.springone2gx.User"
       authorityDomainClass = "com.burtbeckwith.springone2gx.Role"
    
       useRequestMapDomainClass = false
       useControllerAnnotations = true
    }
    

In Eclipse or STS the steps to configure the classpath are:

  • add PLUGIN_DIR/src/groovy as a source folder
  • add PLUGIN_DIR/src/java as a source folder
  • add PLUGIN_DIR/grails-app/services as a source folder
  • add these jars from PLUGIN_DIR/lib
    • facebook-java-api-2.0.4.jar
    • jcifs-1.2.25.jar
    • spring-ldap-1.2.1.jar
    • spring-ldap-tiger-1.2.1.jar
    • spring-security-core-2.0.4.jar
    • spring-security-core-tiger-2.0.4.jar
    • spring-security-ntlm-2.0.4.jar
    • spring-security-openid-2.0.4.jar

Having done all that, let’s create a secured controller to test annotations:

grails create-controller secure

and add the import for the annotation, and annotate at the class level that you must be an admin to access this controller:

import org.codehaus.groovy.grails.plugins.springsecurity.Secured

@Secured(['ROLE_ADMIN'])
class SecureController {

   def index = {
      redirect action: foo
   }

   def foo = {
      render 'OK'
   }

   def bar = {
      render 'also OK'
   }
}

The controller has a redirect from the default action and a second action so we can test that all methods inherit the class-level annotation.

Next lets configure LDAP. To make this a self-contained demo, we’ll use the excellent LDAP server plugin but obviously you’ll need to configure the application to connect to your LDAP server.

Install the plugin by running

grails install-plugin ldap-server

and add the necessary LDAP configuration option to grails-app/conf/SecurityConfig.groovy (at a minimum useLdap = true)

security {
   ...
   useLdap = true
   ldapServer = 'ldap://localhost:10389'
   ldapManagerDn = 'uid=admin,ou=system'
   ldapManagerPassword = 'secret'
   ldapSearchBase = 'dc=d1,dc=example,dc=com'
   ldapSearchFilter = '(uid={0})'
   ldapGroupSearchBase = 'ou=groups,dc=d1,dc=example,dc=com'
   ldapGroupSearchFilter = 'uniquemember={0}'
   ldapUsePassword = false
}

The LDAP plugin requires one or more configured LDAP servers in grails-app/conf/Config.groovy and we’ll need just one:

ldapServers {
   d1 {
      base = 'dc=d1,dc=example,dc=com'
      port = 10389
      indexed = ['objectClass', 'uid', 'mail', 'userPassword', 'description']
   }
}

The plugin will auto-load .ldif data files with user information, so put these records in grails-app/ldap-servers/d1/data/users.ldif:

dn: ou=groups,dc=d1,dc=example,dc=com
objectclass: organizationalUnit
objectclass: top
ou: groups

dn: cn=USER,ou=groups,dc=d1,dc=example,dc=com
objectclass: groupOfUniqueNames
cn: USER
objectclass: top
uniqueMember: cn=person1,dc=d1,dc=example,dc=com
uniqueMember: cn=person2,dc=d1,dc=example,dc=com
uniqueMember: cn=person3,dc=d1,dc=example,dc=com

dn: cn=ADMIN,ou=groups,dc=d1,dc=example,dc=com
objectclass: groupOfUniqueNames
objectclass: top
cn: ADMIN
uniqueMember: cn=person2,dc=d1,dc=example,dc=com

dn: cn=person1,dc=d1,dc=example,dc=com
objectClass: uidObject
objectClass: person
objectClass: top
objectClass: organizationalPerson
uid: person1
userPassword: {SHA}44rSFJQ9qtHWTBAvrsKd5K/p2j0=
cn: person1
sn: jones

dn: cn=person2,dc=d1,dc=example,dc=com
objectClass: uidObject
objectClass: person
objectClass: top
objectClass: organizationalPerson
uid: person2
userPassword: {SHA}KqYKj/f81HPTIeAUav2eJt85UUc=
cn: person2
sn: jones

dn: cn=person3,dc=d1,dc=example,dc=com
objectClass: uidObject
objectClass: person
objectClass: top
objectClass: organizationalPerson
uid: person3
userPassword: {SHA}ERnP037iRzV+A0oI2ETuol9v0g8=
cn: person3
sn: jones

Spring Security will by default convert LDAP groups (‘groupOfUniqueNames’) to roles, prefixing the group names with ROLE_, so this data creates three users; person1, person2, and person3 (with passwords ‘password1’, ‘password2’, and ‘password3’ respectively), all with ROLE_USER and person2 with ROLE_ADMIN.

Since LDAP is only managing authentication details we need local data in the database; create the corresponding entries in BootStrap:

import com.burtbeckwith.springone2gx.User

class BootStrap {

   def init = { servletContext ->
      new User(username: 'person1', enabled: true).save()
      new User(username: 'person2', enabled: true).save()
      new User(username: 'person3', enabled: true).save(flush: true)
   }

   def destroy = {}
}

Since LDAP is handling authentication, we can (partially) remove password-related fields from User.groovy along with other unused fields:

class User {

   static hasMany = [authorities: Role]
   static belongsTo = Role

   String username
   String passwd = 'notused'
   boolean enabled

   static constraints = {
      username blank: false, unique: true
   }
}

We need to leave in the passwd property since GrailsDaoImpl expects it but its value isn’t important so we’ll just hard-code it in the domain class. A custom subclass GrailsDaoImpl or a new implementation of UserDetailsService would remove this requirement.

Start the app using

grails run-app

and open http://localhost:8080/springone2gx_ldap/secure/ in a browser and it should prompt you to login. If you login as person1 or person3 you’ll be denied access since those users only have ROLE_USER but person2 has ROLE_ADMIN and will be allowed.

After successful login it’ll redirect to http://localhost:8080/springone2gx_ldap/secure/foo – verify that http://localhost:8080/springone2gx_ldap/secure/bar is also secured by going to that in your browser.


You can download a finished application based on this discussion here

SpringOne 2GX Sample Apps – Spring Security Form Login

Tuesday, December 01st, 2009

This is the first in a series of posts making the demo applications that I used for my SpringOne 2GX presentations available. I’ll describe here how to create a standard Grails application using the Spring Security plugin that authenticates users from a database. This was used in the Demystifying Spring Security in Grails talk (you can download the presentation here)

Also refer to the plugin documentation for other tutorials here.


To create a standard application that loads users from a database, run

grails create-app springone2gx
cd springone2gx

To make classpath management simpler in Eclipse/STS I create a grails-app/conf/BuildConfig.groovy (in Grails 1.1 apps; in 1.2 this is done for you) with the line

grails.project.plugins.dir='plugins'

to keep plugins in the project root like in 1.0.x but this is optional.

Next install the plugin:

grails install-plugin acegi

Run the create-auth-domains script to generate the person, authority, and request map domain classes and also grails-app/conf/SecurityConfig.groovy:

grails create-auth-domains com.burtbeckwith.springone2gx.User com.burtbeckwith.springone2gx.Role com.burtbeckwith.springone2gx.Requestmap

The other two scripts that the plugin provides are optional and create CRUD pages (generate-manager) and basic user registration (generate-registration). It’s a good idea to run generate-manager; run generate-registration if it’s useful to you.

grails generate-manager

There are three methods for securing URLs in the plugin and for this demo we’ll use controller annotations, so we’ll need to configure that. We can delete the request map class, controller, and CRUD pages since they won’t be needed. The plugin scripts currently assume you’ll be using request maps, so we have to run generate-manager and generate-registration before deleting these.

  • delete grails-app/domain/com/burtbeckwith/springone2gx/Requestmap.groovy
  • delete grails-app/controller/RequestmapController.groovy
  • delete the grails-app/views/requestmap directory and its GSPs
  • remove the com.burtbeckwith.springone2gx.Requestmap import from grails-app/controller/RoleController.groovy
  • in grails-app/conf/SecurityConfig.groovy, disable requestmaps (useRequestMapDomainClass = false) and enable annotations (useControllerAnnotations = true), and remove the requestMapClass property:
    security {
    
       active = true
    
       loginUserDomainClass = "com.burtbeckwith.springone2gx.User"
       authorityDomainClass = "com.burtbeckwith.springone2gx.Role"
    
       useRequestMapDomainClass = false
       useControllerAnnotations = true
    }
    

The current plugin is monolithic – it has support for several authentication mechanisms (OpenID, Facebook, etc.) If you’re not using these you can delete that code and associated jar files. This is completely optional, and you can always add them back by extracting them from the plugin zip.

If you’re not using OpenID, here are the steps to remove that code:

  • delete the org.codehaus.groovy.grails.plugins.springsecurity.openid package: PLUGIN_DIR/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/openid
  • delete these jars
    • openid4java-0.9.2.jar
    • spring-security-openid-2.0.4.jar
    • xmlsec-1.3.0.jar
    • htmlparser-1.6.jar
    • commons-httpclient-3.0.1.jar
    • openxri-client.jar
    • openxri-syntax.jar
  • in LoginController.groovy
    • delete def openIDConsumer
    • delete def openIDAuthenticationProcessingFilter
    • delete the openIdAuthenticate() action
    • remove the config.useOpenId part in the auth() action

If you’re not using Facebook, here are the steps to remove that code:

  • delete the org.codehaus.groovy.grails.plugins.springsecurity.facebook package: PLUGIN_DIR/src/java/org/codehaus/groovy/grails/plugins/springsecurity/facebook
  • delete these jars
    • facebook-java-api-2.0.4.jar
    • json-20070829.jar
  • in LoginController.groovy
    • remove the config.useFacebook part in the auth() action

If you’re not using Kerberos, here are the steps to remove that code:

  • delete the org.codehaus.groovy.grails.plugins.springsecurity.kerberos package: PLUGIN_DIR/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/kerberos

If you’re not using LDAP, here are the steps to remove that code:

  • delete the org.codehaus.groovy.grails.plugins.springsecurity.ldap package: PLUGIN_DIR/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/ldap and PLUGIN_DIR/src/java/org/codehaus/groovy/grails/plugins/springsecurity/ldap
  • delete these jars
    • spring-ldap-1.2.1.jar
    • spring-ldap-tiger-1.2.1.jar
  • in LoginController.groovy
    • remove the config.useFacebook part in the auth() action

If you’re not using CAS, here are the steps to remove that code:

  • delete these jars

    • cas-client-core-3.1.1.jar
    • spring-security-cas-client-2.0.4.jar

Even if you don’t use NTLM it’s best to leave it in – the jars and code aren’t large and it’s more complicated to remove than just deleting.

In Eclipse or STS the steps to configure the classpath are:

  • add PLUGIN_DIR/src/groovy as a source folder
  • add PLUGIN_DIR/src/java as a source folder
  • add PLUGIN_DIR/grails-app/services as a source folder
  • add PLUGIN_DIR/lib/spring-security-core-2.0.4.jar
  • add PLUGIN_DIR/lib/spring-security-core-tiger-2.0.4.jar
  • add PLUGIN_DIR/lib/spring-security-ntlm-2.0.4.jar
  • add PLUGIN_DIR/lib/jcifs-1.2.25.jar

Having done all that, let’s create a secured controller to test annotations:

grails create-controller secure

and add the import for the annotation, and annotate at the class level that you must be an admin to access this controller:

import org.codehaus.groovy.grails.plugins.springsecurity.Secured

@Secured(['ROLE_ADMIN'])
class SecureController {

   def index = {
      redirect action: foo
   }

   def foo = {
      render 'OK'
   }

   def bar = {
      render 'also OK'
   }
}

The controller has a redirect from the default action and a second action so we can test that all methods inherit the class-level annotation.

Hitting this controller will require a login, so lets add code to BootStrap to create a user:

import com.burtbeckwith.springone2gx.Role
import com.burtbeckwith.springone2gx.User

class BootStrap {

   def passwordEncoder

   def init = { servletContext ->

      def adminRole = new Role(description: 'Admin role',
                  authority: 'ROLE_ADMIN').save()

      String password = passwordEncoder.encodePassword('p4ssw0rd', null)
      def me = new User(username: 'admin',
                  passwd: password, enabled: true).save()
      adminRole.addToPeople(me)
      adminRole.save(flush: true)
   }

   def destroy = {}
}

Start the app using

grails run-app

and open http://localhost:8080/springone2gx/secure/ in a browser and it should prompt you to login – use the username and password from the user created in BootStrap.

After successful login it’ll redirect to http://localhost:8080/springone2gx/secure/foo – verify that http://localhost:8080/springone2gx/secure/bar is also secured by going to that in your browser.


The ‘secure’ controller blocks access, but anyone can access the role or user controller – open http://localhost:8080/springone2gx/user/list to verify that. So let’s finish locking down the app.

Add the same annotation to UserController and RoleController that you added to SecureController:

import org.codehaus.groovy.grails.plugins.springsecurity.Secured

@Secured(['ROLE_ADMIN'])
class UserController {
...
}
import org.codehaus.groovy.grails.plugins.springsecurity.Secured

@Secured(['ROLE_ADMIN'])
class RoleController {
...
}

Up to now we’ve assumed that all access should be allowed unless it’s explicitly blocked. This is appropriate for many applications, but you might want to take the pessimistic approach of denying access unless it’s allowed. That’s simple to do.

Set controllerAnnotationsRejectIfNoRule to true in grails-app/conf/SecurityConfig.groovy, and since we can’t annotate CSS/JavaScript/images/etc., we’ll need to allow access to those using the controllerAnnotationStaticRules property:

security {

   active = true

   loginUserDomainClass = 'com.burtbeckwith.springone2gx.User'
   authorityDomainClass = 'com.burtbeckwith.springone2gx.Role'

   useRequestMapDomainClass = false
   useControllerAnnotations = true

   controllerAnnotationsRejectIfNoRule = true
   controllerAnnotationStaticRules = [
      '/**/js/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],
      '/**/css/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],
      '/**/images/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],
      '/*': ['IS_AUTHENTICATED_ANONYMOUSLY']
   ]
}

Users won’t be able to log in now, since you’re now allowing access to LoginController. That’s easy to do: add an annotation:

import org.codehaus.groovy.grails.plugins.springsecurity.Secured

@Secured(['IS_AUTHENTICATED_ANONYMOUSLY'])
class LoginController {
...
}

IS_AUTHENTICATED_ANONYMOUSLY means that any access is allowed – “anonymous” logins as well as real logins. Do the same for LogoutController, and CaptchaController and RegisterController if you’re using the registration features of the plugin.

Using this approach, new controllers will be inaccessible until you annotate them. If you need the pessimistic approach, this is preferred to the alternative that controllers accidentally allow full access because you forgot to lock them down.


You can download a finished application based on this discussion here

Creative Commons License
This work is licensed under a Creative Commons Attribution 3.0 License.