Как удалить серверный сеанс HttpOnly ie при закрытии окна браузера - PullRequest
1 голос
/ 10 января 2020

У меня есть требование закрыть сеанс, который проводит JSESSIONID cook ie, когда пользователь закрывает окно браузера. Spring Cook: этот повар ie устанавливается на стороне сервера:

@Configuration
@EnableWebSecurity
public class SpringSecurityConfiguration {

    @Configuration
    public static class ApplicationApiSecurityConfiguration extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(final HttpSecurity http) {
        http
                    .antMatcher("/**")
          .csrf()
          .disable() // Disable Cross Site Request Forgery Protection
          .formLogin() // Enable form based/cookie based/session based login
          .loginPage(LOGIN_PAGE) // Set our custom login page
          .and()
            .authorizeRequests()
            .antMatchers("/login", "/logout")
            .permitAll() // Allow anyone to access the login and logout page
            .anyRequest()
            .authenticated() //All other request require authentication
          .and()
            .logout()
            .deleteCookies("JSESSIONID") // Delete JSESSIONID cookie on logout
            .clearAuthentication(true) // Clean authentication on logout
            .invalidateHttpSession(true); // Invalidate Http Session on logout
        }
    }
}

На стороне клиента у меня есть AngularJS 1.7 и TypeScript, и я уже перехватываю событие beforeunload, установив up @module.ts

module app.myapp {

    import IModule = angular.IModule;

    import IStateProvider = angular.ui.IStateProvider;
    import IUrlRouterProvider = angular.ui.IUrlRouterProvider;

    import LockService = lib.common.LockService;

    export class MyApp {

        static NAME = 'app.myapp';

        static module(): IModule {
            return angular.module(MyApp.NAME);
        }
    }

    angular.module(MyApp.NAME, ['ui.router'])
        .config([
            '$stateProvider', '$urlRouterProvider',
            ($stateProvider: IStateProvider, $urlRouterProvider: IUrlRouterProvider) => {

                $urlRouterProvider.when('', '/home');

                $stateProvider.state('base', {
                    url: '',
                    abstract: true,
                    template: '<ui-view/>',
                    resolve: {
                        initEventListenerForGlobalUnlock: [
                            LockService.ID,
                            (lockService: LockService) => lockService.initListenerForGlobalUnlock()
                        ]
                    }
                });

                $stateProvider.state('personalProfile', {
                    url: '/profile',
                    parent: 'base',
                    component: PersonalProfile.ID
                });
            }
        ]);
}

и затем для моей реализации LockService:

module lib.common {

    import IWindowService = angular.IWindowService;
    import IRootScopeService = angular.IRootScopeService;

    export class LockService {

        static ID = 'lockService';
        static $inject: string[] = ['$window', '$rootScope'];

        constructor(
            private readonly $window: IWindowService,
            private readonly $rootScope: IRootScopeService
        ) { }

        private readonly globalUnlockHandler = (ev: BeforeUnloadEvent) => {
            // This does not work with HttpOnly cookies
            document.cookie = 'JSESSIONID=;path=/;domain=' + window.location.hostname + ';expires=Thu, 01 Jan 1970 00:00:01 GMT';
            this.$rootScope.$digest();
            return null;
        }

        /**
         * This is called upon app initialisation,
         * an event listener for browser close is registered,
         * which will use an API call to unlock all locked sections before exiting
         */
        initListenerForGlobalUnlock(): void {
            this.$window.addEventListener('beforeunload', this.globalUnlockHandler);
        }
    }

    LibCommon.module().service(LockService.ID, LockService);
}

В настоящее время существует функция выхода из системы, основанная на перенаправлении страницы с помощью this.$window.location.href = 'logout'

Но я хочу удалить сессионный повар ie (или как-то лишить законной силы сеанс), когда окно браузера закрыто, нажав [x] , чтобы даже если пользователь вернулся на той же странице, вставив URL-адрес в адресную строку, и его просят снова войти в систему.

Проблема в том, что JSESSIONID cook ie имеет значение HttpOnly и поэтому не может быть удалено из * 1028. *. И я не знаю, как сказать Spring Security на стороне сервера, чтобы сделать сеанс недействительным.

1 Ответ

0 голосов
/ 12 января 2020

Отвечая на мой собственный вопрос.

Единственный способ, которым я нашел достижение истечения сеанса при закрытии окна браузера, - это выполнить синхронный XHR-запрос к URL-адресу выхода из системы в обработчике события globalUnlockHandler onBeforeUnload.

AngularJS $http.get, вероятно, не работает, потому что он асинхронный и вызов отменяется до его завершения.

Вот пример TypeScript для создания устаревшего кросс-браузерного объекта XHR:

createXMLHttpRequest(): XMLHttpRequest {
    var req: XMLHttpRequest = null;
    if (this.$window.XMLHttpRequest) {
        try {
            req = new XMLHttpRequest();
        } catch (e) { }
    } else if (this.$window.ActiveXObject) {
        try {
            req = new ActiveXObject('Msxml2.XMLHTTP'); // tslint:disable-line
        } catch (e) {
            try {
                req = new ActiveXObject('Microsoft.XMLHTTP'); // tslint:disable-line
            } catch (e) { }
        }
    } // fi
    if (!req) {
        console.warn('createXMLHttpRequest() failed');
    }
    return req;
}

Затем для совершения вызова:

var xhr = new XMLHttpRequest();
if (xhr) {
    xhr.open('GET', 'http://www.domain.name/logout', false);
    try {
        xhr.send(null);
    } catch (e) {
        console.warn(e);
    }
}

Обратите внимание, что XHR устарел и что этот подход противоречит принципам HTTP, поскольку пользователь может открыть новую вкладку браузера и скопировать / вставить URL из существующего. Затем, если первая вкладка закрыта, сеанс будет прерван, но вторая вкладка все равно будет открыта без действительного сеанса.

...