Пристань единого входа с несколькими экземплярами WebAppContext

У меня есть встроенный причальный сервер, который просматривает список веб-приложений (список варьируется в зависимости от развертывания) из разных мест. Я пытаюсь перейти от базовой проверки подлинности к проверке подлинности с помощью формы.

Я бы хотел сделать что-то вроде:

// create constraint
Constraint usersOnly = new Constraint(Constraint.__FORM_AUTH, "user");
usersOnly.setAuthenticate(true);
ConstraintMapping requireAuthentication = new ConstraintMapping();
requireAuthentication.setConstraint(usersOnly);
requireAuthentication.setPathSpec("/*");

// create login service
LoginService loginService = new HashLoginService("realm");
loginService.setConfig("users.txt");

// create form authentication
FormAuthenticator formAuthenticator = new FormAuthenticator("/login", "/login", true);

// create /login route
ServletHolder loginServlet = new ServletHolder(new DefaultServlet() {
@Override
  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.getWriter().append("<html>\n<head>\n<title>Login</title>\n</head>\n<body>\n"
    + "<form method='POST' action='/j_security_check'>\n"
    + "<input type='text' name='j_username'/>\n"
    + "<input type='password' name='j_password'/>\n"
    + "<input type='submit' value='Login'/>\n</form>\n</body>\n</html>\n");
  }
});

ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler();
securityHandler.addMapping(requireAuthentication);
securityHandler.setLoginService(loginService);
securityHandler.setAuthenticator(formAuthenticator);

// assign security to each webapp
for (WebAppContext webapp : webapps) {
  webapp.setSecurityHandler(securityHandler);
  webapp.addServlet(loginServlet, "/login");
}

Это работает должным образом, если в веб-приложениях есть только одно веб-приложение, но если их несколько, вам будет предлагаться войти в систему каждый раз, когда вы переходите по ссылке из одного веб-приложения в другое, и каждый раз при повторной аутентификации вы перенаправляетесь на базовое веб-приложение по адресу маршрут "/" и аутентифицированы только для этого.

Я хочу, чтобы мои контексты обменивались сеансами.

Согласно этим вопросом, имея общий SessionManager для каждого экземпляра WebAppContext должен решить проблему, но у запрашивающего был только один экземпляр WebAppContext. Если я попытаюсь назначить один и тот же экземпляр SessionManager каждому WebAppContext, я получу NPE.

Я также видел, что некоторые ресурсы указывают на установку пути к SessionCookieConfig каждого контекста к общему пути контекста, а для useRequestedId - на true для каждого SessionManager WebAppContext, но это решение предназначено для org.mortbay.jetty и устарело.

Если у вас есть какое-либо понимание или опыт настройки единого входа для встроенного сервера пристаней с несколькими WebAppContexts, или если вы можете придумать лучший способ обслуживания нескольких отдельных веб-приложений на одном общем сервере, укажите мне правильное направление.

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

Дайте мне знать, если я не понимаю, или у вас есть вопросы.


person Ben Vachon    schedule 23.03.2016    source источник
comment
Вы спрашиваете, как расширить безопасность на основе контейнера Jetty (JAAS разновидности сообщений формы) на несколько приложений, работающих на сервере Jetty?   -  person Richard    schedule 23.03.2016
comment
Да, особенно с системой единого входа.   -  person Ben Vachon    schedule 23.03.2016
comment
Хорошо .. У меня там нет опыта. Но если все остальное не помогло, вы могли бы обеспечить безопасность «в приложении», а не «в контейнере». Использование таких вещей, как фильтры и т. Д. Существует множество доступных фильтров безопасности, пружинных элементов безопасности и т. Д., Которые сделают это за вас. Конкретно ли контейнер отправляет пользователя сервлету формы, а после этого ваш пользователь не перенаправляется на исходный запрошенный URL?   -  person Richard    schedule 24.03.2016


Ответы (1)


Мое решение заключалось в том, чтобы расширить класс HashSessionManager, чтобы он запрашивал SessionIdManager перед созданием нового сеанса. В результате экземпляры CrossContextSessionManager под одним и тем же SessionIdManager все совместно используют содержимое сеанса, а не только идентификаторы сеанса. Следовательно, вход в одно веб-приложение означает вход во все.

import java.util.Collection;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.eclipse.jetty.server.session.AbstractSession;
import org.eclipse.jetty.server.session.HashSessionIdManager;
import org.eclipse.jetty.server.session.HashSessionManager;

/**
 * Allows the WebAppContext to check the server's SessionIdManager before creating a new session
 * so that WebAppContext can share session contents for each client rather than just session ids.
 */
public class CrossContextSessionManager extends HashSessionManager {

  // Number of seconds before the user is automatically logged out of an idle webapp session
  private int defaultSessionTimeout = 1800;

  /**
   * Check for an existing session in the session id manager by the requested id.
   * If no session has that id, create a new HttpSession for the request.
   */
  @Override
  public HttpSession newHttpSession(HttpServletRequest request) {
    AbstractSession session = null;

    String requestedId = request.getRequestedSessionId();
    if (requestedId != null) {
      String clusterId = getSessionIdManager().getClusterId(requestedId);
      Collection<HttpSession> sessions = ((HashSessionIdManager) getSessionIdManager()).getSession(clusterId);
      for (HttpSession httpSession : sessions) {
        session = (AbstractSession) httpSession;
        break;
      }
    }

    if (session == null) {
      session = newSession(request);
      session.setMaxInactiveInterval(defaultSessionTimeout);
      addSession(session,true);
    }

    return session;
  }
}

Если запрос уже содержит идентификатор, newSessionId просто извлечет из него этот идентификатор. В противном случае он создаст новый идентификатор, уникальный из всех существующих идентификаторов.

person Ben Vachon    schedule 28.03.2016
comment
Как и где инициализировать этот CrossContextSessionManager? Мы используем контейнер Equinox OSGI, который запускает встроенный Jetty. Поэтому мне интересно, где я могу подключиться к процессу запуска, чтобы передать этот CrossContextSessionManager. Не могли бы вы поделиться некоторыми фрагментами кода, показывающими, как вы это инициализируете? - person Christoph; 06.07.2016
comment
для (WebAppContext webapp: webapps) {CrossContextSessionManager sessionManager = new CrossContextSessionManager (); SessionHandler sessionHandler = новый SessionHandler (sessionManager); webapp.setSessionHandler (sessionHandler); } - person Ben Vachon; 07.07.2016