Handled session expiration

This commit is contained in:
fabio.formosa
2017-07-28 10:51:09 +02:00
parent 68e17e6d08
commit efadbe682e
19 changed files with 584 additions and 57 deletions

View File

@@ -40,6 +40,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-velocity</artifactId>

View File

@@ -5,6 +5,7 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.thymeleaf.extras.springsecurity4.dialect.SpringSecurityDialect;
import org.thymeleaf.spring4.SpringTemplateEngine;
import org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.spring4.view.ThymeleafViewResolver;
@@ -19,12 +20,10 @@ public class MVCConfig extends WebMvcConfigurerAdapter {
registry.addViewController("/login").setViewName("login");
registry.addViewController("/").setViewName("redirect:/manager");
registry.addViewController("/templates/manager/config-form.html")
.setViewName("manager/config-form");
registry.addViewController("/templates/manager/config-form.html").setViewName("manager/config-form");
registry.addViewController("/templates/manager/progress-panel.html")
.setViewName("manager/progress-panel");
registry.addViewController("/templates/manager/logs-panel.html")
.setViewName("manager/logs-panel");
registry.addViewController("/templates/manager/logs-panel.html").setViewName("manager/logs-panel");
}
@@ -33,6 +32,7 @@ public class MVCConfig extends WebMvcConfigurerAdapter {
final SpringTemplateEngine springTemplateEngine = new SpringTemplateEngine();
springTemplateEngine.addTemplateResolver(templateResolver());
springTemplateEngine.addDialect(new LayoutDialect());
springTemplateEngine.addDialect(new SpringSecurityDialect());
return springTemplateEngine;
}

View File

@@ -1,5 +1,7 @@
package it.fabioformosa.quartzmanager.configuration;
import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
@@ -8,21 +10,24 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import it.fabioformosa.quartzmanager.security.AjaxAuthenticationFilter;
import it.fabioformosa.quartzmanager.security.ComboEntryPoint;
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Configuration
@Order(1)
public static class ApiWebSecurityConfig
extends WebSecurityConfigurerAdapter {
public static class ApiWebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable() //
.antMatcher("/notifications").authorizeRequests()
.anyRequest().hasAnyRole("ADMIN").and().httpBasic();
.antMatcher("/notifications").authorizeRequests().anyRequest().hasAnyRole("ADMIN").and()
.httpBasic();
// http.antMatcher("/logs/**").authorizeRequests().anyRequest()
// .permitAll();
@@ -31,30 +36,30 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Configuration
@Order(2)
public static class FormWebSecurityConfig
extends WebSecurityConfigurerAdapter {
public static class FormWebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/css/**", "/js/**", "/img/**",
"/lib/**");
}
@Resource
private ComboEntryPoint comboEntryPoint;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().anyRequest()
.authenticated().and().formLogin().loginPage("/login")
.permitAll().and().logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/manager");
http.exceptionHandling().authenticationEntryPoint(comboEntryPoint).and().csrf().disable()//
.authorizeRequests().anyRequest().authenticated().and()//
.addFilterBefore(new AjaxAuthenticationFilter(authenticationManager()),
UsernamePasswordAuthenticationFilter.class)//
.formLogin().loginPage("/login").permitAll().and().logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/manager");
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/css/**", "/js/**", "/img/**", "/lib/**");
}
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth)
throws Exception {
auth.inMemoryAuthentication().withUser("admin").password("admin")
.roles("ADMIN");
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("admin").password("admin").roles("ADMIN");
}
}

View File

@@ -0,0 +1,34 @@
package it.fabioformosa.quartzmanager.controllers;
import javax.servlet.http.HttpSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/session")
public class SessionController {
private final Logger log = LoggerFactory.getLogger(SessionController.class);
@RequestMapping("/invalidate")
@PreAuthorize("hasAuthority('ADMIN')")
public String invalidateSession(HttpSession session) {
session.invalidate();
log.info("Invalidated current session!");
return "redirect:/manager";
}
@RequestMapping("/refresh")
@PreAuthorize("hasAuthority('ADMIN')")
public HttpEntity<Void> refreshSession(HttpSession session) {
return new ResponseEntity<>(HttpStatus.OK);
}
}

View File

@@ -0,0 +1,55 @@
package it.fabioformosa.quartzmanager.security;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.WebAttributes;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
public class AjaxAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
public class AjaxLoginAuthSuccessHandler extends SimpleUrlAuthenticationSuccessHandler
implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
response.setStatus(HttpServletResponse.SC_OK);
clearAuthenticationAttributes(request);
return;
}
}
public AjaxAuthenticationFilter(AuthenticationManager authenticationManager) {
setAuthenticationManager(authenticationManager);
setUsernameParameter("ajaxUsername");
setPasswordParameter("ajaxPassword");
setPostOnly(true);
setFilterProcessesUrl("/ajaxLogin");
setAuthenticationSuccessHandler(new AjaxLoginAuthSuccessHandler());
}
/**
* Removes temporary authentication-related data which may have been stored
* in the session during the authentication process.
*/
protected final void clearAuthenticationAttributes(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session == null)
return;
session.removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
}
}

View File

@@ -0,0 +1,33 @@
package it.fabioformosa.quartzmanager.security;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.stereotype.Component;
@Component
public class ComboEntryPoint extends LoginUrlAuthenticationEntryPoint {
private static final String LOGIN_FORM_URL = "/login";
public ComboEntryPoint() {
super(LOGIN_FORM_URL);
}
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
if (RESTRequestMatcher.isRestRequest(request)
|| WebsocketRequestMatcher.isWebsocketConnectionRequest(request))
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
else
super.commence(request, response, authException);
}
}

View File

@@ -0,0 +1,26 @@
package it.fabioformosa.quartzmanager.security;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.web.util.matcher.ELRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
public class RESTRequestMatcher {
static private final Logger log = LoggerFactory.getLogger(RESTRequestMatcher.class);
static public RequestMatcher matcherRequestedWith = new ELRequestMatcher(
"hasHeader('X-Requested-With','XMLHttpRequest')");
static public RequestMatcher matcherAccept = new ELRequestMatcher(
"hasHeader('accept','application/json, text/plain, */*')");
static public boolean isRestRequest(HttpServletRequest request) {
log.trace("Detecting if it's an AJAX Request: " + request.getRequestURL() + " accept: "
+ request.getHeader("accept") + " " + " X-Requested-With: "
+ request.getHeader("X-Requested-With"));
return matcherRequestedWith.matches(request) || matcherAccept.matches(request);
}
}

View File

@@ -0,0 +1,18 @@
package it.fabioformosa.quartzmanager.security;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class WebsocketRequestMatcher {
static private final Logger log = LoggerFactory.getLogger(WebsocketRequestMatcher.class);
static public boolean isWebsocketConnectionRequest(HttpServletRequest request) {
log.trace("Detecting if it's a Websocket Connection Request: " + request.getRequestURL());
return request.getServletPath().equals("/progress/info")
|| request.getServletPath().equals("/logs/info");
}
}

View File

@@ -1,5 +1,6 @@
server.context-path=/quartz-manager
server.port=9000
server.session.timeout=28800
spring.thymeleaf.cache=false
spring.thymeleaf.mode=LEGACYHTML5

View File

@@ -1 +1 @@
var schedulerApp = angular.module('schedulerApp', ['starter', 'configurator', 'ff-websocket', 'progress']);
var schedulerApp = angular.module('schedulerApp', ['starter', 'configurator', 'ff-websocket', 'progress', 'http-auth-interceptor', 'authenticationComp']);

View File

@@ -0,0 +1,134 @@
'use strict';
var authenticationComp = angular.module('authenticationComp', ['http-auth-interceptor']);
/**********************************************************************************/
//SESSION REFRESH
authenticationComp.directive('sessionRefresh',
['authService','$rootScope', '$http', '$location',
'LoginService', function(authService, $rootScope, $http, $location, LoginService) {
var openedLoginDialog = false;
return{
restrict: 'AC',
link: function(scope, elem, attrs) {
var lastRefreshTimeForAnonymous;
$rootScope.$on('event:auth-loginRequired', function(e, arg) {
//show login dialog
arg.ajaxLoginError = arg.ajaxLoginError || "";
attrs.title = attrs.title || "Session Expired";
attrs.commonErrorLabel = attrs.commonErrorLabel || "Authenticaion failed:";
attrs.wrongPasswordMsg = attrs.wrongPasswordMsg || "Wrong Password! Please, re-try.";
attrs.serverConnectionFailedMsg = attrs.serverConnectionFailedMsg || "Server connection failed, please try later";
var username = attrs.username || "admin";
if(openedLoginDialog)
return;
openedLoginDialog = true;
bootbox.dialog({
message: "<div id=\"refreshSessionDialog\">" +
"<h4 id=\"refreshSessionHeader\">Login</h4>"+
"<span id=\"refreshSessionError\">" + arg.ajaxLoginError + " </span>"+
"<table id=\"refreshSessionTable\">" +
"<tr>" +
"<label for=\"username\" class=\"block clearfix\">" +
"<span class=\"block input-icon input-icon-right\">" +
"<input id=\"ajaxLoginUsername\" name=\"ajaxLoginUsername\" class=\"form-control\" type=\"text\" value=\"" + username + "\" readonly=\"true\" size=\"35\">" +
"</span>" +
"</label>" +
"</tr>" +
"<tr>" +
"<label for=\"username\" class=\"block clearfix\">" +
"<span class=\"block input-icon input-icon-right\">" +
"<input id=\"ajaxLoginPassword\" name=\"ajaxLoginPassword\" class=\"form-control\" type=\"password\" placeholder=\"password\" size=\"35\">" +
"</span>" +
"</label>"+
"</tr>" +
"</table>" +
"</div>",
title: attrs.title,
onEscape: function() {
openedLoginDialog = false;
},
buttons:{
main:{
label: "Ok",
className: "btn-primary",
callback: function() {
openedLoginDialog = false;
var postData = {};
postData.ajaxUsername = $('#ajaxLoginUsername').val();
postData.ajaxPassword = $('#ajaxLoginPassword').val();
var loginCompleted = LoginService.doLogin(postData.ajaxUsername, postData.ajaxPassword, true);
loginCompleted
.then(function(){
authService.loginConfirmed();
},function(msgError){
$rootScope.$emit('event:auth-loginRequired', {ajaxLoginError: msgError});
});
}
},
},
});
$('#refreshSessionDialog').closest('.modal').css('z-index', '9001');
});
$rootScope.$on('event:auth-loginConfirmed', function() {
//nothing
});
}
};
}]);
/**********************************************************************************/
//LOGIN SERVICE
authenticationComp.service('LoginService', ['$q', '$http', '$window', '$log', function($q, $http, $window, $log){
this.doLogin = function(username, password, ignoreAuthModule){
ignoreAuthModule = ignoreAuthModule || false;
var ajaxLoginDone = $q.defer();
var postData = {};
postData.ajaxUsername = username;
postData.ajaxPassword = password;
$http({
method: 'POST',
url: 'ajaxLogin',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
transformRequest: function(obj) {
var str = [];
for(var p in obj)
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
return str.join("&");
},
data: postData,
ignoreAuthModule : ignoreAuthModule
})
.then(function (res) {
if(res.status == 200){
for(var i in res.cookies)
if(res.cookies[i] != null)
document.cookie = res.cookies[i];
ajaxLoginDone.resolve(res.loggedUser);
}
else
ajaxLoginDone.reject(res.msgError);
}, function(data, status){
ajaxLoginDone.reject();
});
return ajaxLoginDone.promise;
};
}]);

View File

@@ -1,23 +1,15 @@
angular.module('progress')
.directive('logsPanel', ['LogService', function(LogService){
var appendLog = function(log){
var logTable = document.getElementById('logTable');
var row = document.createElement('tr');
row.style.wordWrap = 'break-word';
row.appendChild(document.createTextNode(log));
logTable.appendChild(row);
}
var MAX_LOGS = 10;
return{
restrict: 'E',
controller : ['$scope', function($scope){
controller : ['$scope', '$rootScope', '$http', function($scope, $rootScope, $http){
$scope.logs = new Array();
LogService.receive().then(null, null, function(logRecord){
var _showNewLog = function(logRecord){
if($scope.logs.length > MAX_LOGS)
$scope.logs.pop();
@@ -28,7 +20,31 @@ angular.module('progress')
logItem.threadName = logRecord.threadName;
$scope.logs.unshift(logItem);
})
};
var _refreshSession = function(){
$http({
method : 'GET',
url : 'session/refresh'
});
};
var _handleNewMsgFromLogWebsocket = function(receivedMsg){
if(receivedMsg.type == 'SUCCESS')
_showNewLog(receivedMsg.message);
else if(receivedMsg.type == 'ERROR')
_refreshSession(); //if websocket has been closed for session expiration, try to refresh it
};
LogService.receive().then(null, null, function(receivedMsg){
_handleNewMsgFromLogWebsocket(receivedMsg);
});
$rootScope.$on('event:auth-loginConfirmed', function() {
//REST API login succeeded, now open websocket connection again
LogService.reconnectNow();
});
}],
templateUrl : 'templates/manager/logs-panel.html'
};

View File

@@ -1,16 +1,25 @@
angular.module('progress')
.directive('progressPanel',['ProgressService', function(ProgressService){
return{
restrict: 'E',
controller : ['$scope', function($scope){
.directive('progressPanel', [ 'ProgressService', function(ProgressService) {
return {
restrict : 'E',
controller : [ '$scope', '$rootScope', function($scope, $rootScope) {
ProgressService.receive().then(null, null, function(receivedMsg) {
if (receivedMsg.type == 'SUCCESS') {
var newStatus = receivedMsg.message;
$scope.progress = newStatus;
$scope.percentageStr = $scope.progress.percentage + '%';
}
});
$rootScope.$on('event:auth-loginConfirmed', function() {
//re-open websocket connection after REST API login
ProgressService.reconnectNow();
});
}],
ProgressService.receive().then(null, null, function(newStatus){
$scope.progress = newStatus;
$scope.percentageStr = $scope.progress.percentage + '%';
});
}],
templateUrl : 'templates/manager/progress-panel.html'
};
}]);
templateUrl : 'templates/manager/progress-panel.html'
};
} ]);

View File

@@ -29,6 +29,7 @@ angular.module('ff-websocket')
var getMessage = function(data) {
var out = {};
out.type = 'SUCCESS';
out.message = JSON.parse(data.body);
out.headers = {};
out.headers.messageId = data.headers["message-id"];
@@ -40,24 +41,42 @@ angular.module('ff-websocket')
return out;
};
that.reconnect = function() {
$timeout(function() {
initialize();
that.reconnectionPromise = null;
that.scheduleReconnection = function() {
that.reconnectionPromise = $timeout(function() {
console.log("Socket reconnecting... (if it fails, next attempt in " + that.options.RECONNECT_TIMEOUT + " msec)");
_initialize();
}, that.options.RECONNECT_TIMEOUT);
};
that.reconnectNow = function(){
_socket.stomp.disconnect();
if(that.reconnectionPromise && that.reconnectionPromise.cancel)
that.reconnectionPromise.cancel();
_initialize();
};
var _errorCallback = function(errorMsg){
var out = {};
out.type = 'ERROR';
out.message = errorMsg;
_deferred.notify(out);
that.scheduleReconnection();
};
var _startListener = function(frame){
console.log('Connected: ' + frame);
_socket.stomp.subscribe(that.options.TOPIC_NAME, function(data){
_deferred.notify(getMessage(data).message);
_deferred.notify(getMessage(data));
});
};
var _initialize = function(){
_socket.client = new SockJS(that.options.SOCKET_URL);
_socket.stomp = Stomp.over(_socket.client);
_socket.stomp.connect({}, _startListener);
_socket.stomp.onclose = that.reconnect;
_socket.stomp.connect({}, _startListener, _errorCallback);
_socket.stomp.onclose = that.scheduleReconnection;
};
that.receive = function(){

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,141 @@
/*global angular:true, browser:true */
/**
* @license HTTP Auth Interceptor Module for AngularJS
* (c) 2012 Witold Szczerba
* License: MIT
*/
(function () {
'use strict';
angular.module('http-auth-interceptor', ['http-auth-interceptor-buffer'])
.factory('authService', ['$rootScope','httpBuffer', function($rootScope, httpBuffer) {
return {
/**
* Call this function to indicate that authentication was successful and trigger a
* retry of all deferred requests.
* @param data an optional argument to pass on to $broadcast which may be useful for
* example if you need to pass through details of the user that was logged in
* @param configUpdater an optional transformation function that can modify the
* requests that are retried after having logged in. This can be used for example
* to add an authentication token. It must return the request.
*/
loginConfirmed: function(data, configUpdater) {
var updater = configUpdater || function(config) {return config;};
$rootScope.$broadcast('event:auth-loginConfirmed', data);
httpBuffer.retryAll(updater);
},
/**
* Call this function to indicate that authentication should not proceed.
* All deferred requests will be abandoned or rejected (if reason is provided).
* @param data an optional argument to pass on to $broadcast.
* @param reason if provided, the requests are rejected; abandoned otherwise.
*/
loginCancelled: function(data, reason) {
httpBuffer.rejectAll(reason);
$rootScope.$broadcast('event:auth-loginCancelled', data);
}
};
}])
/**
* $http interceptor.
* On 401 response (without 'ignoreAuthModule' option) stores the request
* and broadcasts 'event:auth-loginRequired'.
* On 403 response (without 'ignoreAuthModule' option) discards the request
* and broadcasts 'event:auth-forbidden'.
*/
.config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push(['$rootScope', '$q', 'httpBuffer', function($rootScope, $q, httpBuffer) {
return {
responseError: function(rejection) {
var config = rejection.config || {};
if (!config.ignoreAuthModule) {
switch (rejection.status) {
case 401:
var deferred = $q.defer();
var bufferLength = httpBuffer.append(config, deferred);
if (bufferLength === 1)
$rootScope.$broadcast('event:auth-loginRequired', rejection);
return deferred.promise;
case 403:
$rootScope.$broadcast('event:auth-forbidden', rejection);
break;
}
}
// otherwise, default behaviour
return $q.reject(rejection);
}
};
}]);
}]);
/**
* Private module, a utility, required internally by 'http-auth-interceptor'.
*/
angular.module('http-auth-interceptor-buffer', [])
.factory('httpBuffer', ['$injector', function($injector) {
/** Holds all the requests, so they can be re-requested in future. */
var buffer = [];
/** Service initialized later because of circular dependency problem. */
var $http;
function retryHttpRequest(config, deferred) {
function successCallback(response) {
deferred.resolve(response);
}
function errorCallback(response) {
deferred.reject(response);
}
$http = $http || $injector.get('$http');
$http(config).then(successCallback, errorCallback);
}
return {
/**
* Appends HTTP request configuration object with deferred response attached to buffer.
* @return {Number} The new length of the buffer.
*/
append: function(config, deferred) {
return buffer.push({
config: config,
deferred: deferred
});
},
/**
* Abandon or reject (if reason provided) all the buffered requests.
*/
rejectAll: function(reason) {
if (reason) {
for (var i = 0; i < buffer.length; ++i) {
buffer[i].deferred.reject(reason);
}
}
buffer = [];
},
/**
* Retries all the buffered requests clears the buffer.
*/
retryAll: function(updater) {
for (var i = 0; i < buffer.length; ++i) {
var _cfg = updater(buffer[i].config);
if (_cfg !== false)
retryHttpRequest(_cfg, buffer[i].deferred);
}
buffer = [];
}
};
}]);
})();
/* commonjs package manager support (eg componentjs) */
if (typeof module !== "undefined" && typeof exports !== "undefined" && module.exports === exports){
module.exports = 'http-auth-interceptor';
}

View File

@@ -0,0 +1 @@
!function(){"use strict";angular.module("http-auth-interceptor",["http-auth-interceptor-buffer"]).factory("authService",["$rootScope","httpBuffer",function($rootScope,httpBuffer){return{loginConfirmed:function(data,configUpdater){var updater=configUpdater||function(config){return config};$rootScope.$broadcast("event:auth-loginConfirmed",data),httpBuffer.retryAll(updater)},loginCancelled:function(data,reason){httpBuffer.rejectAll(reason),$rootScope.$broadcast("event:auth-loginCancelled",data)}}}]).config(["$httpProvider",function($httpProvider){$httpProvider.interceptors.push(["$rootScope","$q","httpBuffer",function($rootScope,$q,httpBuffer){return{responseError:function(rejection){var config=rejection.config||{};if(!config.ignoreAuthModule)switch(rejection.status){case 401:var deferred=$q.defer(),bufferLength=httpBuffer.append(config,deferred);return 1===bufferLength&&$rootScope.$broadcast("event:auth-loginRequired",rejection),deferred.promise;case 403:$rootScope.$broadcast("event:auth-forbidden",rejection)}return $q.reject(rejection)}}}])}]),angular.module("http-auth-interceptor-buffer",[]).factory("httpBuffer",["$injector",function($injector){function retryHttpRequest(config,deferred){function successCallback(response){deferred.resolve(response)}function errorCallback(response){deferred.reject(response)}$http=$http||$injector.get("$http"),$http(config).then(successCallback,errorCallback)}var $http,buffer=[];return{append:function(config,deferred){return buffer.push({config:config,deferred:deferred})},rejectAll:function(reason){if(reason)for(var i=0;i<buffer.length;++i)buffer[i].deferred.reject(reason);buffer=[]},retryAll:function(updater){for(var i=0;i<buffer.length;++i){var _cfg=updater(buffer[i].config);_cfg!==!1&&retryHttpRequest(_cfg,buffer[i].deferred)}buffer=[]}}}])}(),"undefined"!=typeof module&&"undefined"!=typeof exports&&module.exports===exports&&(module.exports="http-auth-interceptor");

View File

@@ -4,7 +4,7 @@
<head lang="en" th:fragment="head">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<link href="http://cdn.jsdelivr.net/webjars/bootstrap/3.3.4/css/bootstrap.min.css"
<link href="http://cdn.jsdelivr.net/webjars/bootstrap/3.3.7/css/bootstrap.min.css"
rel="stylesheet" media="screen" />
<link th:href="@{css/animate.css}" rel="stylesheet" />
@@ -15,10 +15,16 @@
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css"/>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script src="http://cdn.jsdelivr.net/webjars/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
<script th:src="@{/js/lib/bootbox.min.js}"></script>
<script th:src="@{/js/lib/sockjs-0.3.4.js}"></script>
<script th:src="@{/js/lib/stomp.js}"></script>
<script th:src="@{/js/lib/http-auth-interceptor.min.js}"></script>
<script th:src="@{/js/app/components/starter/starter-module.js}"></script>
<script th:src="@{/js/app/components/starter/starter-directives.js}"></script>
@@ -33,6 +39,9 @@
<script th:src="@{/js/app/components/progress/progress-directive.js}"></script>
<script th:src="@{/js/app/components/progress/logs-service.js}"></script>
<script th:src="@{/js/app/components/progress/logs-directive.js}"></script>
<script th:src="@{/js/app/components/authentication/authentication.js}"></script>
<script th:src="@{/js/app/app.js}"></script>
</head>

View File

@@ -57,5 +57,21 @@
<th:block layout:fragment="script"></th:block>
</div><!-- row -->
</div> <!-- container-fluid -->
<!-- REFRESH SESSION DIALOG -->
<div id="refreshSessionBox"
data-session-refresh="active"
data-ajax-login-server='/'
th:attr="data-username=${#authentication.name}"
data-title="Session Expired"
data-common-error-label="authentication failed!"
data-wrong-password-msg="Wrong password! Please, re-try."
data-server-connection-failed-msg="Unexpected error! Server connection failed. Please, try later."
data-relogin-failed-msg="Session refreshing failed! Please, refresh current page to continue"
data-relogin-label="Authentication"
style="display:none;"
>
</div>
</body>
</html>