Merge pull request #25 from patriot1burke/master
session timeout improvements
This commit is contained in:
commit
25888b2ef4
7 changed files with 73 additions and 92 deletions
|
@ -1,8 +1,8 @@
|
||||||
{
|
{
|
||||||
"realm" : "demo",
|
"realm" : "demo",
|
||||||
"enabled" : true,
|
"enabled" : true,
|
||||||
"tokenLifespan" : 6000,
|
"tokenLifespan" : 10,
|
||||||
"accessCodeLifespan" : 30,
|
"accessCodeLifespan" : 10,
|
||||||
"sslNotRequired" : true,
|
"sslNotRequired" : true,
|
||||||
"cookieLoginAllowed" : true,
|
"cookieLoginAllowed" : true,
|
||||||
"privateKey" : "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
|
"privateKey" : "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
|
||||||
|
|
|
@ -22,7 +22,6 @@
|
||||||
<script src="lib/angular/angular-resource.js"></script>
|
<script src="lib/angular/angular-resource.js"></script>
|
||||||
<script src="lib/angular/ui-bootstrap-tpls-0.4.0.js"></script>
|
<script src="lib/angular/ui-bootstrap-tpls-0.4.0.js"></script>
|
||||||
|
|
||||||
<script src="/auth-server/rest/saas/isLoggedIn.js"></script>
|
|
||||||
<script src="js/app.js"></script>
|
<script src="js/app.js"></script>
|
||||||
<script src="js/controllers.js"></script>
|
<script src="js/controllers.js"></script>
|
||||||
<script src="js/loaders.js"></script>
|
<script src="js/loaders.js"></script>
|
||||||
|
@ -63,13 +62,14 @@
|
||||||
<script src="lib/jquery/jquery.idletimeout.js" type="text/javascript"></script>
|
<script src="lib/jquery/jquery.idletimeout.js" type="text/javascript"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$.idleTimeout('#idletimeout', '#idletimeout a', {
|
$.idleTimeout('#idletimeout', '#idletimeout a', {
|
||||||
idleAfter: 5,
|
idleAfter: 300,
|
||||||
pollingInterval: 0,
|
pollingInterval: 60,
|
||||||
keepAliveURL: '',
|
keepAliveURL: '/auth-server/rest/saas/keepalive',
|
||||||
serverResponseEquals: '',
|
serverResponseEquals: '',
|
||||||
|
failedRequests: 1,
|
||||||
onTimeout: function(){
|
onTimeout: function(){
|
||||||
$(this).slideUp();
|
$(this).slideUp();
|
||||||
window.location = "timeout.html";
|
window.location = "/auth-server/rest/saas/logout";
|
||||||
},
|
},
|
||||||
onIdle: function(){
|
onIdle: function(){
|
||||||
$(this).slideDown(); // show the warning bar
|
$(this).slideDown(); // show the warning bar
|
||||||
|
@ -84,35 +84,4 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
<!--
|
|
||||||
<body data-ng-controller="GlobalCtrl">
|
|
||||||
<div id="idletimeout">
|
|
||||||
You will be logged off in <span></span> seconds due to inactivity.
|
|
||||||
<a id="idletimeout-resume" href="#">Click here to continue using this web page</a>.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="alert-container" data-ng-show="notification" data-ng-click="notification = null">
|
|
||||||
<div class="alert alert-{{notification.type}}">{{notification.message}}</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="wrap">
|
|
||||||
<div data-ng-include data-src="'partials/menu.html'"></div>
|
|
||||||
|
|
||||||
<div data-ng-view id="view" data-ng-hide="httpProviderError"></div>
|
|
||||||
|
|
||||||
<div id="httpProviderError" data-ng-show="httpProviderError">
|
|
||||||
<button class="btn btn-danger" data-ng-click="httpProviderError=null">
|
|
||||||
<strong>Error</strong> {{httpProviderError}}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="loading">
|
|
||||||
<i class="icon-spinner icon-spin"></i> Loading...
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
-->
|
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -160,13 +160,19 @@ module.config(function($httpProvider) {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
module.factory('errorInterceptor', function($q, $window, $rootScope, $location) {
|
module.factory('errorInterceptor', function($q, $window, $rootScope, $location, Auth) {
|
||||||
return function(promise) {
|
return function(promise) {
|
||||||
return promise.then(function(response) {
|
return promise.then(function(response) {
|
||||||
$rootScope.httpProviderError = null;
|
$rootScope.httpProviderError = null;
|
||||||
return response;
|
return response;
|
||||||
}, function(response) {
|
}, function(response) {
|
||||||
|
if (response.status == 401) {
|
||||||
|
console.log('session timeout?');
|
||||||
|
Auth.loggedIn = false;
|
||||||
|
$location.url('/');
|
||||||
|
} else {
|
||||||
$rootScope.httpProviderError = response.status;
|
$rootScope.httpProviderError = response.status;
|
||||||
|
}
|
||||||
return $q.reject(response);
|
return $q.reject(response);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -191,6 +197,7 @@ module.factory('spinnerInterceptor', function($q, $window, $rootScope, $location
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
module.directive('kcInput', function() {
|
module.directive('kcInput', function() {
|
||||||
var d = {
|
var d = {
|
||||||
scope : true,
|
scope : true,
|
||||||
|
@ -235,6 +242,7 @@ module.directive('kcEnter', function() {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
module.filter('remove', function() {
|
module.filter('remove', function() {
|
||||||
return function(input, remove, attribute) {
|
return function(input, remove, attribute) {
|
||||||
|
|
|
@ -11,6 +11,13 @@ module.controller('GlobalCtrl', function($scope, $http, Auth, Current, $location
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.auth = Auth;
|
$scope.auth = Auth;
|
||||||
|
$http.get('/auth-server/rest/saas/whoami').success(function(data, status) {
|
||||||
|
Auth.user = data;
|
||||||
|
Auth.loggedIn = true;
|
||||||
|
})
|
||||||
|
.error(function(data, status) {
|
||||||
|
Auth.loggedIn = false;
|
||||||
|
});
|
||||||
|
|
||||||
$scope.$watch(function() {
|
$scope.$watch(function() {
|
||||||
return $location.path();
|
return $location.path();
|
||||||
|
@ -34,11 +41,11 @@ module.controller('GlobalCtrl', function($scope, $http, Auth, Current, $location
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showrealm) {
|
if (showrealm) {
|
||||||
console.log('redirecting');
|
console.log('default redirect to realm: ' + id);
|
||||||
Current.realm = Current.realms[id];
|
Current.realm = Current.realms[id];
|
||||||
$location.url("/realms/" + id);
|
$location.url("/realms/" + id);
|
||||||
} else {
|
} else {
|
||||||
console.log('not redirecting');
|
//console.log('not redirecting');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,32 +2,14 @@
|
||||||
|
|
||||||
var module = angular.module('keycloak.services', [ 'ngResource' ]);
|
var module = angular.module('keycloak.services', [ 'ngResource' ]);
|
||||||
|
|
||||||
module.service('Auth', function($resource, $http, $location, $routeParams) {
|
module.service('Auth', function() {
|
||||||
var auth = {
|
var auth = {
|
||||||
loggedIn : keycloakCookieLoggedIn
|
loggedIn : false
|
||||||
};
|
};
|
||||||
auth.user = {
|
auth.user = {
|
||||||
userId : null,
|
userId : null,
|
||||||
displayName : null
|
displayName : null
|
||||||
};
|
};
|
||||||
auth.logout = function() {
|
|
||||||
auth.user = {
|
|
||||||
userId : null,
|
|
||||||
displayName : null
|
|
||||||
};
|
|
||||||
auth.loggedIn = false;
|
|
||||||
$http.get('/auth-server/rest/saas/logout-cookie');
|
|
||||||
};
|
|
||||||
if (!auth.loggedIn) {
|
|
||||||
return auth;
|
|
||||||
}
|
|
||||||
$http.get('/auth-server/rest/saas/whoami').success(function(data, status) {
|
|
||||||
auth.user = data;
|
|
||||||
//alert(data.userId);
|
|
||||||
})
|
|
||||||
.error(function(data, status) {
|
|
||||||
alert("Failed!");
|
|
||||||
});
|
|
||||||
return auth;
|
return auth;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -82,9 +82,11 @@
|
||||||
_startTimer: function(){
|
_startTimer: function(){
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
this.timer = win.setTimeout(function(){
|
if (this.options.pollingInterval > 0) {
|
||||||
|
this.timer = win.setTimeout(function () {
|
||||||
self._keepAlive();
|
self._keepAlive();
|
||||||
}, this.options.pollingInterval * 1000);
|
}, this.options.pollingInterval * 1000);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_stopTimer: function(){
|
_stopTimer: function(){
|
||||||
|
@ -103,6 +105,7 @@
|
||||||
|
|
||||||
// if too many requests failed, abort
|
// if too many requests failed, abort
|
||||||
if( !this.failedRequests ){
|
if( !this.failedRequests ){
|
||||||
|
console.log('aborting...');
|
||||||
this._stopTimer();
|
this._stopTimer();
|
||||||
options.onAbort.call( this.warning[0] );
|
options.onAbort.call( this.warning[0] );
|
||||||
return;
|
return;
|
||||||
|
@ -112,12 +115,14 @@
|
||||||
timeout: options.AJAXTimeout,
|
timeout: options.AJAXTimeout,
|
||||||
url: options.keepAliveURL,
|
url: options.keepAliveURL,
|
||||||
error: function(){
|
error: function(){
|
||||||
|
console.log('failure for keepalive');
|
||||||
self.failedRequests--;
|
self.failedRequests--;
|
||||||
},
|
},
|
||||||
success: function(response){
|
success: function(response){
|
||||||
if($.trim(response) !== options.serverResponseEquals){
|
console.log('success for keepalive');
|
||||||
|
/* if($.trim(response) !== options.serverResponseEquals){
|
||||||
self.failedRequests--;
|
self.failedRequests--;
|
||||||
}
|
}*/
|
||||||
},
|
},
|
||||||
complete: function(){
|
complete: function(){
|
||||||
if( recurse ){
|
if( recurse ){
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package org.keycloak.services.resources;
|
package org.keycloak.services.resources;
|
||||||
|
|
||||||
|
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||||
import org.jboss.resteasy.logging.Logger;
|
import org.jboss.resteasy.logging.Logger;
|
||||||
import org.jboss.resteasy.spi.HttpRequest;
|
import org.jboss.resteasy.spi.HttpRequest;
|
||||||
import org.jboss.resteasy.spi.HttpResponse;
|
import org.jboss.resteasy.spi.HttpResponse;
|
||||||
|
@ -11,27 +12,12 @@ import org.keycloak.services.managers.AuthenticationManager;
|
||||||
import org.keycloak.services.managers.RealmManager;
|
import org.keycloak.services.managers.RealmManager;
|
||||||
import org.keycloak.services.models.RealmModel;
|
import org.keycloak.services.models.RealmModel;
|
||||||
import org.keycloak.services.models.RoleModel;
|
import org.keycloak.services.models.RoleModel;
|
||||||
import org.keycloak.services.models.UserModel;
|
|
||||||
import org.keycloak.services.models.UserCredentialModel;
|
import org.keycloak.services.models.UserCredentialModel;
|
||||||
|
import org.keycloak.services.models.UserModel;
|
||||||
import org.keycloak.services.resources.admin.RealmsAdminResource;
|
import org.keycloak.services.resources.admin.RealmsAdminResource;
|
||||||
|
|
||||||
import javax.ws.rs.Consumes;
|
import javax.ws.rs.*;
|
||||||
import javax.ws.rs.ForbiddenException;
|
import javax.ws.rs.core.*;
|
||||||
import javax.ws.rs.FormParam;
|
|
||||||
import javax.ws.rs.GET;
|
|
||||||
import javax.ws.rs.NotAuthorizedException;
|
|
||||||
import javax.ws.rs.NotFoundException;
|
|
||||||
import javax.ws.rs.POST;
|
|
||||||
import javax.ws.rs.Path;
|
|
||||||
import javax.ws.rs.Produces;
|
|
||||||
import javax.ws.rs.core.Context;
|
|
||||||
import javax.ws.rs.core.HttpHeaders;
|
|
||||||
import javax.ws.rs.core.MediaType;
|
|
||||||
import javax.ws.rs.core.MultivaluedMap;
|
|
||||||
import javax.ws.rs.core.NewCookie;
|
|
||||||
import javax.ws.rs.core.Response;
|
|
||||||
import javax.ws.rs.core.UriBuilder;
|
|
||||||
import javax.ws.rs.core.UriInfo;
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -87,22 +73,42 @@ public class SaasService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Path("whoami")
|
@Path("keepalive")
|
||||||
@GET
|
@GET
|
||||||
@Produces("application/json")
|
@NoCache
|
||||||
public Response whoAmI(final @Context HttpHeaders headers) {
|
public Response keepalive(final @Context HttpHeaders headers) {
|
||||||
|
logger.info("keepalive");
|
||||||
return new Transaction() {
|
return new Transaction() {
|
||||||
@Override
|
@Override
|
||||||
public Response callImpl() {
|
public Response callImpl() {
|
||||||
logger.info("WHOAMI start.");
|
|
||||||
RealmManager realmManager = new RealmManager(session);
|
RealmManager realmManager = new RealmManager(session);
|
||||||
RealmModel realm = realmManager.defaultRealm();
|
RealmModel realm = realmManager.defaultRealm();
|
||||||
if (realm == null) throw new NotFoundException();
|
if (realm == null) throw new NotFoundException();
|
||||||
UserModel user = authManager.authenticateSaasIdentityCookie(realm, uriInfo, headers);
|
UserModel user = authManager.authenticateSaasIdentityCookie(realm, uriInfo, headers);
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
return Response.status(404).build();
|
return Response.status(401).build();
|
||||||
|
}
|
||||||
|
NewCookie refreshCookie = authManager.createSaasIdentityCookie(realm, user, uriInfo);
|
||||||
|
return Response.noContent().cookie(refreshCookie).build();
|
||||||
|
}
|
||||||
|
}.call();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Path("whoami")
|
||||||
|
@GET
|
||||||
|
@Produces("application/json")
|
||||||
|
@NoCache
|
||||||
|
public Response whoAmI(final @Context HttpHeaders headers) {
|
||||||
|
return new Transaction() {
|
||||||
|
@Override
|
||||||
|
public Response callImpl() {
|
||||||
|
RealmManager realmManager = new RealmManager(session);
|
||||||
|
RealmModel realm = realmManager.defaultRealm();
|
||||||
|
if (realm == null) throw new NotFoundException();
|
||||||
|
UserModel user = authManager.authenticateSaasIdentityCookie(realm, uriInfo, headers);
|
||||||
|
if (user == null) {
|
||||||
|
return Response.status(401).build();
|
||||||
}
|
}
|
||||||
logger.info("WHOAMI: " + user.getLoginName());
|
|
||||||
return Response.ok(new WhoAmI(user.getLoginName(), user.getLoginName())).build();
|
return Response.ok(new WhoAmI(user.getLoginName(), user.getLoginName())).build();
|
||||||
}
|
}
|
||||||
}.call();
|
}.call();
|
||||||
|
@ -111,6 +117,7 @@ public class SaasService {
|
||||||
@Path("isLoggedIn.js")
|
@Path("isLoggedIn.js")
|
||||||
@GET
|
@GET
|
||||||
@Produces("application/javascript")
|
@Produces("application/javascript")
|
||||||
|
@NoCache
|
||||||
public String isLoggedIn(final @Context HttpHeaders headers) {
|
public String isLoggedIn(final @Context HttpHeaders headers) {
|
||||||
return new Transaction() {
|
return new Transaction() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -165,6 +172,7 @@ public class SaasService {
|
||||||
|
|
||||||
@Path("loginPage.html")
|
@Path("loginPage.html")
|
||||||
@GET
|
@GET
|
||||||
|
@NoCache
|
||||||
public void loginPage() {
|
public void loginPage() {
|
||||||
new Transaction() {
|
new Transaction() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -179,6 +187,7 @@ public class SaasService {
|
||||||
|
|
||||||
@Path("logout")
|
@Path("logout")
|
||||||
@GET
|
@GET
|
||||||
|
@NoCache
|
||||||
public void logout() {
|
public void logout() {
|
||||||
new Transaction() {
|
new Transaction() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -194,6 +203,7 @@ public class SaasService {
|
||||||
|
|
||||||
@Path("logout-cookie")
|
@Path("logout-cookie")
|
||||||
@GET
|
@GET
|
||||||
|
@NoCache
|
||||||
public void logoutCookie() {
|
public void logoutCookie() {
|
||||||
logger.info("*** logoutCookie");
|
logger.info("*** logoutCookie");
|
||||||
new Transaction() {
|
new Transaction() {
|
||||||
|
@ -205,6 +215,7 @@ public class SaasService {
|
||||||
}
|
}
|
||||||
|
|
||||||
public final static String loginFormPath = "/sdk/login.xhtml";
|
public final static String loginFormPath = "/sdk/login.xhtml";
|
||||||
|
|
||||||
protected void forwardToLoginForm(RealmModel realm) {
|
protected void forwardToLoginForm(RealmModel realm) {
|
||||||
request.setAttribute(RealmModel.class.getName(), realm);
|
request.setAttribute(RealmModel.class.getName(), realm);
|
||||||
URI action = uriInfo.getBaseUriBuilder().path(SaasService.class).path(SaasService.class, "processLogin").build();
|
URI action = uriInfo.getBaseUriBuilder().path(SaasService.class).path(SaasService.class, "processLogin").build();
|
||||||
|
@ -216,7 +227,6 @@ public class SaasService {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Path("login")
|
@Path("login")
|
||||||
@POST
|
@POST
|
||||||
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
||||||
|
|
Loading…
Reference in a new issue