Java – single sign on jetty with multiple webappcontext instances
I have an embedded jetty server that iterates over a list of webapps from many different locations (between different deployments) I'm trying to switch from basic authentication to form authentication
What I want to do is:
// 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"); }
If there is only one webapp in webapps, it can work normally, but if there are multiple webapps, you will be prompted to log in every time you track the link from one webapp to another, and will be redirected to the underlying webapp every time you re authenticate Route '/' and authenticate only for that route
I want my background to share the conversation
According to this question, configuring a public SessionManager for each webappcontext instance should solve the problem, but asker has only one webappcontext instance If I try to assign the same SessionManager instance to each webappcontext, I will get NPE
I also see that some resources point to setting the path of sessioncookeieconfig of each context as the public context path and setting userequestedd as true of SessionManager of each webappcontext, but this solution is applicable to org mortbay. Jetty and out of date
If you have any insights or experience in setting up SSO for embedded jetty servers with multiple webappcontexts, or if you can think of a better way to use a common server to provide multiple different web applications, please point out my right direction
How to allow users to authenticate all web applications processed by one server by filling out a single form?
If I don't know or you have any questions, please let me know
Solution
My solution is to extend the hashsessionmanager class to query sessionidmanager before creating a new session The result is that the crosscontextsessionmanager instance under the same sessionidmanager shares the session content, not just the session ID. therefore, logging in to a webapp means logging in all
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; } }
If the request already carries an ID, newsessionid will only extract the ID. otherwise, it will create a unique new ID from all existing IDs