星期日, 十月 05, 2014

The final mile: Upgrade to Grails 2.4.3 and use Spring Security REST plugin

The final mile: Upgrade to Grails 2.4.3 and use Spring Security REST plugin

After I wrote down this series of Create a restful application with AngularJS and Grails, I have received some feedback from my blogspot and mail. I decide to update this sample to the latest Grails and use the Spring Security REST plugin instead of my customized solution, it is more powerful and flexible.

Upgrade to Grails 2.4.3

  1. Update the Grails version to 2.4.3, which is the newest when I wrote this. You can change the value in application.properties file directly or using grails command to complete the work.
    grails set-grails-version 2.4.3
    
  2. Please read the Upgrading from Grails 2.3 section of the official reference document to update dependencies manually. Note: The upgrade command was removed in Grails 2.4, currently this manual approach is the only way to upgrade your application to the latest 2.4.x.
  3. Make sure all things are done well. Run the application via command line.
    grails run-app
    
If there is no exception or error info displayed in the console, that means you have upgraded your application successfully. Congratulations!

Configure Spring Security REST Plugin

Spring Security REST plugin is an extension of Spring Security plugin which allow you create stateless, token-based authentication for your REST API.
  1. Open BuildConfig.groovy file, and add Spring Security REST Plugin in the plugins section.
    compile ":spring-security-rest:1.4.0", {
        excludes: 'spring-security-core'
    }
    
  2. Add the basic configuration in the Config.groovy file.
    //Config for Spring Security REST plugin
    
    //login
    grails.plugin.springsecurity.rest.login.active=true
    grails.plugin.springsecurity.rest.login.endpointUrl="/api/login"
    grails.plugin.springsecurity.rest.login.failureStatusCode=401
    grails.plugin.springsecurity.rest.login.useJsonCredentials=true
    grails.plugin.springsecurity.rest.login.usernamePropertyName='username'
    grails.plugin.springsecurity.rest.login.passwordPropertyName='password'
    
    //logout
    grails.plugin.springsecurity.rest.logout.endpointUrl='/api/logout'
    
    
    //token generation
    grails.plugin.springsecurity.rest.token.generation.useSecureRandom=true
    grails.plugin.springsecurity.rest.token.generation.useUUID=false
    
    //token storage
    // use memcached.
    //grails.plugin.springsecurity.rest.token.storage.useMemcached  false
    //grails.plugin.springsecurity.rest.token.storage.memcached.hosts   localhost:11211
    //grails.plugin.springsecurity.rest.token.storage.memcached.username    ''
    //grails.plugin.springsecurity.rest.token.storage.memcached.password    ''
    //grails.plugin.springsecurity.rest.token.storage.memcached.expiration  3600
    
    //use GROM
    //grails.plugin.springsecurity.rest.token.storage.useGorm   false
    //grails.plugin.springsecurity.rest.token.storage.gorm.tokenDomainClassName null
    //grails.plugin.springsecurity.rest.token.storage.gorm.tokenValuePropertyName   tokenValue
    //grails.plugin.springsecurity.rest.token.storage.gorm.usernamePropertyName username
    //class AuthenticationToken {
    //
    //  String tokenValue
    //  String username
    //}
    
    //use cache as storage
    grails.plugin.springsecurity.rest.token.storage.useGrailsCache=true
    grails.plugin.springsecurity.rest.token.storage.grailsCacheName='xauth-token'
    
    //token rendering
    grails.plugin.springsecurity.rest.token.rendering.usernamePropertyName='username'
    grails.plugin.springsecurity.rest.token.rendering.authoritiesPropertyName='roles'
    grails.plugin.springsecurity.rest.token.rendering.tokenPropertyName='token'
    
    
    //token validate
    grails.plugin.springsecurity.rest.token.validation.useBearerToken = true
    
    //if disable 'Bearer', you can configure a custom header.
    //grails.plugin.springsecurity.rest.token.validation.useBearerToken = false
    //grails.plugin.springsecurity.rest.token.rendering.tokenPropertyName   access_token
    //grails.plugin.springsecurity.rest.token.validation.headerName = 'x-auth-token'
    grails.plugin.springsecurity.rest.token.validation.active=true
    grails.plugin.springsecurity.rest.token.validation.endpointUrl='/api/validate'
    
    grails{
        plugin{
            springsecurity{
                filterChain{
                    chainMap = [
                        '/api/guest/**': 'anonymousAuthenticationFilter,restTokenValidationFilter,restExceptionTranslationFilter,filterInvocationInterceptor',
                        '/api/**': 'JOINED_FILTERS,-exceptionTranslationFilter,-authenticationProcessingFilter,-securityContextPersistenceFilter',  // Stateless chain
                        '/**': 'JOINED_FILTERS,-restTokenValidationFilter,-restExceptionTranslationFilter'                                          // Traditional chain
                    ]
                }
    
                rest {
                    token { validation { enableAnonymousAccess = true } }
                }
            }
        }
    }
    
More details for the configuration, please read the plugin docs.

Configure CORS

By default there is a CORS plugin included as a dependency of Spring security REST Plugin.
Of course, you can declare it in BuildConfig.groovy file explicitly.
runtime ":cors:1.1.6"
Add the following configuration in Config.groovy file.
//cors config.
cors.enabled=true
cors.url.pattern = '/api/*'
cors.headers=[
    'Access-Control-Allow-Origin': '*',
    'Access-Control-Allow-Credentials': true,
    'Access-Control-Allow-Headers': 'origin, authorization, accept, content-type, x-requested-with',
    'Access-Control-Allow-Methods': 'GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS',
    'Access-Control-Max-Age': 3600
    ]

Update frontend codes

Due to the modification of the backend codes, you could have to change the authentication in the frontend codes.
Change the login code fragment in the app.js file.
$http.post(apiUrl+'/login', {username: username, password: password})
      .success(function(user){
            console.log('logged in successfully!')
            $rootScope.user = user;
            $http.defaults.headers.common['Authorization'] = 'Bearer '+user.token;
            $cookieStore.put('user', user);
            $rootScope.$broadcast('event:loginConfirmed');                               
       });

Run the project

  1. I have committed the codes into my github account. Clone it into your system.
    git clone https://github.com/hantsy/angularjs-grails-sample
    
  2. Run the backend application. Go to the server folder, and execute the following command to start up the Grails application which works as a REST producer.
    grails run-app
    
  3. Run the frontend application. Go to the client folder, and run the following to serve the frontend application.
    node scripts\web-server.js
    
  4. Open your favorite browser and navigate http://localhost:8000/app.
Enjoy!

星期三, 十二月 18, 2013

Create a restful application with AngularJS and Grails(4):Standalone AngularJS application


Standalone AngularJS application

In the real world applications, it is usually required to integrate the third party APIs into the project. Most of case, the third party service is provided as flexible REST APIs for developers.
Imagine the REST API in this sample could sever other applications, such as a mobile app or other website in future, ideally it should be designated as an API centric application.
The AngularJS codes can be moved to a standalone application, and consume the REST web service remotely, like other applications.
Split the original codes into two projects.
/
 /client
 /server
The client is an AngularJS based application. The codes are based on AngularJS Seed sandbox. I copied the app folder in the former Grails application.
The server is nearly no difference from the original Grails part, but the app folder in the web-app folder is moved to client. Additionally, you have to configure CORS in the server side.

Configure CORS

There is a good post from the Spring community to help you understand CORS(Cross-Origin Resource Sharing).
The post provides a solution to resolve the limitation of cross domain resource accessing.
But I would like use a CORS filter from Ebay, I have used it in projects and it has some configuration options.
By default, the Grails application does not includes a web.xml file, it is generated from the default templates at runtime. Use the following command to install the templates for this projects.
grails install-templates
Open the web.xml file under the src/templates/war folder.
Configure CORS filter in the web.xml.
<filter>
 <filter-name>CORS Filter</filter-name>
 <filter-class>org.ebaysf.web.cors.CORSFilter</filter-class>
 <init-param>
  <description>A comma separated list of allowed origins. Note: An '*' cannot be used for an allowed origin when using credentials.</description>
  <param-name>cors.allowed.origins</param-name>
  <param-value>http://localhost:8000, http://localhost:8080</param-value>
 </init-param>
 <init-param>
  <description>A comma separated list of HTTP verbs, using which a CORS
   request can be made.</description>
  <param-name>cors.allowed.methods</param-name>
  <param-value>GET,POST,HEAD,OPTIONS,PUT,DELETE</param-value>
 </init-param>
 <init-param>
  <description>A comma separated list of allowed headers when making a non simple CORS request.</description>
  <param-name>cors.allowed.headers</param-name>
  <param-value>Content-Type,X-Requested-With,Authorization,Origin,Access-Control-Request-Method,Access-Control-Request-Headers</param-value>
 </init-param>
 <init-param>
  <description>A comma separated list non-standard response headers that will be exposed to XHR2 object.</description>
  <param-name>cors.exposed.headers</param-name>
  <param-value></param-value>
 </init-param>
 <init-param>
  <description>A flag that suggests if CORS is supported with cookies</description>
  <param-name>cors.support.credentials</param-name>
  <param-value>true</param-value>
 </init-param>
 <init-param>
  <description>A flag to control logging</description>
  <param-name>cors.logging.enabled</param-name>
  <param-value>true</param-value>
 </init-param>
 <init-param>
  <description>Indicates how long (in seconds) the results of a preflight request can be cached in a preflight result cache.</description>
  <param-name>cors.preflight.maxage</param-name>
  <param-value>10</param-value>
 </init-param>
</filter>
<filter-mapping>
 <filter-name>CORS Filter</filter-name>
 <url-pattern>/*</url-pattern>
</filter-mapping>
Run the server project as a Grails application from command line or IDE. Now it works as a REST API producer and severs client applications.
And then run the client project as a NodeJS application.
node scripts\web-server.js

Sample codes

Create a restful application with AngularJS and Grails(3): Authentication and Authorization


Authentication and Authorization

Grails provides a series of built-in authentication solutions, such as Form, Basic, Digest etc. And there are several additional plugins which provides CAS, OAuth authentication, please search them from the official Grails.org website.
For API centric applications, Basic is the simplest authentication.

Configure Basic authentication

By default, Form based authentication is enabled, it is easy to configure Basic authentication in Grails application.
Includes the following line in the Config.groovy file.
grails.plugin.springsecurity.useBasicAuth = true
Basic authentication includes a specific basicExceptionTranslationFilter, so the general-purpose exceptionTranslationFilter can be excluded.
grails.plugin.springsecurity.filterChain.chainMap = [
 '/api/**':'JOINED_FILTERS,-exceptionTranslationFilter',
 '/**':JOINED_FILTERS,-basicAuthenticationFilter,-basicExceptionTranslationFilter'
 ]
All resources matched /api/** will be protected and require authentication.
Try access the a protected resource, for example, http://localhost:8080/angularjs-grails-sample/api/books.json. There is a browser prompt popup for requiring username and password.

Stateless API

By default, Grails will create session to store the client principle, it is useful for a web application. For a REST API, it is usually designated as stateless.
Spring security provides a stateless option in http element. In Grails, you could have to configure it yourself.
In the resources.groovy file, declare a SecurityContextRepository andSecurityContextPersistenceFilter bean.
statelessSecurityContextRepository(NullSecurityContextRepository) {}

statelessSecurityContextPersistenceFilter(SecurityContextPersistenceFilter, ref('statelessSecurityContextRepository')) { }
The SecurityContextPersistenceFilter is responsible for session creation, and it delegates the real work to SecurityContextRepository bean.NullSecurityContextRepository is an implementation of SecurityContextRepository which does not create the user data in HttpSession, it is suitable for stateless case.
Apply it in Config.groovy.
grails.plugin.springsecurity.filterChain.chainMap = [
 '/api/**': 'statelessSecurityContextPersistenceFilter,logoutFilter,authenticationProcessingFilter,customBasicAuthenticationFilter,securityContextHolderAwareRequestFilter,rememberMeAuthenticationFilter,anonymousAuthenticationFilter,basicExceptionTranslationFilter,filterInvocationInterceptor',
 ]
In the above, all filters used for /api/ url pattern are listed one by one.
There are some options for the configuration of the filters.
  • If the value includes a JOINED_FILTERS, it is indicates it will includes all default filters. You can append -filterName to exclude the filter from the default filter list. For example,
   JOINED_FILTERS,-basicExceptionTranslationFilter
It will include all filters but excludes basicExceptionTranslationFilter.
  • If the value is none, security will skip the url pattern.
  • You can specify the filters one by one.
Note: The exclusion can be used only with JOINED_FILTERS option.
I also create a custom BasicAuthenticationEntryPoint.
public class CustomBasicAuthenticationEntryPoint extends
  BasicAuthenticationEntryPoint {

 private static Logger log = LoggerFactory
   .getLogger(CustomBasicAuthenticationEntryPoint.class);

 @Override
 public void commence(HttpServletRequest request,
   HttpServletResponse response, AuthenticationException authException)
   throws IOException, ServletException {
  // TODO Auto-generated method stub
  // super.commence(request, response, authException);
  log.debug("call @ commence...");
  response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
 }

}
The purpose is simple, it maps all authentication and authorization exception to 401 status. This will simplify the frontend AngluarJS processing work.
Configure this AuthenticationEntryPoint in resources.groovy.
customBasicAuthenticationEntryPoint(CustomBasicAuthenticationEntryPoint) {
 realmName = SpringSecurityUtils.securityConfig.basic.realmName // 'Grails Realm'
}

customBasicAuthenticationFilter(BasicAuthenticationFilter, ref('authenticationManager'), ref('customBasicAuthenticationEntryPoint')) {
 authenticationDetailsSource = ref('authenticationDetailsSource')
 rememberMeServices = ref('rememberMeServices')
 credentialsCharset = SpringSecurityUtils.securityConfig.basic.credentialsCharset // 'UTF-8'
}

basicAccessDeniedHandler(AccessDeniedHandlerImpl)

basicRequestCache(NullRequestCache)

basicExceptionTranslationFilter(ExceptionTranslationFilter, ref('customBasicAuthenticationEntryPoint'), ref('basicRequestCache')) {
 accessDeniedHandler = ref('basicAccessDeniedHandler')
 authenticationTrustResolver = ref('authenticationTrustResolver')
 throwableAnalyzer = ref('throwableAnalyzer')
}

Sample codes

Create a restful application with AngularJS and Grails(2): Secure the backend REST API


Secure the backend REST API

In Spring application, Spring Security is usually used to secure the application. Grails has a builtin Spring Security based plugin to integrate Spring Security into Grails applications.

Install SpringSecurity core plugin

Open BuildConfig.groovy file, add spring-security-core plugin.
plugins {
 ...
 compile ":spring-security-core:2.0-RC2"
}
Run the following command in the project root folder to initialize the spring security plugin.
grails compile --non-interactive --refresh-dependencies
And use the built-in s2-quickstart script from this plugin to create the essential domain classes.
grails s2-quickstart Person Authority Requestmap
When it is done, the basic security configuration is added in Config.groovy.
grails.plugin.springsecurity.userLookup.userDomainClassName = 'com.hantsylabs.grails.example.security.Person'
grails.plugin.springsecurity.userLookup.authorityJoinClassName = 'com.hantsylabs.grails.example.security.PersonAuthority'
grails.plugin.springsecurity.authority.className = 'com.hantsylabs.grails.example.security.Authority'
grails.plugin.springsecurity.requestMap.className = 'com.hantsylabs.grails.example.security.Requestmap'
grails.plugin.springsecurity.securityConfigType = 'Annotation'
grails.plugin.springsecurity.controllerAnnotations.staticRules = [
 '/':                              ['permitAll'],
 '/index':                         ['permitAll'],
 '/index.gsp':                     ['permitAll'],
 '/**/js/**':                      ['permitAll'],
 '/**/css/**':                     ['permitAll'],
 '/**/images/**':                  ['permitAll'],
 '/**/favicon.ico':                ['permitAll']
 ]

Configure securityConfigType

There are three securityConfigType supported by this spring security plugin.
  • Annotation
  • InterceptUrlMap
  • Requestmap
By default, the Annotation type is configured.
grails.plugin.springsecurity.controllerAnnotations.staticRules is use for configuring the protection rule for the static resources. It is a map, the key is the url, the value is the configuration attribute which is a list and can accept the Spring security constants or Spring expression, eg. IS_AUTHENTICATED, isFullyAuthenticated(). If you have some experience of Spring security before, it is easy to understatnd.
Besides these, in your Java codes, you can use Grails or Spring Security specific @Securedannotation on methods in a Controller to apply the security restrict rules.
If you select InterceptUrlMap, all resources are protected by url intercepting only.
grails.plugin.springsecurity.securityConfigType = 'InterceptUrlMap'
grails.plugin.springsecurity.interceptUrlMap  = [
 '/':                              ['permitAll'],
 '/index':                         ['permitAll'],
 '/index.gsp':                     ['permitAll'],
 '/**/js/**':                      ['permitAll'],
 '/**/css/**':                     ['permitAll'],
 '/**/images/**':                  ['permitAll'],
 '/**/favicon.ico':                ['permitAll']
 ]
For Requestmap, it is easy to understand, it store the url intercepting mapping rules into database.
grails.plugin.springsecurity.securityConfigType = 'Requestmap'
There is a Requestmap class already generated for this project.
class Requestmap {

 String url
 String configAttribute
 HttpMethod httpMethod

 static mapping = {
  cache true
 }

 static constraints = {
  url blank: false, unique: 'httpMethod'
  configAttribute blank: false
  httpMethod nullable: true
 }
}
In the BootStrap.groovy class, you can add some codes to initialize the Requestmap.
def init = { servletContext ->
 ...
  for (String url in [
   '/', '/index', '/index.gsp', '/**/favicon.ico',
   '/**/js/**', '/**/css/**', '/**/images/**',
   '/login', '/login.*', '/login/*',
   '/logout', '/logout.*', '/logout/*']) {
   new Requestmap(url: url, configAttribute: 'permitAll').save()
   }

}
In this sample, InterceptUrlMap is used as example.
grails.plugin.springsecurity.securityConfigType = 'InterceptUrlMap'
grails.plugin.springsecurity.interceptUrlMap  = [
 '/':                              ['permitAll'],
 '/index':                         ['permitAll'],
 '/index.gsp':                     ['permitAll'],
 '/**/js/**':                      ['permitAll'],
 '/**/css/**':                     ['permitAll'],
 '/**/images/**':                  ['permitAll'],
 '/**/favicon.ico':                ['permitAll'],
 '/login/**':                 ['permitAll'],
 '/logout/**':                 ['permitAll'],
 '/**':      ['isFullyAuthenticated()']
 ]
The security plugin provides a LoginController and LogoutController for login and logout actions.

Run the project

Open BootStrap.groovy file, add some sample user data for test purpose.
def init = { servletContext ->
  
 def person =new Person(username:"test", password:"test123")
 person.save()
  
 def roleUser=new Authority(authority:"ROLE_USER")
 roleUser.save()
  
 new PersonAuthority(person:person, authority:roleUser).save()
}
In Eclipse IDE(Spring ToolSuite), select Run as-> Grails Command(run-app) in the project context menu,
Or in the command line, run the following command in the project root folder to run the this project.
grails run-app
Try to access the protected REST API resources, for example,http://localhost:8080/angluarjs-grails-sample/books.json. It will redirect to a login page. Login as test/test123, it will show the protected resources.

Sample codes