跳至主要内容

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!

评论

此博客中的热门博文

Build a Reactive application with Angular 5 and Spring Boot 2.0

I have created a post to describe Reactive programming supports in Spring 5 and its subprojects, all codes of this article are updated the latest Spring 5 RELEASE, check spring-reactive-sample under my Github account.
In this post, I will create a simple blog system, including:
A user can sign in and sign out.An authenticated user can create a post.An authenticated user can update a post.Only the user who has ADMIN role can delete a post.All users(including anonymous users) can view post list and post details.An authenticated user can add his comments to a certain post. The backend will be built with the latest Spring 5 reactive stack, including:
Spring Boot 2.0, at the moment the latest version is 2.0.0.M7Spring Data MongoDB supports reactive operations for MongoDBSpring Session adds reactive support for WebSessionSpring Security 5 aligns with Spring 5 reactive stack The frontend is an Angular based SPA and it will be generated by Angular CLI.
The source code is hosted on Github, …

Activating CDI in JSF 2.3

Activating CDI in JSF 2.3 When I upgraed my Java EE 7 sample to the newest Java EE 8, the first thing confused me is the CDI beans are not recoganized in Facelects template in a JSF 2.3 based web applicaiton, which is working in the development version, but in the final release version, they are always resolved as null. I filed an issue on Mojarra and discussed it with the developers from communities and the JSF experts.
According to the content of README, In a JSF 2.3 application, to activate CDI support, declaring a 2.3 versioned faces-config.xml and adding javax.faces.ENABLE_CDI_RESOLVER_CHAIN in web.xml is not enough, you have to declare @FacesConfig annotated class to enable CDI.
Here is the steps I created a workable JSF 2.3 applicatoin in Java EE 8.
Create a Java web application, this can be done easily by NetBeans IDE, or generated by Maven archetype, for exmaple.
$ mvn archetype:generate -DgroupId=com.example -DartifactId=demo -DarchetypeArtifactId=maven-archetype-w…

JPA 2.1: Attribute Converter

JPA 2.1: Attribute Converter If you are using Hibernate, and want a customized type is supported in your Entity class, you could have to write a custom Hibernate Type. JPA 2.1 brings a new feature named attribute converter, which can help you convert your custom class type to JPA supported type. Create an Entity Reuse thePostentity class as example. @Entity @Table(name="POSTS") public class Post implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name="ID") private Long id; @Column(name="TITLE") private String title; @Column(name="BODY") private String body; @Temporal(javax.persistence.TemporalType.DATE) @Column(name="CREATED") private Date created; @Column(name="TAGS") private List<String> tags=new ArrayList<>(); } Create an attribute converter In this example…