JAX-RS Security using JSON Web Encryption(JWE) with AngularJS, Bootstrap, Grid-UI and MongoDB Example
JAX-RS Security using JSON Web Encryption(JWE) with AngularJS, Bootstrap, Grid-UI and MongoDB Example
In this tutorial we will discuss how to use AngularJS, Bootstrap and Grid-UI to connect to secure JAX-RS RESTful web services using JWE/JWT/JWS for Authentication and Authorization. In our example implementation, we will be using Symmetric Encryption where the receiver and sender share a common key. In our previous tutorial, JAX-RS Security using JSON Web Encryption (JWE) with JWS/JWT for Authentication and Authorization we discussed the backend aspects including RESTful Web Services using JAX-RS, Jackson, Jose4j (JOSE is short for Javascript Object Signing and Encryption) using JSON Web Encryption(JWE), JSON Web Key (JWK), JSON Web Signature(JWS), and JSON Web Tokens(JWT).
Authentication and Authorization Login Security using AngularJS, Bootstrap, GridUI and MongoDB
In this JSON Web Encryption(JWE) tutorial we will create a simple form-based logon screen using Bootstrap, AngularJS and Grid-UI. This front-end application will integrate with our JAX-RS Restful Web Services to perform the authentication and authorization using JSON Web Tokens, JSON Web Encryption, JSON Web Key, and JSON Web Signature. The logon page will be used to capture the username/password and call the authenticate rest call. Our authenticate rest call will use these credentials and compare them against our MongoDB database. Upon, successful authentication an encrypted authorization token will be returned to the user. The user must then use this token on all subsequent calls by passing it in the request header. All of the restricted end-points will expect the encrypted and valid JWT token to be present in the header.

What is JSON Web Encryption (JWE)?
JSON Web Encryption, JWE for short, are encrypted using cryptographic algorithms and serialized for tokenization in HTTP authorization headers. In order to ensure the message or token has not been altered in any way the token contains a digital signature (JWS) that is cryptographically encrypted using a strong algorithm such as HMAC SHA-256.
CONTENT MASTER KEY ENCRYPTION
JWE supports three forms of Content Master Key (CMK) encryption:
- Asymmetric encryption under the recipient’s public key.
- Symmetric encryption under a key shared between the sender and receiver.
- Symmetric encryption under a key agreed upon between the sender and receiver.
Structure of JSON Web Encryption Compact Serialization
A JSON Web Encryption compact serialization is structured into five parts: The JWE Protected Header, JWE Encrypted Key, JWE Initialization Vector, JWE Ciphertext, and the JWE Authentication Tag separated by period character (.).

JWT Token encrypted using JWE and signed with JWS
Below you can see an actual encrypted JSON Web Token using JWE and base64 encoded and signed with JWS to ensure the contents have not been modified in any way.
eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2Iiwia2lkIjpudWxsLCJ jdHkiOiJKV1QifQ..TKJLnTuWFr-c88OpGrwWWw.QMr2usYlLPj4EosyZ-VFHopCkavJE QucDlPCjNaA2Rk78B1BUwci2sb6m8ZVveXKNPGGu6yl3Yd4tE_4-LyVX_kgQFEdxjAO9Y kwJ-iANdrBdomjrmarNXyqapgHuJ0Z5aTKKzSc8mbOlFbcohHp9eexDjbI1Rgy7Fxzgez eOkqkGbIuy8KGI0siCBFp6ttm5rfePCU7bjeBkPjECPk8WvxMyH9VmLJArUu1vZnEO0AZ Qvcmc8ijmId4ezX1a89KmOkxh-I_h3H8DU9Yx7On7JKonHbm7xFx9jH4nwPVtl0FB2LW7 EdZtD-baH2tgSz8jiDSjEkgaEtp61wGgynG9BG_XlO0mw-Imu_aFjz2j9bMPpZUFdlHrE ljqBYRcP9d.yrsAzczT88htfobE1B_9lg
Getting Started
In order to run this tutorial yourself, you will need the following:
- Java JDK 1.6 or greater
- Favorite IDE Spring Tool Suite (STS), Eclipse IDE or NetBeans (I happen to be using STS because it comes with a Tomcat server built-in)
- Tomcat 7 or greater or other popular container (Weblogic, Websphere, Glassfish, JBoss, VMWare vFabric, etc). For this tutorial I am using VMware vFabric tc Server Developer Edition which is essentially an enhanced Tomcat instance integrated with Spring STS
- MongoDB
- AngularJS
- Bootstrap the most popular HTML, CSS, and JS framework for developing responsive web projects
- Grid UI
Included Files
In our front-end UI application, the following list of files resides in out include folder.
angular-animate.js angular-spinner.js angular-touch.js angular.js animate.min.css app.js applogin.js bootstrap.css bootstrap.js csv.js font-awesome.css font-awesome.min.css jquery-1.11.3.js jquery.layout.js moment.js pdfmake.js spin.js styles.css ui-bootstrap-tpls-0.13.0.min.js ui-grid-unstable.css ui-grid-unstable.js ui-grid.css ui-grid.js vfs_fonts.js
Complete Project Overview
I have added the project overview to give you a full view of the structure and show you all files contained in this sample project.

RESTful Web Service End Points
Restricted URIs will be shown with LOCK icon in the table below.
# | URI | Method | Description |
---|---|---|---|
1 | /rest/security/status | GET | Displays the current status of the API being used. Non-restricted REST end-point |
2 | /rest/security/authenticate | GET | Authenticates the user by using the username and password that is passed in the header against the User in the User Collection in MongoDB data store. |
3 | /rest/security/getallroles | GET | Retrieves all roles for a given username by returning the roles as a JSON array. **Restricted REST end-point |
4 | /rest/security/showallitems | GET | Retrieves all items in our MongoDB datastore return the entire collection as a JSON array. **Restricted REST end-point |
Our Login Bootstrap/AngularJS Application
In our login screen, I have added 3D shadows and some amount of animation to make the login screen pop and give it some character. In addition, I have added Bootstrap’s validation styles for error, warning, and success messages to provide the user feedback. To use, I have added .has-warning, .has-error, or .has-success to the parent elements. So depending on whether the fields are populated or not, they will change in color from RED to GREEN. In this example, I did not add any special validations like minimum or maximum length requirements.

Reveal Password via jQuery
I have added some jQuery to allow us to show the password field when pressing and holding down the eye_icon. I have chosen to use keypress instead of onClick for security reasons. That way, the password is only ever visible during active user input.

jQuery Javascript Snippet
In the showPassword javascript function, we use jQuery to get the attribute type of an element whose id=password using the #password jquery tagID selector. Using the tagID selector we return back the type of element selected, and if it is of type ‘password’ we change its type to ‘text’ type (which makes the password become visible). Additionally, we look for an element with a tagID of eye_icon and remove a class called fa-eye, and add a class called fa-eye-slash (toggling the icon from eye_icon to eye_icon_slash).
<script type="text/javascript"> function showPassword() { console.log("Inside showPassword..."); var type = $("#password").attr("type"); if (type == "password") { $("#password").attr("type", "text"); $("#eye_icon").removeClass('fa fa-eye').addClass('fa fa-eye-slash'); } } function hidePassword() { console.log("Inside hidePassword..."); var type = $("#password").attr("type"); if (type == "text") { $("#password").attr("type", "password"); $("#eye_icon").removeClass('fa fa-eye-slash').addClass('fa fa-eye'); } } ... </script>
Password input and input-group-addon Snippet
<span class="input-group-addon"> <i class="fa fa-key fa-fw"></i> </span> <input type="password" class="form-control" id="password" name="password" required ng-model="login.password" placeholder="Password" /> <span class="input-group-addon"> <a href="" onmousedown="showPassword();" onmouseup="hidePassword();" onmouseout="hidePassword();"> <i id="eye_icon" class="fa fa-eye revealIcon"></i> </a> </span>
Login Screen (login.jsp)
<%@ page language="java" %> <%@ page import="java.util.*" %> <%@ page import="java.text.*" %> <html ng-app="app"> <head> <meta http-equiv="cache-control" content="max-age=0" /> <meta http-equiv="cache-control" content="no-cache" /> <meta http-equiv="expires" content="0" /> <meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT" /> <meta http-equiv="pragma" content="no-cache" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <!--[if lte IE 7]> <style type="text/css"> body { font-size: 85%; } </style> <![endif]--> <!-- 1.3.15 --> <script src="include/angular.js"></script> <script src="include/angular-touch.js"></script> <script src="include/angular-animate.js"></script> <script src="include/applogin.js"></script> <script src="include/jquery-1.11.3.js"></script> <script src="include/jquery.layout.js"></script> <script src="include/spin.js"></script> <script src="include/angular-spinner.js"></script> <link rel="stylesheet" href="include/font-awesome.min.css"> <script src="include/ui-bootstrap-tpls-0.13.0.min.js"></script> <script src="include/bootstrap.js"></script> <link rel="stylesheet" href="include/animate.min.css"> <link rel="styleSheet" href="include/styles.css" /> <link rel="stylesheet" href="include/bootstrap.css"> <script type="text/javascript"> function showPassword() { console.log("Inside showPassword..."); var type = $("#password").attr("type"); if (type == "password") { $("#password").attr("type", "text"); $("#eye_icon").removeClass('fa fa-eye').addClass('fa fa-eye-slash'); } } function hidePassword() { console.log("Inside hidePassword..."); var type = $("#password").attr("type"); if (type == "text") { $("#password").attr("type", "password"); $("#eye_icon").removeClass('fa fa-eye-slash').addClass('fa fa-eye'); } } function submitForm() { console.log("Inside submitForm..."); var username = $("#username").val(); var user_len = $("#username").val().length; var plainText = $("#password").val(); var pw_len = $("#password").val().length; if (user_len == 0) { $( "#error" ).html("Username is required, please try again..."); return; } if (pw_len == 0) { $( "#error" ).html("Password is required, please try again"); return; } var base64Text = window.btoa(unescape(encodeURIComponent(plainText))); $("#encoded_pw").val(base64Text); document.myform.submit(); }; function clearErrors() { console.log("Inside clearErrors..."); $( "#error" ).html(""); }; $( document ).ready(function() { console.log("Document Ready Now..."); $("#username").keypress(function(event) { console.log("keypress event..." + event); if (event.which == 13) { event.preventDefault(); submitForm(); } }); $("#password").keypress(function(event) { console.log("keypress event..." + event); if (event.which == 13) { event.preventDefault(); submitForm(); } }); }); </script> </head> <% boolean isDebug = false; String debugParam = request.getParameter("debug"); if (debugParam != null && (debugParam.toLowerCase().equals("true") || debugParam.toLowerCase().equals("yes") || debugParam.equals("1"))) { isDebug = true; } session = request.getSession(); String error_msg = (String)session.getAttribute("error"); %> <body class="login-background" ng-controller="MainCtrl"> <script type="text/ng-template" id="myModalContent.html"> <div class="modal-header-error"> <h4 class="modal-title-error"><span class="glyphicon glyphicon-alert" aria-hidden="true"></span> {{modal.title}}</h4> </div> <div class="modal-body"> <b>{{modal.message}}</b> </div> <div class="modal-footer"> <button class="btn btn-danger" ng-click="ok()">OK</button> </div> </script> <div class="login-panel"> <div class="animated bounceIn shadow"> <div class="panel panel-info"> <div class="panel-heading"><i class="fa fa-lock fa-2x"></i> <font class="loginTitle"> Login Security via JWT, JWS and JWE</font></div> <div class="panel-body"> <form id="myform" name="myform" method="POST" action="processLogin.jsp"> <div class="form-fields"> <span us-spinner spinner-key="spinner-1"></span> <div class="col-lg-10"> <input id="encoded_pw" name="encoded_pw" type="hidden" ng-model="login.encoded_pw"/> <div class="form-group has-feedback" ng-class="{'has-error': myform.username.$invalid, 'has-success': myform.username.$valid}"> <div class="input-group margin-bottom-sm" > <span class="input-group-addon"><i class="fa fa-user fa-fw"></i></span> <input type="text" class="form-control" id="username" name="username" required ng-model="login.username" placeholder="Username" focus-on="setFocus"/> </div> </div> <div><br/></div> <div class="form-group has-feedback" ng-class="{'has-error': myform.password.$invalid, 'has-success': myform.password.$valid}"> <div class="input-group margin-bottom-sm" > <span class="input-group-addon"><i class="fa fa-key fa-fw"></i></span> <input type="password" class="form-control" id="password" name="password" required ng-model="login.password" placeholder="Password" /> <span class="input-group-addon"><a href="" onmousedown="showPassword();" onmouseup="hidePassword();" onmouseout="hidePassword();"> <i id="eye_icon" class="fa fa-eye revealIcon"></i></a></span> </div> </div> <div id="failure-message" class="login-message"> <p id="error" name="error"> <% if (error_msg != null) { out.print(error_msg); } %> </p> </div> <div style="float: right;"><br/> <button id="clear" type="button" class="btn btn-primary" style="width: 80px;" ng-click="clearLogin()" onclick="clearErrors()"> <i class="fa fa-times"></i> Clear</button> <button id="login" type="button" class="btn btn-primary" style="width: 100px;" onclick="submitForm();"> <i class="fa fa-chevron-circle-left"></i> Login</button> </div> </div> </div> </form> </div> </div> </div> <div ng-element-ready="setDefaults('<%=isDebug %>')"></div> <div ng-element-ready="init()"></div> </div> </body> </html>
Logoff JSP (logoff.jsp)
<%@ page language="java" %> <%@ page import="java.util.*, org.apache.log4j.Logger" %> <% Logger logger = Logger.getLogger("com.avaldes.logoff"); String redirectURL = "login.jsp"; logger.info("redirectURL...: " + redirectURL); session = request.getSession(); session.invalidate(); response.sendRedirect(redirectURL); %>
processLogin (processLogin.jsp)
<%@ page language="java" %> <%@ page import="java.util.*" %> <%@ page import="java.util.*, org.apache.log4j.Logger, com.avaldes.util.ApacheConnection, org.json.simple.parser.*, org.json.simple.*" %> <%! static Logger logger=Logger.getLogger("com.avaldes.processLogin"); %> <% //Logger logger = Logger.getLogger("com.avaldes.processLogin"); int MaxInactiveInterval = 30*60; // 30 minutes String fullProtocol = request.getProtocol().toLowerCase(); String protocol[] = fullProtocol.split("/"); String baseUrl = protocol[0]+"://" + request.getHeader("Host"); String url=baseUrl+"/JweSecurityExample/rest/security/authenticate"; String username = request.getParameter("username"); String password = request.getParameter("password"); String loginURL = "login.jsp"; String targetURL = loginURL; if (username != null && username.equals("")) { username = null; } if (password != null && password.equals("")) { password = null; } logger.info("URL...........: [" + url + "]"); logger.info("USERNAME......: [" + username + "]"); logger.info("PASSWORD......: [" + password + "]"); // add checks for username / password if (username != null && password != null) { String redirectURL = baseUrl + "/JweSecurityExample/InventoryApp.jsp"; ApacheConnection httpConnection = new ApacheConnection(); Map<String,String> header = new HashMap<String,String>(); header.put("username", username); header.put("password", password); String authenticationJSON = httpConnection.executePost(url, header, null); logger.info("Authentication JSON...: " + authenticationJSON); JSONParser authParser=new JSONParser(); JSONObject jsonAuthObj = (JSONObject) authParser.parse(authenticationJSON); Long authStatusCode = (Long) jsonAuthObj.get("status_code"); String authMessage = (String) jsonAuthObj.get("message"); logger.info("JSONObject....: " + jsonAuthObj); logger.info("status_code...: " + authStatusCode); logger.info("message.......: " + authMessage); session = request.getSession(); if (authStatusCode != null && authStatusCode.intValue() != 200) { if (authStatusCode.intValue() == 403) { session.setAttribute("error", "Username/Password are incorrect, please try again..."); } if (authStatusCode.intValue() == 412) { session.setAttribute("error", "Username/Password is required, please try again..."); } targetURL = loginURL; } else { session.setAttribute("username", username); session.setAttribute("token", authMessage); targetURL = redirectURL; } session.setMaxInactiveInterval(MaxInactiveInterval); } else { logger.error("Username or Password is NULL..."); } response.sendRedirect(targetURL); %>
Our Sample Application
If you remember our previous tutorial, we learned that once we have authenticated, our backend restful web service generates the JWE/JWT token which is injected into our Java Session object. In our application, we are retrieving the username and JWE/JWT token passed from this session object. Our JWE/JWT token contains claims containing a list of roles assigned to a specific user. In our application, if the user has the admin role then admin options will be available to them.

Grid-UI Inventory Application (InventoryApp.jsp)
<%@ page language="java" %> <%@ page import="org.apache.log4j.Logger" %> <!doctype html> <html ng-app="app"> <head> <script src="include/angular.js"></script> <script src="include/angular-touch.js"></script> <script src="include/angular-animate.js"></script> <script src="include/csv.js"></script> <script src="include/vfs_fonts.js"></script> <script src="include/pdfmake.js"></script> <script src="include/jquery-1.11.3.js"></script> <script src="include/jquery.layout.js"></script> <script src="include/ui-grid.js"></script> <script src="include/angular-spinner.js"></script> <script src="include/spin.js"></script> <script src="include/app.js"></script> <script src="include/ui-bootstrap-tpls-0.13.0.min.js"></script> <script src="include/bootstrap.js"></script> <link rel="stylesheet" href="http://ui-grid.info/release/ui-grid.css"> <link rel="stylesheet" href="include/font-awesome.min.css"> <link rel="styleSheet" href="include/styles.css" /> <link rel="stylesheet" href="include/bootstrap.css"> </head> <%! static Logger logger = Logger.getLogger("com.avaldes.InventoryApp"); %> <% String fullProtocol = request.getProtocol().toLowerCase(); String protocol[] = fullProtocol.split("/"); String baseUrl = protocol[0] + "://" + request.getHeader("Host"); session = request.getSession(); String username = (String) session.getAttribute("username"); String token = (String) session.getAttribute("token"); logger.info("username..: " + username); logger.info("token.....: " + token); if (token == null) { String loginURL = "login.jsp"; response.sendRedirect(loginURL); } %> <body> <div ng-controller="MainCtrl"> <div class="page-header"> <h2><strong>JWT/JWS/JWE Sample Application<br> <small>Using JSON Web Tokens, JSON Web Signature and JSON Web Encryption</small></strong></h2> <span ng-show="userRoles.indexOf('admin') > 0"> <button id="login" type="button" onClick="alert('Show Admin Window...')" class="btn btn-primary" style="width: 100px;"> <i class="fa fa-user fa-fw"></i> Admin</button> </span> <a href="logoff.jsp"> <button id="login" type="button" class="btn btn-primary" style="width: 100px;" > <i class="fa fa-power-off"></i> Logout</button></a> <span class="right_justified"> <button class="btn btn-info" style="width: 200px;"> Welcome {{username}} </button> </span> </div> <div class="row"> <div class="span4"> <span us-spinner spinner-key="spinner-0"></span> <div id="grid1" ui-grid="gridOptions" class="grid"></div> </div> </div> <div ng-element-ready="setDefaults('<%=baseUrl%>', '<%=username %>', '<%=token %>')"></div> <div ng-element-ready="loadAllRoles()"></div> <div ng-element-ready="loadAllItems()"></div> </div> </body> </html>
AngularJS Application (app.js)
var app = angular.module('app', ['ngTouch', 'angularSpinner', 'ui.grid', 'ui.grid.resizeColumns', 'ui.grid.moveColumns']); app.config(['usSpinnerConfigProvider', function (usSpinnerConfigProvider) { usSpinnerConfigProvider.setDefaults({ lines: 13, // The number of lines to draw length: 5, // The length of each line width: 4, // The line thickness radius: 8, // The radius of the inner circle corners: 1, // Corner roundness (0..1) rotate: 0, // The rotation offset direction: 1, // 1: clockwise, -1: counterclockwise color: '#333', // #rgb or #rrggbb or array of colors speed: 1, // Rounds per second trail: 80, // Afterglow percentage shadow: false, // Whether to render a shadow hwaccel: false, // Whether to use hardware acceleration className: 'spinner', // The CSS class to assign to the spinner zIndex: 2e9, // The z-index (defaults to 2000000000) top: '50%', // Top position relative to parent left: '50%' // Left position relative to parent }); }]); app.service('ajaxService', function($http) { this.getData = function(URL, ajaxMethod, ajaxParams, token) { var restURL = URL + ajaxParams; console.log("Inside ajaxService..."); console.log("Connection using URL=[" + restURL + "], Method=[" + ajaxMethod + "]"); return $http({ method: ajaxMethod, url: restURL, headers: { 'token': token } }); }; this.postData = function(URL, ajaxMethod, jsonData, ajaxParams) { var restURL = URL + ajaxParams; console.log("Inside ajaxService POST..."); console.log("Connection using URL=[" + restURL + "], Method=[" + ajaxMethod + "]"); return $http({ method: ajaxMethod, url: restURL, headers: {'Content-Type': 'application/json'}, data: jsonData, }); }; }); /* ------------------------------------------------------------------ * MAIN CONTROLLER --------------------------------------------------------------------*/ app.controller('MainCtrl', function ($scope, $http, $log, uiGridConstants, ajaxService, usSpinnerService) { $scope.gridOptions = { enableCellEditOnFocus: false, enableGridMenu: false, enableSorting: true, enableRowSelection: true, enableRowHeaderSelection: false, enableColumnResizing: true, }; $scope.gridOptions.columnDefs = [ { name: '_id', displayName: 'ID', width: 120, maxWidth: 150, minWidth: 90, }, { name: 'item-id', displayName: 'Item-ID', width: 120, maxWidth: 150, minWidth: 90, }, { name: 'item-name', displayName: 'Item-Name', width: 510, maxWidth: 800, minWidth: 400, }, { name: 'price', displayName: 'Price', width: 120, maxWidth: 200, minWidth: 70, }, { name: 'quantity', displayName: 'Quantity', width: 110, maxWidth: 200, minWidth: 70, } ]; $scope.startSpin = function(key) { usSpinnerService.spin(key); }; $scope.stopSpin = function(key) { usSpinnerService.stop(key); }; $scope.loadAllRoles = function() { $scope.startSpin('spinner-0'); console.log("Inside loadUserRoles " + $scope.loadAllRolesUrl); function onSuccess(response) { console.log("+++++loadUserRoles SUCCESS++++++"); if (response.data.status_code != '403' || response.data.status_code != '404') { $scope.userRoles = response.data; } $scope.stopSpin('spinner-0'); }; function onError(response) { console.log("-------loadUserRoles FAILED-------"); $scope.stopSpin('spinner-0'); console.log("Inside loadUserRoles error condition..."); }; //----MAKE AJAX REQUEST CALL to GET DATA---- ajaxService.getData($scope.loadAllRolesUrl, 'GET', '', $scope.token).then(onSuccess, onError); }; $scope.loadAllItems = function() { $scope.startSpin('spinner-0'); console.log("Inside loadAllItems " + $scope.loadAllItemsUrl); function onSuccess(response) { console.log("+++++loadAllItems SUCCESS++++++"); if (response.data.status_code != '404') { $scope.gridOptions.data = response.data; } $scope.stopSpin('spinner-0'); }; function onError(response) { console.log("-------loadAllItems FAILED-------"); $scope.stopSpin('spinner-0'); console.log("Inside loadAllItems error condition..."); }; //----MAKE AJAX REQUEST CALL to GET DATA---- ajaxService.getData($scope.loadAllItemsUrl, 'GET', '', $scope.token).then(onSuccess, onError); }; $scope.setDefaults = function(baseUrl, username, token) { $scope.loadAllRolesUrl = baseUrl + "/JweSecurityExample/rest/security/getallroles"; $scope.loadAllItemsUrl = baseUrl + "/JweSecurityExample/rest/security/showallitems"; $scope.username = username; $scope.token = token; console.log("Setting Defaults"); console.log("loadAllRolesUrl....: " + $scope.loadAllRolesUrl); console.log("loadAllItemsUrl....: " + $scope.loadAllItemsUrl); console.log("username...........: " + $scope.username); console.log("token..............: " + $scope.token); }; }); app.directive('ngElementReady', [function() { return { priority: Number.MIN_SAFE_INTEGER, restrict: "A", link: function($scope, $element, $attributes) { $scope.$eval($attributes.ngElementReady); } }; }]);
AngularJS ApplicationLogin (applogin.js)
var app = angular.module('app', ['ui.bootstrap', 'angularSpinner']); app.config(['usSpinnerConfigProvider', function (usSpinnerConfigProvider) { usSpinnerConfigProvider.setDefaults({ lines: 13, // The number of lines to draw length: 5, // The length of each line width: 4, // The line thickness radius: 8, // The radius of the inner circle corners: 1, // Corner roundness (0..1) rotate: 0, // The rotation offset direction: 1, // 1: clockwise, -1: counterclockwise color: '#333', // #rgb or #rrggbb or array of colors speed: 1, // Rounds per second trail: 80, // Afterglow percentage shadow: false, // Whether to render a shadow hwaccel: false, // Whether to use hardware acceleration className: 'spinner', // The CSS class to assign to the spinner zIndex: 2e9, // The z-index (defaults to 2000000000) top: '50%', // Top position relative to parent left: '50%' // Left position relative to parent }); }]); app.directive('ngElementReady', [function() { return { priority: Number.MIN_SAFE_INTEGER, restrict: "A", link: function($scope, $element, $attributes) { $scope.$eval($attributes.ngElementReady); } }; }]); app.directive('focusOn', function() { return function(scope, elem, attr) { scope.$on(attr.focusOn, function(e) { elem[0].focus(); }); }; }); app.service('ajaxService', function($http) { this.getData = function(URL, ajaxMethod, ajaxParams) { var restURL = URL + ajaxParams; console.log("Inside ajaxService..."); console.log("Connection using URL=[" + restURL + "], Method=[" + ajaxMethod + "]"); return $http({ method: ajaxMethod, url: restURL, }); }; this.postData = function(URL, ajaxMethod, jsonData, ajaxParams) { var restURL = URL + ajaxParams; console.log("Inside ajaxService POST..."); console.log("Connection using URL=[" + restURL + "], Method=[" + ajaxMethod + "]"); return $http({ method: ajaxMethod, url: restURL, headers: {'Content-Type': 'application/json'}, data: jsonData, }); }; this.postFormData=function(URL, ajaxMethod, jsonData, ajaxParams) { var restURL = URL + ajaxParams; console.log("Inside ajaxService POST..."); console.log("Connection using URL=[" + restURL + "], Method=[" + ajaxMethod + "]"); return $http({ method: ajaxMethod, url: restURL, headers:{'Content-Type':'application/x-www-form-urlencoded' }, data: jsonData, }); }; }); /* --------------------------------------------------------------- * MAIN CONTROLLER -----------------------------------------------------------------*/ app.controller('MainCtrl', function ($scope, $rootScope, $http, $log, $timeout, $modal, $filter, ajaxService, usSpinnerService) { $scope.showModal = false; $scope.debugFlag = false; $scope.modal = {}; $scope.login = {}; $scope.startSpin = function(key) { usSpinnerService.spin(key); }; $scope.stopSpin = function(key) { usSpinnerService.stop(key); }; $scope.init = function() { console.log("Inside init()..."); $scope.login = {}; $scope.$broadcast('setFocus'); }; $scope.setDefaults = function(debugFlag) { $scope.debugFlag = debugFlag; }; $scope.clearLogin = function() { console.log('Inside clearLogin...'); $scope.login = {}; $rootScope.$broadcast('setFocus'); }; $scope.processLogin = function() { $scope.startSpin('spinner-1'); console.log('Inside loginUser: '); //---Cancel Modal Dialog Window--- $scope.cancel = function () { console.log('Closing Modal Dialog Window...'); $scope.showModal = false; }; getLoginURL = "processLogin.jsp?"; getLoginURL += '&etc=' + new Date().getTime(); console.log("getLoginURL...: " + getLoginURL); function onSuccess(response) { console.log("+++++getLoginURL SUCCESS++++++"); if ($scope.debugFlag == 'true') { console.log("Inside getLoginURL response..." + JSON.stringify(response.data)); } else { console.log("Inside getLoginURL response... (XML response is being skipped, debug=false)"); } if (response.data.status_code == '404') { $scope.showModalWindow('Error!',response.data.message, 'sm'); } else { } $scope.stopSpin('spinner-1'); }; function onError(response) { console.log("-------getLoginURL FAILED-------"); $scope.stopSpin('spinner-1'); console.log("Inside getLoginURL error condition..."); $scope.showModalWindow('Error!', response.data.message, 'sm'); }; //----MAKE AJAX REQUEST CALL to POST DATA---- ajaxService.postFormData(getLoginURL, 'POST', $scope.login, '').then(onSuccess, onError); }; }); /* ----------------------------------------------------------------- * MODAL DIALOG WINDOW CONTROLLER -------------------------------------------------------------------*/ app.controller('ModalInstanceCtrl', function($scope,$modalInstance) { $scope.ok = function () { $modalInstance.dismiss('cancel'); }; });
MongoDB Items Collections
{ "_id" : "10029T1", "item-id" : "123", "item-name" : "KitchenAid Artisan 5 qt. Stand Mixer", "price" : 314.99, "quantity" : 13 } { "_id" : "12349K1", "item-id" : "k10001", "item-name" : "Keurig K10 MINI Plus Brewing System", "price" : 79.99, "quantity" : 36 } { "_id" : "83949PT", "item-id" : "EPP1029", "item-name" : "Electric Power Pressure Cooker XL (8 qt)", "price" : 119.99, "quantity" : 8 } { "_id" : "71829Y", "item-id" : "IQ50009", "item-name" : "KitchenIQ 50009 Edge Grip 2 Stage Knife Sharpener, Black", "price" : 5.79, "quantity" : 23 } { "_id" : "30814B", "item-id" : "3081414B", "item-name" : "La Crosse Technology 308-1414B Wireless Atomic Digital Color Forecast Station", "price" : 49.99, "quantity" : 6 } { "_id" : "PAN110CFM", "item-id" : "110CFM", "item-name" : "Panasonic FV-11VQ5 WhisperCeiling 110 CFM Ceiling Mounted Fan, White", "price" : 113.79, "quantity" : 7 } { "_id" : "AS4175", "item-id" : "AS4175", "item-name" : "American Standard 4175.300.075 Colony Soft Pull-Down Kitchen Faucet, Stainless Steel", "price" : 120, "quantity" : 9 } { "_id" : "FM3700B", "item-id" : "FM3700B", "item-name" : "PUR Advanced Faucet Water Filter Chrome FM-3700B", "price" : 23.95, "quantity" : 27 } { "_id" : "ARC150SB", "item-id" : "ARC150SB", "item-name" : "Aroma 20 Cup Cooked (10 cup uncooked) Digital Rice Cooker, Slow Cooker", "price" : 36.99, "quantity" : 13 } { "_id" : "CPT180TST", "item-id" : "CPT180TST", "item-name" : "Cuisinart Metal Classic 4-Slice Toaster", "price" : 69.99, "quantity" : 6 } { "_id" : "GR4NWPAN", "item-id" : "GR4NWPAN", "item-name" : "Cuisinart Griddler® and Waffle Maker with Removable Plates", "price" : 99.99, "quantity" : 13 }
MongoDB Users Collections
{ "_id" : "1", "username" : "apacheuser", "password" : "Summer95!", "firm" : "Apache", "roles" : ["client", "admin"] } { "_id" : "2", "username" : "springuser", "password" : "Spring99!", "firm" : "SpringSource", "roles" : ["client"] } { "_id" : "3", "username" : "user3", "password" : "Autumn03!", "firm" : "RedHat", "roles" : ["client"] }
Testing out the Web Services
In addition to using our AngularJS/Bootstrap/Grid-UI web application to test out our restful services I used both CURL and Postman which is a Google Chrome Application. Using this tool I validated each of the REST API calls. Please review the screen shots below:
Testing Application and POSTMAN Chrome Extension
Download the Complete Source Code
That’s It!
I hope you enjoyed this tutorial. It was certainly a lot of fun putting it together and testing it out. Please continue to share the love and like us so that we can continue bringing you quality tutorials. Happy Coding!!!

Please Share Us on Social Media






Leave a Reply