The purpose of this assignment is to introduce you to authentication in websites. In particular, you will learn about:
- Spring Security Core plugin for Grails
- Spring Security UI plugin for Grails
- How to control access to views
- Bonus: Learn how to make AJAX calls
Step 1: Install Spring Security Plugins
We will use the Spring Security Core plugin with an additional plugin, the Spring Security UI plugin. The Spring Security Core authenticates users and control access to pages. The Spring Security UI provides convenient UI for controlling access and users. It also uses the mail plugin during the registration and reset forgotten passwords. The plugin pages are at:
- https://plugins.grails.org/plugin/grails/spring-security-core
- https://plugins.grails.org/plugin/grails/spring-security-ui
- https://plugins.grails.org/plugin/grails/mail
The documentation for the plugins are at
- https://grails.github.io/grails-spring-security-core/5.0.0-RC1/index.html
- https://grails.github.io/grails-spring-security-ui/4.0.x/index.html
- https://grails3-plugins.github.io/mail/
To install plugins, all you need to do is to list them in build.gradle file ( found at the root of the project files). Near the bottom of the build.gradle file, you should see a long list of dependencies. Cut and paste into this dependencies list the declarations below:
dependencies { ... // Add for Spring Security implementation "org.grails.plugins:spring-security-core:5.1.0" implementation 'org.grails.plugins:spring-security-ui:4.0.0-RC1' // implementation 'org.grails.plugins:mail:2.0.0' // Does not work using Java 11. Also don't have to use. }
If you stop and start your app now, you will not get what you expect. The browser will be redirected to the login screen (with bad/broken styling). We need to setup Spring Security Core.
Step 2: Setup Spring Security Core.
1. Run S2 Quick Start script.
Open the Grails command prompt by clicking the “Tools” in the menu bar then select “Grails” followed by “Grails Command”. In the widow that pops up enter:
s2-quickstart cs4760progassign User Role
Alternative you can run the Grails command at the project root directory using a Bash terminal
./grailsw s2-quickstart cs4760progassign User Role
The script creates 3 domain classes, User.groovy, Role.groovy and UserRole.groovy. It also creates a application.groovy file in grails-app/conf/ directory. You may need to reload the project files into IntelliJ IDEA by clicking the “refresh” arrows in the toolbar. Read the tutorial in the Spring Security documentation to learn more:
https://grails.github.io/grails-spring-security-core/5.0.0-RC1/index.html#tutorials
If you read the first part of the tutorial, you notice that we should add
grails.plugin.springsecurity.logout.postOnly = false
to the bottom of the conf/application.groovy file. This makes logout easy. During debugging your web app, you can logout by clicking the logout controller in the control list page.
2. Make Role and Users in Bootstrap.groovy
We can add some users and roles to the Bootstrap.groovy file. Cut and paste the code below into init/Bootstrap.groovy init closure.
package cs4760progassign import grails.gorm.transactions.Transactional class BootStrap { @Transactional void addUsers() { // Add for creating Roles and Users def adminRole= new Role(authority: 'ROLE_ADMIN').save(flush: true) def userRole = new Role(authority: 'ROLE_USER').save(flush: true) def testAdmin = new User(username: 'admin', password: 'password') testAdmin.save(flush: true) def testUser = new User(username: 'user', password: 'password') testUser.save(flush: true) UserRole.create testAdmin, adminRole, true UserRole.create testUser, userRole, true UserRole.withSession { it.flush() it.clear() } } def init = { servletContext -> addUsers() ... } def destroy = { } }
If you do not have the package specification, you may have to import the User, Role and UserRole, click on the offending class name, then hit alt-enter, finally select import. Also be sure to import grails.gorm.transactions.Transactional
and to annotate the addUsers
method with @Transactonal
. Do not forget to call addUsers
in the init
method.
The code makes two roles and two users, one a regular user and the other an admin user.
Rerun your web app. If you navigate away from the home page, you’ll be prompted with a log in screen. You can log in and see the home page, but you cannot navigate to any other pages because neither Admin or User roles are authorize to see any pages yet. Spring Security set pessimistic locking by default:
https://grails.github.io/grails-spring-security-core/5.0.0-RC1/index.html#pessimistic-lockdown
Step 3: Setup Access to Views
There are basically two methods for setting up access, either using the Request Mapping or annotating the controller or action. Read the Spring Security documentation about Request Mappings
https://grails.github.io/grails-spring-security-core/5.0.0-RC1/index.html#requestMappings
1. Add Static Rules
We use static mapping for the public views.
https://grails.github.io/grails-spring-security-core/5.0.0-RC1/index.html#configGroovyMap
Add these static rules to the grails.plugin.springsecurity.controllerAnnotations.staticRules map at the bottom of the conf/application.groovy file.
grails.plugin.springsecurity.controllerAnnotations.staticRules = [ ... [pattern: '/books/**', access: ['permitAll']], [pattern: '/authors/**', access: ['permitAll']], [pattern: '/controllerList/**', access: ['ROLE_ADMIN']], [pattern: '/author/**', access: ['ROLE_ADMIN']], ]
Do not forget to add a comma to the end of the prior list. But I have noticed that groovy parser does not mind if the last item in a list has a comma, so I leave it with a comma at the end, making it easy to add more rules.
The first two rules grants public to access to all of the books and authors views. The last two rule restrict access to the controllerList and author views to admin users.
Rerun the grails app. Working through the programming assignment this year, I noticed a new error after adding new static rules to the application.groovy.
FAILURE: Build failed with an exception.
What went wrong:
Execution failed for task ':findMainClass'.
Unable to find a single main class from the following candidates [application, cs4760progassign.Application]
To fix the this error you need to rebuild the project from scratch. To this open the Gradle panel, by clicking the “Gradle” tap on the right border of intelliJ IDEA. Gradle is the build tool used by Grails. In the Gradle panel you will see your project “cs4760progassign”. Click the down arrow and you will see “Tasks” and “Run Configurations”. Gradle is like “Make”; it is organized by dependent tasks. Gradle grails-run command will run the application/bootRun without building the entire project, probably to save time. Apparently, the project needs a complete rebuild. To do this click the down arrow adjacent to “build”. Select “clean” and click. This will delete the build/ directory. It also adds “c4760progassign [clean]” command to the command list in the run text field. Now the app will rebuild from scratch. Select the “Grails: cs4760progassign” from the command list and run the app by clicking the green right arrow. The app should now build and run.
Open the app in your browser. Now you can view the home and book index views. To view the controller list you’ll need to login as admin. You still can not access the book views.
If you try access a page that you do not have authorization to view Spring Security will confront you with a login page. After logging in, Spring Security should redirect you to the page that you tried to access. This does not happen all the time (I’m not sure why) and the browser remains on the login page. If this happens, you can navigate to page using the browser’s back arrow.
2. Add Annotation to BookController
Read about annotating controller actions at
https://grails.github.io/grails-spring-security-core/5.0.0-RC1/index.html#securedAnnotations
Now, you try annotating the Book controller methods. Add the annotation just above the class definition.
@Secured(['ROLE_ADMIN']) class BookController { ... }
You also need to add the import to the top of the class.
import grails.plugin.springsecurity.annotation.Secured
Stop and start your web app. Notice that you can view the the home page, Books and Authors index actions without logging in, but you cannot access the Author or Book actions without encountering the login screen.
Generally, you do not want to mix the two techniques. I prefer to use the static rules instead of the annotation for two reasons. First, you can see all the rules at once in application.groovy. Second, you can control access to code you did not write, for example a plugin, without modify the plugin code.
Step 4: Spring Security UI
The Spring Security UI plugin provides admin user and role controllers and views, so that the admin can perform common tasks such as adding users and roles. Also Spring Security UI provides views for displaying security information.
1. Accessing User-Role Backend
At the start page you’ll notice that Spring Security UI plugin has added many controllers. At the bottom of the list is the grails.plugin.springsecurity.ui.UserControler. If you click on the link you will be confronted with the login screen. You can login as admin, but that will not give you access to the view.
In the conf/applictaion.groovy, add to the bottom of the staticRules the entries:
[pattern: '/user/**', access: ['ROLE_ADMIN']], [pattern: '/role/**', access: ['ROLE_ADMIN']], [pattern: '/registrationCode/**', access: ['ROLE_ADMIN']], [pattern: '/securityInfo/**', access: ['ROLE_ADMIN']], [pattern: '/logout/**', access: ['permitAll']],
We also add the finally rule to let anyone logout.
Restart the grails app. Login as admin, you should see the User Management Search page.
2. Explore User-Role Backend
Read about the Spring Security UI controllers and views in the documentation:
http://grails-plugins.github.io/grails-spring-security-ui/latest/index.html
Spring Security UI is a good tool for learning about Spring Security, so explore all the features of the backend. You can access them from the menu bar at the top of the page.
Spring Security UI has many configurations and techniques to customize. You should reread the documentation, especially the “Customization” section if you use it for your team project application.
Step 5: Add login/logout to Navbar
In this step you will add login and out links to the navbar. You can also have the navbar identifiy the person logged in.
Read the chapter on Helper Classes in the Spring Security Core plugin documentation. In particular read about the Security Tag Library:
https://grails.github.io/grails-spring-security-core/5.0.0-RC1/index.html#securityTagLib
You will use the <sec:ifLoggedIn>, <sec:ifNotLoggedIn> and <sec:username> tags in the navbar.
Add the following code to _navbar.gsp below the other links for the menu:
<sec:ifLoggedIn><li class="nav-item"><a class="nav-link" href="#"><sec:username/></a></li></sec:ifLoggedIn> <li class="nav-item"> <sec:ifLoggedIn><g:link class="nav-link" controller="Logout">log out</g:link></sec:ifLoggedIn> <sec:ifNotLoggedIn><g:link class="nav-link" controller="login" action="auth">Login</g:link></sec:ifNotLoggedIn> </li>
Restart the app, and try out the new menu items.
Notice that after logging in the user name appears in the navbar. The link could be used to send the user to their profile page.
The <sec:ifLoggedIn>
, <sec:ifNotLoggedIn>
and < sec:username/>
tags are accessing session parameters. I’ll speak more about session parameters in a lecture.
Step 6: Study AJAX
AJAX is a technique to create dynamic effects in a view without loading a new page. The basic idea is that a request is sent to the server but the response is returned to the same page instead of loading a new page. In this way, only a portion of the page needs to load rather than the complete page.
1. Study JavaScript
AJAX uses JavaScript, so you’ll want to study JavaScript. A good resource is W3 Schools.
http://www.w3schools.com/js/default.asp
An more complete resource is at the Mozilla Developer Network.
https://developer.mozilla.org/en-US/docs/Web/JavaScript
Most of the language should be familiar to you. But there are some aspects that you should study.
For the most part, you will be writing functions for event handlers. You should read about javascript functions:
- https://www.w3schools.com/js/js_functions.asp
- https://www.w3schools.com/js/js_events.asp
- https://www.w3schools.com/js/js_arrow_function.asp
A major purpose of javascript is to manipulate the web page. Javascript’s models the web page as a Document Object Model (DOM). Read the chapters about the DOM.
- http://www.w3schools.com/js/js_htmldom.asp
- http://www.w3schools.com/js/js_htmldom_methods.asp
- http://www.w3schools.com/js/js_htmldom_document.asp
- http://www.w3schools.com/js/js_htmldom_elements.asp
- http://www.w3schools.com/js/js_htmldom_html.asp
- http://www.w3schools.com/js/js_htmldom_css.asp
- http://www.w3schools.com/js/js_htmldom_events.asp
- http://www.w3schools.com/js/js_htmldom_eventlistener.asp
These tutorials are very short, so they will not take long to read. Be sure to try the examples so that you understand each concept.
2. Study JQuery
JQuery is a JavaScript library that simplifies using JavaScript. We will be using JQuery, so you will want to study it. A good resource for JQuery is W3 Schools.
http://www.w3schools.com/jquery/default.asp
Be sure to study the introductory chapters about jQuery:
- http://www.w3schools.com/jquery/default.asp
- http://www.w3schools.com/jquery/jquery_intro.asp
- http://www.w3schools.com/jquery/jquery_syntax.asp
- http://www.w3schools.com/jquery/jquery_selectors.asp
- http://www.w3schools.com/jquery/jquery_events.asp
Also be sure to study the use of AJAX in jQuery:
- http://www.w3schools.com/jquery/jquery_ajax_intro.asp
- http://www.w3schools.com/jquery/jquery_ajax_load.asp
- http://www.w3schools.com/jquery/jquery_ajax_get_post.asp
More resources are available at the jQuery Learning Center.
After reading the above, you should be prepared to understand the code that we’ll add to our web app.
Step 7: Make a Simple AJAX Call
Now, we’ll make a very simple ajax code in the home view. We’ll add a link to the home view to show the current date and time.
1. Edit index.gsp
In the views/index.gsp file, add the link and result div to the home/index.gsp by adding the code below in the body of the view just below the jumbotron div.
<!-- Simple Ajax link to show time --> <g:link controller="home" action="showTime" elementId="timeLink">Show the time!</g:link> <div id="time"> time </div>
You should notice that the “Show the time!” link will call the showTime action in the Home controller, so we should make that controller and action.
2. Add the Server Code
We need place to run the server code. Typically, we would use the controller for the view, but currently the home page does not have a controller associated with it. Note that the g:link already specifies a Home controller. Make a new controller call it Home. After Grails has generated the controller, add a showTime method to the home controller by cutting and pasting the code below.
private static final boolean debugTime = true //flag for debug printing def showTime() { if(debugTime)println "In showTime" render "The time is ${new Date()}" }
You also need to grant the public access to home controller. In application.groovy add the following static rules:
[pattern: '/home/**', access: ['permitAll']],
You’ll have to clean the project before rerunning Grails. Rerun the Grail and try the link in the browser. It should take you to a new page showing the time.
But, that is not what we want. We would like for the date and time to show on the same home page. We need to add some JavaScript.
4. Add JavaScript
In grails-app/assets/javascrips/ directory, make showTime.js file. Then cut and paste the code below into the file.
console.log("Hello showtime"); $(document).ready( function(){ $('#timeLink').click(function(){ console.log("showtime click function"); $('#time').load(this.href); return false; }); });
Note that you need the “return false” to prevent the normal behavior the link, navigating to a new page.
You might wonder how the showTime.js file is added to the page
In the /views/layout/site.gsp, the other asset tag at the bottom of the page tells asset-pipline to source the application.js.
<asset:javascript src="application.js"/>
Inspect the grails-app/asset/javascript/application.js file, add above “//= require self” the directive:
//= require_tree . //= require_self
which will include all the JavaScript files in the directory.
The asset pipeline does much more, especially in production environment. The link below has a very good video introducing the asset-pipeline plugin.
https://www.youtube.com/watch?v=VHDBlGtAHB4
It is worth watching because you will need to control your assets.
6. Test the Code
Run the app. You might get an error building the project, similar to
Execution failed for task ‘:bootRunMainClassName’.
Execution failed for task ':bootRunMainClassName'. > Unable to find a single main class from the following candidates [application, cs4760progassign.Application]
I want you to learn about Chrome and FireFox developer tools. They both have developer tools for debugging web pages and JavaScript. In Chrome and Firefox, right click on the page and select inspect. In the panel that opens, select console tab.
You should see the message “Hello showtime.” If not refresh the page and click the “show time” link again. The message is form the console.log method.
There are a lot more features in both of these developer tools that you will use to debug your apps. The “Inspector” or “Inspect Element” tools are good for debugging layout and css. Both Chrome and FireFox allow editing the css in the tool so that you can see the effects in real time.
The “Network” tool will show you request timing. Clicking on a request in the “Network” tool will show you the details of the request.
Step 8: Change AJAX call to trigger on Document Ready Event
Sometimes we want the results of a AJAX to load just after the page has loaded in the browser. The event that the document has loaded is the ready event on the document. Change the javascript in the showtime.js file to:
// Show time - Simple Ajax console.log("Hello showtime"); $(document).ready( function(){ console.log("In document ready"); $('#time').load("/cs4760progassign/home/showTime"); $('#timeLink').click(function(){ $('#time').load(this.href); return false; }); });
Refresh the page and you’ll see the time div is load with the date and time. You can navigate away from the home page and navigate back, and see that the date and time loads has the page load. You can also use the “Show the time!” link to update the time.
Step 9: Make AJAX Recent Book
On the Book Store home page we want the most recently published book to appear below the time. The home page should look something like
Book Store Show the time! The time is ….. Most Recent Book The Stand by Stephen King
You are on your own coding this AJAX call, but I can give you some hints. Use the “first” or “last” method on a list that you get from the Book domain.
Note that the AJAX calls in this assignment are only for example. You would generally not use AJAX calls like these to generate simple dynamic content. Rather you would have the controller send the view a model as we did in the prior assignment.
You should use an AJAX call when the user updates data in the view. Rather than redrawing the entire page, you would have the AJAX call update the effected portion of the view.
There another case when to use AJAX calls for dynamic content in the view. That is when the view should work both online and offline (meaning with or without an internet connection). In that case, the view must identify if there is an internet connect or not. If there is not an internet connection then generally the view should get the dynamic content from local storage, and if there is an internet connection then the view should get the dynamic content from an AJAX call similar to the ones in this assignment.
Step 10: Make Screen Shots and Submit
After creating the AJAX call for the most recent book, make two screen shows, one of your index.gsp and the other of the javascript file.
Submit the screen shots in canvas for the “Assignment Programming Assignment 3 – Authenticating Administrative Pages”
Congratulations, you have secured your website and made an AJAX call.