I was frustrated as hell today finding examplesonhowtouseHTTPBuilder to perform a simple POST and GET request in my Grails application.
I now have something that I can use. This code has a dependency on groovyx.net.http libraries. This is available without even thinking by including the Rest Plugin into your app.
grails-app/conf/BuildConfig.groovy
1234
plugins{...runtime":rest:0.7"...
Here’s the code. I placed it in /src/groovy/com/berry/utils/ApiConsumer.groovy
packagecom.berry.utilsimportgroovyx.net.http.HTTPBuilderimportgroovyx.net.http.ContentTypeimportgroovyx.net.http.Methodimportgroovyx.net.http.RESTClientclassApiConsumer{staticdefpostText(StringbaseUrl,Stringpath,query,method=Method.POST){try{defret=nulldefhttp=newHTTPBuilder(baseUrl)// perform a POST request, expecting TEXT responsehttp.request(method,ContentType.TEXT){uri.path=pathuri.query=queryheaders.'User-Agent'='Mozilla/5.0Ubuntu/8.10Firefox/3.0.4'// response handler for a success response coderesponse.success={resp,reader->println"response status: ${resp.statusLine}"println'Headers:-----------'resp.headers.each{h->println" ${h.name} : ${h.value}"}ret=reader.getText()println'Responsedata:-----'printlnretprintln'--------------------'}}returnret}catch(groovyx.net.http.HttpResponseExceptionex){ex.printStackTrace()returnnull}catch(java.net.ConnectExceptionex){ex.printStackTrace()returnnull}}staticdefgetText(StringbaseUrl,Stringpath,query){returnpostText(baseUrl,path,query,Method.GET)}}
With this new class, we can easily perform requests.
123456789
defurl="http://myexample.com"defpath="/path/to/api"defquery=[firstName:"Eric",lastName:"Berry",email:"cavneb@gmail.com"]// Submit a request via GETdefresponse=ApiConsumer.getText(url,path,query)// Submit a request via POSTdefresponse=ApiConsumer.postText(url,path,query)
Shiro is a security framework that is meant to make life easier. The goal of it is to allow the developer to perform authentication, authorization, cryptography and session management. After reading over the 10 minute tutorial, I trust that it is an excellent option to use in my Grails application. It should be also noted that the Grails.org website also uses Shiro for all authentication.
My goal for this article is to help people understand how to implement the Shiro plugin into their Grails application as simply as possible. I will also cover some common usages of the plugin for reference.
A bit more about Shiro
I strongly suggest going through the documentation for Shiro before continuing. If not, there are a couple of tidbits that will help you understand how it works.
First, a term that Shiro uses for a User is Subject. Anytime the Subject is referenced, it is referring to Shiro’s version of a User class.
Second, there is always a Subject that represents the current user. The Subject can be handled prior to authentication. This means that if someone was to visit your site without any authentication, there is already an instanciated class that represents them. In Java, you can obtain the Subject with the following:
1
SubjectcurrentUser=SecurityUtils.getSubject();
To authenticate the user, you must create a UserPasswordToken and call login on the instanciated Subject. The success or failure of the login are returned via exceptions. Here is an example of code that may be used when logging in a user.
12345678910111213141516171819202122
if(!currentUser.isAuthenticated()){UsernamePasswordTokentoken=newUsernamePasswordToken("foo","bar");token.setRememberMe(true);// Use remember me if desired - all built in!// Attempt to authenticate. If no exception thrown, the login was successfultry{currentUser.login(token);// Username wasn't in the system}catch(UnknownAccountExceptionuae){// Password didn't match}catch(IncorrectCredentialsExceptionice){// Account with username is locked}catch(LockedAccountExceptionlae){// Unexpected error }catch(AuthenticationExceptionae){}}
Cool! Now we have a bit of knowledge to help us know what we are doing in our Grails implementation.
Installation
Let’s start by creating a new Grails application. I am using version 2.0.3. In the code samples to follow, anything that starts with a $ means that it is a shell command.
$ grails create-app shiro-example
$ cd shiro-example
The Shiro Plugin can be installed by using the dependency in your BuildConfig.groovy. Even though the plugin page shows version 1.1.4, we will use the latest snapshot release:
grails-app/conf/BuildConfig.groovy
1234
plugins{...runtime":shiro:1.2.0-SNAPSHOT"...
Once you have the dependency setup in the BuildConfig.groovy file, you must compile the app to make sure the dependencies are installed.
$ grails compile
Let’s kickstart our app now by using the shiro-quick-start command. I am using the optional --prefix=[package]. Make sure you have a dot at the end if you are using the prefix.
$ grails shiro-quick-start --prefix=com.example.
| Created file grails-app/domain/com/example/User.groovy
| Created file grails-app/domain/com/example/Role.groovy
| Created file grails-app/realms/com/example/DbRealm.groovy
| Created file grails-app/controllers/com/example/AuthController.groovy
| Created file grails-app/views/auth/login.gsp
| Created file grails-app/conf/com/example/SecurityFilters.groovy
Whoa! What just happened? As you can see, you now have created some files:
domain/../User.groovy is the domain class for your user.
domain/../Role.groovy is the domain class for your user roles.
realms/../DbRealm.groovy is the class Shiro uses to handle authentication and permissions.
controllers/../AuthController.groovy and views/auth/login.gsp are the user-facing controller and view meant to handle login.
config/../SecurityFilters.groovy adds a beforeInterceptor to all url’s. This blocks/redirects users from accessing pages they are not authorized to visit (based on their roles).
What is a realm?
Shiro can use different realms for authenticating a user. A realm is basically a resource Shiro should use to authenticate a user. Further explanation can be found here.
Configuration
Bootstrapping
Bootstrapping users and roles is similar to how it’s done with the Spring Security Core plugin. Let’s create a couple of roles and users to go with them.
importcom.example.Roleimportcom.example.UserclassBootStrap{defshiroSecurityServicedefinit={servletContext->// Create the admin roledefadminRole=Role.findByName('ROLE_ADMIN')?:newRole(name:'ROLE_ADMIN').save(flush:true,failOnError:true)// Create the user roledefuserRole=Role.findByName('ROLE_USER')?:newRole(name:'ROLE_USER').save(flush:true,failOnError:true)// Create an admin userdefadminUser=User.findByUsername('admin')?:newUser(username:"admin",passwordHash:shiroSecurityService.encodePassword('password')).save(flush:true,failOnError:true)// Add roles to the admin userassertadminUser.addToRoles(adminRole).addToRoles(userRole).save(flush:true,failOnError:true)// Create an standard userdefstandardUser=User.findByUsername('joe')?:newUser(username:"joe",passwordHash:shiroSecurityService.encodePassword('password')).save(flush:true,failOnError:true)// Add role to the standard userassertstandardUser.addToRoles(userRole).save(flush:true,failOnError:true)}defdestroy={}}
On line 5, we include the Shiro Security Service. This is necessary for us to encode the password into a password hash for the user.
On lines 9-15, the admin and user roles are created.
On lines 17-21, the admin user is either loaded from the database or is created. Here is where we use the Shiro Security Service for encoding the password.
On lines 23-26, the roles ADMIN_ROLE and USER_ROLE are assigned to the admin user. Note that we add both roles to this user. This allows the admin user to act as a normal user as well. This may or may not be appropriate, depending on the application.
Lines 28-36 are similar to 23-26, but for the standard user role instead of the admin role.
Kick the Tires
Now that we have the default users created, let’s run the app and test to make sure the login works. Start up the application with the run-app command:
Click on the link com.example.AuthController and login as the admin user (username: ‘admin’, password: ‘password’).
If you successfully logged in, you will be redirected back to the first page. This doesn’t help us very much.
Adding Security
Now that we’ve figured out how to authenticate users with the login form, we need to create some pages that require being authenticated. Pay attention.. this is really the core of the plugin.. and the part that was most difficult for me to figure out.
Let’s start off by creating a controller with three actions: index, secured and admin.
packagecom.exampleclassHomeController{defindex(){render"This page is not secured"}defsecured(){render"This page requires a user to be logged in"}defadmin(){render"This page requires the logged in user to be an administrator"}}
As you can see, the three actions should have different security levels. index will allow anyone to be able to view the page. secured requires that a user be logged in. admin requries that the user logged in must have the ROLE_ADMIN role assigned to them.
Try out the URL’s to the new actions created. Notice that they all lead you back to the login page if you haven’t logged in. Stranger yet is that once you log in, you should see this;
Don’t worry though. This is expected and can be resolved easily by updating SecurityFilters.groovy.
packagecom.example/** * Generated by the Shiro plugin. This filters class protects all URLs * via access control by convention. */classSecurityFilters{/** * Array of controller/action combinations which will be skipped from authentication * if the controller and action names match. The action value can also be '*' if it * encompasses all actions within the controller. */staticnonAuthenticatedActions=[[controller:'home',action:'index']]/** * Array of controller/action combinations that will be authenticated against the user's * role. The map also includes the roles which the controller/action pair will match * against. */staticauthenticatedActions=[[controller:'home',action:'secured',roles:['ROLE_ADMIN','ROLE_USER']],[controller:'home',action:'admin',roles:['ROLE_ADMIN']]]deffilters={all(controller:'*',action:'*'){before={// Determine if the controller/action belongs is not to be authenticateddefneedsAuth=!nonAuthenticatedActions.find{(it.controller==controllerName)&&((it.action=='*')||(it.action==actionName))}if(needsAuth){// Get the map within the authenticated actions which pertain to the current// controller and view.defauthRoles=authenticatedActions.find{(it.controller==controllerName)&&((it.action=='*')||(it.action==actionName))}if(authRoles){// Perform the access control for each of the roles provided in the authRolesaccessControl{authRoles.roles.each{roleName->role(roleName)}}}// Skip authentication if the authRoles was not foundelse{returntrue}}// Skip authentication if no auth is neededelse{returntrue}}}}}
This may be a lot to take in but the only sections that are really important are lines 14-16 and 23-26. Here we are establishing which controller/action combinations are not to be authenticated, and which are to be authenticated against the provided roles.
Now when you access the pages, the expected security should be in place. Give it a shot.
Confused?
This can be a confusing topic, and moreso a confusing portion of the topic. Please feel free to ask any questions in the comments section and I will do my best to help out.*
Creating Users
The shiro-quick-start command did create the login page and authentication controller, but it didn’t help us a bit on creating users. Good thing it’s pretty easy to do.
We could create a controller called Users to handle the signup process, however, I don’t want to do that. The reason is because I would like to eventually have a Users controller that will perform all the CRUD operations for the users and would only be accessible by administrators.
Instead, let’s create a new controller for handling the signup process. Let’s call it Signup.
$ grails create-controller com.example.Signup
| Created file grails-app/controllers/com/example/SignupController.groovy
| Created file grails-app/views/signup
| Created file test/unit/com/example/SignupControllerTests.groovy
packagecom.exampleimportorg.apache.shiro.authc.UsernamePasswordTokenimportorg.apache.shiro.SecurityUtilsclassSignupController{defshiroSecurityServicedefindex(){Useruser=newUser()[user:user]}defregister(){// Check to see if the username already existsdefuser=User.findByUsername(params.username)if(user){flash.message="User already exists with the username '${params.username}'"render('index')}// User doesn't exist with username. Let's create oneelse{// Make sure the passwords matchif(params.password!=params.password2){flash.message="Passwords do not match"render('index')}// Passwords match. Let's attempt to save the userelse{// Create useruser=newUser(username:params.username,passwordHash:shiroSecurityService.encodePassword(params.password))if(user.save()){// Add USER role to new useruser.addToRoles(Role.findByName('ROLE_USER'))// Login userdefauthToken=newUsernamePasswordToken(user.username,params.password)SecurityUtils.subject.login(authToken)redirect(controller:'home',action:'secured')}else{}}}}}
In the code above, there are two actions: index and register. The index action is what contains the form (view). The register action performs the user registration and responds/redirects according to the results.
Paste this into your /grails-app/views/home/index.gsp view:
If the signup is successful, you will see a page like this:
Congrats! You now have a full circle authenticated app using Shiro!
Tag Lib Reference
1
<shiro:isLoggedIn>Body</shiro:isLoggedIn>
This tag only writes its body to the output if the current user is logged in.
1
<shiro:isNotLoggedIn>Body</shiro:isNotLoggedIn>
This tag only writes its body to the output if the current user is NOT logged in.
1
<shiro:authenticated/>
A synonym for ‘isLoggedIn’. This is the same name as used by the standard Shiro tag library.
1
<shiro:notAuthenticated/>
A synonym for ‘isNotLoggedIn’. This is the same name as used by the standard Shiro tag library.
1
<shiro:user>Body</shiro:user>
This tag only writes its body to the output if the current user is either logged in or remembered from a previous session (via the “remember me” cookie).
1
<shiro:notUser>Body</shiro:notUser>
This tag only writes its body to the output if the current user is neither logged in nor remembered from a previous session (via the “remember me” cookie).
1
<shiro:remembered>Body</shiro:remembered>
This tag only writes its body to the output if the current user is remembered from a previous session (via the “remember me” cookie) but not currently logged in.
1
<shiro:notRemembered>Body</shiro:notRemembered>
This tag only writes its body to the output if the current user is not remembered from a previous session (via the “remember me” cookie). This is the case if they are a guest user or logged in.
1
<shiro:principaltype="type"property="property"/>
Outputs the string form of the current user’s identity. By default this assumes that the subject has only one principal; its string representation is written to the page.
The type is the species the type or class of the principal to use.
The property value specifies the name of the property on the principal to use as the string representation, e.g. firstName.
This tag only writes its body to the output if the current user doesn’t have all of the given roles (inversion of hasAllRoles).
The in property is a List of roles, e.g. ['ROLE_ADMIN', 'ROLE_USER'].
This tag only writes its body to the output if the current user has the given permission. Attribute descriptions are the same as found on hasPermissions above.
This tag only writes its body to the output if the current user does not have the given permission. Attribute descriptions are the same as found on hasPermissions above.
Notes
I did not cover topics such as permissions. For more information on this, please refer to the Shiro documentation and the Shiro Plugin source code.
The Rails asset pipeline is very powerful, but often misunderstood. At the Utah Ruby User Group, most of the attendees aren’t sure how to use it fully in their Rails app. It’s considered as one of the magic features that Rails offers. I admit that I was confused as well and took it’s magic for granted. Not any longer.
I reference the word asset a lot in this article. An asset is a file that is to be included in your Rails application (JavaScript, CSS, Image, etc).
In this article, I want to simplify the asset pipeline so it is better understood.
Purpose
The asset pipeline has three goals: precompile, concatenate and minify assets into one central path. Or in other words, it takes all of your stylesheets, javascript files, images and any other files you want, joins them together when possible, and places them in the public/assets folder.
Moving Parts
The asset pipeline is powered by two technologies: Sprockets and Tilt, the latter being a dependency of the former (look at your Gemfile.lock if you don’t believe me).
Sprockets performs the asset packaging which takes the assets from all the specified paths, compiles them together and places them in the target path (public/assets).
Tilt is the template engine that Sprockets uses. This allows file types like scss and erb to be used in the asset pipeline. See the Tilt Readme for a list of supported template engines.
Asset Paths
Rails applications default to having three possible asset paths.
app/assets is for assets that are owned by the application, such as custom images, JavaScript files or stylesheets.
lib/assets is for your own libraries’ code that doesn’t really fit into the scope of the application or those libraries which are shared across applications.
vendor/assets is for assets that are owned by outside entities, such as code for JavaScript plugins and CSS frameworks.
The Manifest
The keystone of the asset pipeline is the manifest file. By default, Rails creates one for stylesheets (app/assets/stylesheets/application.css) and JavaScript files (app/assets/javascripts/application.js). This file uses directives to declare dependencies in asset source files.
For directives that take a path argument, you may specify either a logical path or a relative path. Relative paths begin with ./ and reference files relative to the location of the current file.
Here are some directives that can be used:
require[path] inserts the contents of the asset source file specified by path. If the file is required multiple times, it will appear in the bundle only once.
include[path] works like require, but inserts the contents of the specified source file even if it has already been included or required.
require_directory[path] requires all source files of the same format in the directory specified by path. Files are required in alphabetical order.
require_tree[path] works like require_directory, but operates recursively to require all files in all subdirectories of the directory specified by path.
require_self tells Sprockets to insert the body of the current source file before any subsequent require or include directives.
depend_on[path] declares a dependency on the given path without including it in the bundle. This is useful when you need to expire an asset’s cache in response to a change in another file.
stub[path] allows dependency to be excluded from the asset bundle. The path must be a valid asset and may or may not already be part of the bundle. Once stubbed, it is blacklisted and can’t be brought back by any other require.
Documentation for this section was largely extracted from the Sprockets README.
Usage
Using the asset pipeline is very simple. All it involves is placing assets (js/css/images/other) into the asset path. You can access the files using multiple helper methods within your views:
Files must belong in their respective paths. For example, all JavaScript files must be in a javascripts folder within an asset path.
The truth is that the paths (stylesheets, javascripts, images) are only there for organization. You can have all the assets in a single folder or in a hundred.
Sass files need to use erb extension to allow for asset path inclusions within the files.
The truth is that sass-rails provides -url and -path helpers for the following asset types: image, font, video, audio, JavaScript and stylesheet.
A good way to include assets easily in a Rails application is by using gems. To include assets within a gem to be precompiled with the Rails application that is using it, all you need is to place the assets in one of the three standard asset paths: app/assets, lib/assets and vendor/assets. These will be automatically included in by Sprockets when the assets are compiled. See the Rails documentation for more information.
FAQ
Q: Why doesn’t the auto-generated scss and coffeescript only get included in their respective controller views?
Because the assets all concatenate into one file, there are no seperate files to be included on a view-by-view basis. There is a way to get around this by using css classes.
Let’s say we have a controller named Users with an accompanying sass file called users.css.scss. Make sure your css is wrapped in a class which includes the name of the controller:
123
body-users{//Customcssgoeshere}
Next, add a class to the body tag of your layout:
1
<bodyclass="body-#{controller_name}">
Now the css in users.css.scss will only be applied to views under the Users controller.
Q: Do I have to use the asset pipeline?
No. In Rails 3.1, the asset pipeline is enabled by default. It can be disabled in config/application.rb by putting this line inside the application class definition:
1
config.assets.enabled=false
Q: What happens if there are duplicate file names in different asset folders?
Let’s say you have two asset files with the same name in different paths. For example, let’s say we have two files: app/assets/stylesheets/style.css.scss and vendor/assets/stylesheets/style.css.scss.
When the assets are compiled, it disregards all the duplicate files after the first one found in the asset path. Let’s look at the asset path using the rails console:
Note that the path /Users/eberry/example/app/assets/stylesheets appears before the path /Users/eberry/example/vendor/assets/stylesheets.
Q: How can I precompile assets that aren’t to be used in the pipeline?
Let’s say you want to include the folder other/assets into the asset pipeline to be precompiled. This is a simple addition in the application.rb file (or environment specific config file).
1234567
moduleFooclassApplication<Rails::Application...# Add additional path to the assets path for pipeline compilationconfig.assets.paths<<"#{Rails.root}/other/assets"endend
Now when you run the command Rails.application.config.assets.paths in the Rails console, you will see the new asset path.
Q: How can I have certain JavaScript files appear at the bottom of the HTML page?
Multiple manifests can be created in the assets folder. For example, I can have a separate manifest called footer.js which includes the files footer_1.js and footer_2.js.
12
//= require footer_1//= require footer_2
I can add this into the HTML by using the same javascript_include_tag that is used in the HTML header of the layout.
Q: How can I precompile additional assets without having to include them in the manifest?
Let’s say we have a file called search.js in our app/assets/javascripts directory and we don’t include it in the manifest. We still would like it to be compiled and placed into the public/assets when the assets are compiled.
This is very simple. Just add the following to your application.rb file (or environment specific config file):
123
# Precompile additional assets (application.js, # application.css, and all non-JS/CSS are already added)config.assets.precompile+=%w( search.js )
This configuration option appears by default in config/environments/production.rb.
Summary
As I said before, the asset pipeline has three goals: precompile, concatenate and minify* assets.
Precompilation let’s you use higher-level languages to create actual assets (for example, Sass to CSS).
Concatenation is very important in the production environment. It can reduce the number of requests that a browser makes to render a web page, which leads to faster load time.
Minification takes out the extra whitespace and comments from your assets. This allows for smaller asset file size, which leads to faster load times.
Feel free to hop on the #urug channel on Freenode to chat with me anytime. Also, for a different perspective on asset handling, see the Resources plugin for Grails.
Peter Ledbrook ( @pledbrook ) gave me some tips on how to handle this better using Enums. Enum values should always be in uppercase letters because they are constants in the fixed set. If we were to refactor the code above, it would something more like this: