Для наиболее безопасной передачи конфиденциальных данных по сети, в веб-приложениях, используется HTTPS-соединение. HTTPS-соединение лучше использовать не на весь пользовательский сеанс, а лишь на определенных этапах где защищенность канала действительно очень важна.
К примеру, если сделать на HTTPS-соединении только логин пользователя, а после его входа в систему снова использовать обычный HTTP, то на web-сервере Tomcat придется реализовать поддержку корректного переключения с HTTPS на HTTP.
Конфигурация Spring Security для такого случая:
<security:http auto-config="true" >
<security:intercept-url pattern="/login.html" access="IS_AUTHENTICATED_ANONYMOUSLY"
requires-channel="https"/>
<security:intercept-url pattern="/billing.html" access="ROLE_USER"
requires-channel="http"/>
</security:http>
Возможны два варианта переключения соединений:
В первом случае все будет работать, т.к. Tomcat создаст cookie сперва для HTTP сессии, которая будет действительна и для HTTPS, а следовательно может быть аутентифицирована Spring Security и использована дальше в работе с обычным HTTP.
Во втором случае Tomcat создаст cookie для HTTPS с параметром secure, а это значит что аутентификация такой сессии не позволит использовать в дальнейшем обычную HTTP сессию. Сookie с параметром secure привязана лишь к HTTPS-сессиям. Для пользователя же это обернется редиректом Spring Security на страницу логина, но с уже созданной cookie, и только второй логин уже пустит пользователя в систему. Чтобы решить эту проблему необходимо, находясь в HTTPS сессии, создать cookie для обычной HTTP сессии без параметра secure. С помощью фильтра это реализовать довольно просто:
package com.blogspot.nkoksharov.filter;
import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.commons.lang.StringUtils;
public class HttpsCookieFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
final HttpServletRequest httpRequest = (HttpServletRequest) request;
final HttpServletResponse httpResponse = (HttpServletResponse) response;
final HttpSession session = httpRequest.getSession(false);
if (request.isSecure() && session != null) {
final Cookie sessionCookie = new Cookie("JSESSIONID", session.getId());
sessionCookie.setMaxAge(-1);
sessionCookie.setSecure(false);
String contextPath = httpRequest.getContextPath();
if (StringUtils.isNotBlank(contextPath)) {
sessionCookie.setPath(contextPath);
} else {
sessionCookie.setPath("/");
}
httpResponse.addCookie(sessionCookie);
}
chain.doFilter(request, response);
}
...
}
Теперь переход с https-соединения будет выглядеть так:
Фильтр лучше всего поместить в самом начале цепочки фильтров, важно чтобы он был перед springSecurityFilterChain
. Следует отметить, что при таком решении http-cookie будет приходить пользователю на каждый https-запрос. Т.к. сложно опеределить при https-соединении создана ли cookie для http-канала.
3 комментария:
Спасибо за заметку, как раз искал информацию по использованию фильтров с cookie и сессиями, и нашел ваш блог. Все понятно и в тему.
Спасибо большущщее!!!! :))) Пол дня протрахался с этим безобразием. Извините за мой французский...
//Даниил
Пост действительно очень хороший. Но здесь было бы неплохо описать ситуацию, когда cookies отключены.
Отправить комментарий