Spring security-не удается выйти из системы


Я перенастроил свое приложение GWT/GXT на базовую LDAP-авторизацию с использованием базовой HTTP-аутентификации. Это хорошо работает, когда я запускаю новый браузер - я получаю приглашение и получаю авторизацию против корпоративного LDAP. Моя проблема - я не могу выйти из системы, пока не закрою/не открою браузер. Я могу отладить и посмотреть, как вызывается SecurityContextLogoutHandler#logout и выполняется следующий код

    if (invalidateHttpSession) {
        HttpSession session = request.getSession(false);
        if (session != null) {
            session.invalidate();
        }
    }

    SecurityContextHolder.clearContext();

Однако это, по-видимому, не имеет никакого эффекта, поскольку сайт перезагружается, и я никогда не получаю другого запроса http auth, если я не перезапущу браузер (даже очистка кэша / cookies не поможет). Вот соответствующая часть applicationContext.xml

<security:http auto-config='true'>
    <security:intercept-url pattern="/reports/**" access="ROLE_USER" />
    <security:http-basic />
    <security:logout logout-url="/reports/logout" 
              logout-success-url="/reports/Application.html" />       
</security:http>

Я пытался определить custom LogoutSuccessHandler и do authentication.setAuthenticated(false);, но это также не имеет никакого эффекта

Здесь есть что-нибудь, чего мне не хватает? Ваша помощь будет очень признательна

6 14

6 ответов:

Хорошо. проведя слишком много времени с этим, я думаю, что у меня есть ответ. Это просто - нельзя отказаться от базовой аутентификации HTTP с помощью серверной технологии. В основном строка авторизации декодируется base-64 в заголовке HTTP, и когда защищенная страница загружается в браузер, маркер безопасности повторно заполняется, поэтому независимо от того, как часто вы стираете его на сервере, он воскрешается каждый раз, когда вызывается страница. Я полагаю, что можно сыграть несколько хитрых трюков с браузером сторона, но это было бы хрупко и ненадежно

В моем случае я буду переключаться на аутентификацию на основе формы, которая в любом случае дает гораздо лучший контроль над процессом входа/выхода.

Я буду продолжать принимать свой собственный ответ в пользу того, кто выйдет с приемлемым решением

Бостон в основном прав, что вы не можете обрабатывать эту серверную сторону. Есть, однако, немного Хака, который доступен .

Короткий ответ состоит в том, чтобы перенаправить пользователя на URL, который вы хотите, чтобы он приземлился, но префикс его с плохим именем пользователя, за которым следует @. Так как уродливые примеру,
<security:logout logout-url="/reports/logout" 
    logout-success-url="http://BADNAME@localhost/reports/Application.html" />

В идеале вы бы реализовали обработчик, который сделал бы это за вас, но для иллюстрации я думаю, что это дает представление о концепции.

Угу... странный. Я не вижу ничего плохого в вашей конфигурации. Вам действительно не нужно определять какой-либо пользовательский обработчик выхода, потому что он должен обрабатываться Spring Security.

Попробуйте это, вместо определения вашего logout-url, используйте ссылку выхода по умолчанию: -

...
<security:logout logout-success-url="/reports/Application.html" />      
...

Затем используйте /j_spring_security_logout для выхода из системы.

Работает ли это вообще?

<logout invalidate-session="true" logout-url="/reports/logout"
        logout-success-url="/reports/Application.html" />

Просто установите invalidate session в 'true' и посмотрите, работает ли он.

Проверьте, есть ли этот Листнер в вашем интернете.xml:

<!-- Security: to listen session requests -->
<listener>
     <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
 </listener>

Если вышеперечисленные вещи не работают, то вы можете попробовать следующее решение. Я думаю, проблема в вашей следующей строке,

<security:intercept-url pattern="/reports/**" access="ROLE_USER" />

Вышеприведенная строка означает, что вы добавили ссылку access ROLE_USER на yout '/reports/**'. Но после выхода из системы вы даете тот же url-адрес для успешного выхода из системы. тогда как это будет работать? Либо измените расположение страницы успешного выхода из системы, либо вы можете добавить следующую строку в тег http.

<intercept-url pattern="/reports/Application.html" filters="none" />

Я твердо верю, что вы правы. Вы не можете аннулировать базовую или дайджест-аутентификацию, не делая этого в браузере. Даже там это будет чрезвычайно трудно, если не невозможно. Честно говоря, я даже не могу придумать, как это сделать (кроме попытки закрыть браузер).

Как вы уже отмечали, проблема заключается в том, что, несмотря на то, что вы аннулируете все на стороне сервера, вы дали браузеру все, что ему нужно для автоматической генерации нового сеанс аутентификации сам по себе. Единственный способ очистить это-очистить браузер. Даже перезапуск вашего сервера не отменит возможность браузера создавать новую аутентификацию, что является довольно хорошим признаком того, что вы не сможете выполнить это на стороне сервера без аннулирования контракта на базовую или дайджест-аутентификацию.

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

Аутентификация формы может быть вашим лучшим выбором, потому что там вы не даете браузеру информацию о том, как воскресить аутентификацию, если вы не разрешите файл cookie rememberme. Даже там, вы можете очистить куки при выходе из системы, так что это проще.

Один из способов, который я нашел для этого, - это возврат ответов HTTP 401. Некоторые быстрые тесты показывают, что это работает в версиях Safari, Chrome и Firefox для macOS. Код, который я использую, по существу эквивалентен этому:

@RequestMapping(value="/logout",method=RequestMethod.POST)
public void logout(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
    session.setAttribute(LOGOUT_SESSION_KEY, true);
    response.setStatus(303);
    response.addHeader("Location", URL_OF_APPLICATION_HOME_PAGE);
}

@RequestMapping("/")
public void home(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
    if (Boolean.TRUE.equals(session.getAttribute(LOGOUT_SESSION_KEY))) {
        if (request.getHeader("Authorization") != null) {
            session.invalidate();
        }

        response.setStatus(401);
        response.addHeader("WWW-Authenticate", "Basic realm=\"" + HTTP_BASIC_REALM + "\"");
        return;
    }

    /* Generate home page */
}

Хитрость заключается в том, что обычно вам нужно вернуть заголовок 401 дважды - после первого, браузер повторит попытку с его кэшированными учетными данными (отправив заголовок" Authorization", в котором мы уничтожаем старую сессию пользователя), второй, кажется, очистит их и запрашивает вход в систему.