Spring безопасность - невозможно выйти
Я обновил приложение GWT/GXT с помощью базовой авторизации LDAP, используя базовую HTTP-аутентификацию. Он работает хорошо, когда я запускаю новый браузер - я получаю приглашение и получаю разрешение на корпоративный LDAP. Моя проблема. Я не могу выйти из системы, если я не закрою/не открою браузер. Я могу отлаживать и видеть, как вызывается SecurityContextLogoutHandler#logout
, и выполняется следующий код
if (invalidateHttpSession) {
HttpSession session = request.getSession(false);
if (session != null) {
session.invalidate();
}
}
SecurityContextHolder.clearContext();
Однако он, похоже, не имеет никакого эффекта, поскольку сайт перезагружается, и я никогда не получаю другое приглашение HTTP auth, если я не перезапущу браузер (даже очистка кэша/файлов cookie не поможет). Здесь соответствующая часть 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>
Я попытался определить пользовательский LogoutSuccessHandler
и сделать authentication.setAuthenticated(false);
, но это также не имеет эффекта
Что-нибудь здесь здесь отсутствует? Ваша помощь будет высоко оценена.
Ответы
Ответ 1
OK. Проведя слишком много времени, я думаю, что у меня есть ответ. Это просто - невозможно выполнить базовую HTTP-аутентификацию с использованием серверной технологии. В основном строка авторизации base-64 декодируется в HTTP-заголовке, и когда защищенная страница загружается в браузер, токен безопасности перезаписывается, поэтому независимо от того, как часто вы удаляете его на сервере, он получает воскрешение при каждом вызове страницы. Я полагаю, что можно использовать некоторые умные трюки на стороне браузера, но это было бы хрупким и ненадежным.
В моем случае я перейду на проверку подлинности на основе форм, которая в любом случае значительно улучшит управление процессом входа/выхода.
Я буду придерживаться своего собственного ответа в пользу того, кто выходит с приемлемым решением
Ответ 2
Бостон в основном прав, что вы не можете справиться с этой серверной стороной. Существует, однако, немного взлома, доступного.
Короткий ответ заключается в перенаправлении пользователя на URL-адрес, на который вы хотите приземлиться, но префикс его с неправильным именем пользователя, за которым следует @
. Итак, как уродливый пример,
<security:logout logout-url="/reports/logout"
logout-success-url="http://[email protected]/reports/Application.html" />
В идеале вы должны реализовать обработчик, который будет делать это для вас, но для иллюстрации я думаю, что он получает концепцию.
Ответ 3
Э-э... странно. Я не вижу ничего плохого в вашей конфигурации. Вам действительно не нужно определять какой-либо пользовательский обработчик, поскольку он должен обрабатываться Spring Security.
Попробуйте это, вместо определения logout-url
используйте ссылку для выхода по умолчанию: -
...
<security:logout logout-success-url="/reports/Application.html" />
...
Затем используйте /j_spring_security_logout
для выхода из системы.
Это вообще работает?
Ответ 4
<logout invalidate-session="true" logout-url="/reports/logout"
logout-success-url="/reports/Application.html" />
просто установите недействительный сеанс на "true" и посмотрите, работает ли он.
проверьте, есть ли этот список в вашем web.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" />
Над строкой означает, что вы добавили доступ ROLE_USER к ссылке yout '/reports/**'.
Но после выхода из системы вы даете тот же URL-адрес для выхода из системы. то как это будет работать?
Либо измените местоположение страницы успеха выхода, либо вы можете добавить строку followin в тег http.
<intercept-url pattern="/reports/Application.html" filters="none" />
Ответ 5
Я твердо верю, что вы правы. Вы не можете аннулировать аутентификацию Basic или Digest без ее использования в браузере. Даже там это будет чрезвычайно сложно, если не невозможно. Честно говоря, я даже не могу придумать, как это сделать (кроме попытки закрыть браузер).
Как вы отметили, проблема в том, что даже если вы отменили все действия на стороне сервера, вы предоставили браузеру все, что ему нужно, чтобы автоматически генерировать новый сеанс аутентификации самостоятельно. Единственный способ очистить это - очистить браузер. Даже перезапуск вашего сервера не будет отключать возможность браузера создавать новую аутентификацию, что является довольно хорошим показателем того, что вы не сможете выполнить это на стороне сервера, не нарушив контракт на аутентификацию Basic или Digest.
Откровенно говоря, это похоже на довольно существенное отверстие для безопасности в браузерах, поскольку вы никогда не должны оставлять браузер, работающий на сервере открытого доступа, после аутентификации на сайте с базовой или дайджест-аутентификацией.
Проверка подлинности на форме может быть лучшим выбором, потому что вы не предоставляете браузеру информацию о том, как воскресить аутентификацию, если вы не разрешите куки файл rememberme. Даже там, вы можете очистить файл cookie при выходе из системы, чтобы это было проще.
Ответ 6
Один из способов, который я нашел, - вернуть HTTP 401 ответы. Некоторые быстрые тесты показывают, что это работает в версиях MacOS Safari, Chrome и Firefox. Код, который я использую, по существу эквивалентен этому:
@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 - после первого браузера он будет повторять свои кешированные учетные данные (отправляет заголовок "Авторизация", после чего мы уничтожаем старый пользовательский сеанс), второй, похоже, очищает их и запрашивает логин.