alexgorbatchev

Friday, March 6, 2015

Grails 3 App with Security (Part 1)

The great Spring Security Plugin is now available for Grails 3!

I will leave these blog posts up as a reference, but I would strongly suggest (as if you needed the suggestion) to use the Spring-Security plugin for Grails 3

Since Spring Security Core Plugin is not was not working for Grails 3, and I could not find any other resources for the spring-boot-starter-security related to Grails 3 I decided to share what I did to get up and going. I created a github project at https://github.com/dspies/grails-3-with-security and tagged each step if you want to follow along.
Uses:
  • Grails Version: 3.0.0.M2
  • Groovy Version: 2.4.1
  • JVM Version: 1.7.0_51

Setting up Security:

Simply add the following to build.gradle in the dependencies:
compile "org.springframework.boot:spring-boot-starter-security"
This will cause ALL HTTP endpoints to require authorization and a random password to be generated each time you start up your app. Also, because Spring-Boot-Security-Starter logs the randomly generated password at level INFO, you need to add the following to your logback.groovy file.
 //see http://logback.qos.ch/manual/groovy.html for more info
 logger('org.springframework.boot.autoconfigure.security', INFO)
This is not sufficient for anything but a demo, so let's customize it a bit. Let's start with the easiest part, creating a fixed password. There are a few methods of setting a static password, but we'll concentrate on two 1) in the application.yml or 2) in a WebSecurityConfigurerAdapter Class.

In the Application.yml file

    security:
        user:
            password: password

In a WebSecurityConfigurerAdapter Class

Using a WebSecurityConfigurerAdapter class, we can override the default implementation(s) used in spring-boot-starter-security.

grails-app/init/simpleappwithsecurity/SecurityConfiguration.groovy
package simpleappwithsecurity

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .inMemoryAuthentication()
                .withUser("user").password("pwd").roles("USER");
    }
}
One important note, Grails does NOT know anything about the SecurityConfiguration class unless you do something to tell your Grails' app about the new class. The two easiest methods are to specify a bean in resources.groovy
grails-app/conf/spring/resources.groovy
import simpleappwithsecurity.SecurityConfiguration

beans = {
    webSecurityConfiguration(SecurityConfiguration)
}
If you use this, you do not need the class annotations @Configuration and @EnableWebSecurity on the SecurityConfiguration class. Another method is to enable Spring's Component Scan on the Grails application in the Application.groovy file by adding @ComponentScan to the Application class, such as
grails-app/init/Application.groovy

...
import org.springframework.context.annotation.ComponentScan;

@ComponentScan
class Application extends GrailsAutoConfiguration {
...
I think this depends on how you view your application. If it is a Grails Application, using primarily Grails Plugins with Spring sprinkled in, then use the first method. If it is a Spring Boot application with Grails sprinkled in, use the latter method. For now, our app is a Grails app, so we'll use the bean definition in resources.groovy.

Securing specific URIs (or request maps)

Let's extend our example by adding pattern matches to our SecurityConfiguration class.
grails-app/init/simpleappwithsecurity/SecurityConfiguration.groovy
...
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers('/admin/**').hasAnyRole('ADMIN')
                .antMatchers('/home/**').hasAnyRole('USER', 'ADMIN')
                .antMatchers('/').permitAll()
            .and()
                .formLogin().permitAll()
            .and()
                .logout().permitAll()
    }

    //<-- --="" .inmemoryauthentication="" .withuser="" auth="" code="" configureglobal="" end="" exception="" in="" of="" password="" previous="" public="" roles="" snippet="" throws="" user="" uthenticationmanagerbuilder="" utowired="" void="">
                .and()
                .withUser('admin').password('admin').roles('ADMIN');
    }

...
Restarting the application now will allow you to see the index page, but require authentication when attempting to access /home or /admin controllers.

Resources: