Understanding Session Mechanisms, Distributed Session Challenges, and SSO Solutions with CAS Implementation

This article explains traditional session and authentication mechanisms, the problems of session sharing in clustered and multi‑service environments, and presents practical SSO solutions—including CAS‑based single sign‑on and its differences from OAuth2—accompanied by complete Java Spring code examples.

IT Architects Alliance
IT Architects Alliance
IT Architects Alliance
Understanding Session Mechanisms, Distributed Session Challenges, and SSO Solutions with CAS Implementation

When a company’s product portfolio grows, users must switch between many systems, which leads to poor user experience and high password‑management costs. Implementing a unified authentication (single sign‑on) can greatly improve usability and security.

Traditional Session Mechanism and Authentication – HTTP is stateless, so each request creates a new server thread without user context. To associate requests with a user, a Session is created on the server and identified by a JSESSIONID cookie (or URL rewriting). The cookie is stored in the browser memory; if disabled, the session ID appears in the URL. A session is scoped to a single browser window, so opening another window creates a new session.

Server‑Side Session Process – When a request arrives, the server checks for a JSESSIONID. If found, the corresponding session object is retrieved from memory; otherwise a new session is created and the ID is sent back to the client.

Session Authentication Flow – The flow is illustrated with diagrams (omitted). While this works for a single server, it does not scale to distributed clusters.

Session Challenges in a Cluster – In a load‑balanced environment, successive requests from the same user may be routed to different servers, causing the session not to be found because it resides only on the first server. To solve this, two main approaches are used:

Session replication – copy session data to all nodes (high cost, latency, maintenance difficulty).

Centralized session storage – store sessions in a shared store such as Redis, allowing all servers to read/write the same session data.

Centralized storage avoids synchronization overhead and is the recommended solution.

Multi‑Service Login Challenges and SSO – When many independent systems each require login, users must authenticate repeatedly. Single sign‑on (SSO) lets a user log in once and obtain a ticket that is accepted by all systems.

CAS‑Based SSO Flow – The article describes a CAS implementation:

User accesses system B, is redirected to ouath.com for login.

After successful login, a cookie is set on ouath.com and a ticket (stored in Redis) is generated.

System B receives the ticket, retrieves the session ID from Redis, creates its own cookie, and redirects the user back.

Subsequent accesses to B use the cookie directly.

The complete interaction diagram is shown in the original article. CAS vs OAuth2 – CAS provides centralized authentication for web SSO, while OAuth2 is a third‑party authorization protocol that allows resource access without exposing user credentials. CAS protects client‑side resources; OAuth2 protects server‑side resources. Demo Code Below are the core Java Spring code snippets used in the demo. All code blocks are wrapped in tags as required.</p> <code>public class UserForm implements Serializable { private static final long serialVersionUID = 1L; private String username; private String password; private String backurl; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getBackurl() { return backurl; } public void setBackurl(String backurl) { this.backurl = backurl; } } @Controller public class IndexController { @Autowired RedisTemplate redisTemplate; @GetMapping("/toLogin") public String toLogin(Model model, HttpServletRequest request) { Object userInfo = request.getSession().getAttribute(LoginFilter.USER_INFO); if (userInfo != null) { String ticket = UUID.randomUUID().toString(); redisTemplate.opsForValue().set(ticket, userInfo, 2, TimeUnit.SECONDS); return "redirect:" + request.getParameter("url") + "?ticket=" + ticket; } UserForm user = new UserForm(); user.setUsername("laowang"); user.setPassword("laowang"); user.setBackurl(request.getParameter("url")); model.addAttribute("user", user); return "login"; } @PostMapping("/login") public void login(@ModelAttribute UserForm user, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { request.getSession().setAttribute(LoginFilter.USER_INFO, user); String ticket = UUID.randomUUID().toString(); redisTemplate.opsForValue().set(ticket, user, 20, TimeUnit.SECONDS); if (user.getBackurl() == null || user.getBackurl().length() == 0) { response.sendRedirect("/index"); } else { response.sendRedirect(user.getBackurl() + "?ticket=" + ticket); } } @GetMapping("/index") public ModelAndView index(HttpServletRequest request) { ModelAndView mv = new ModelAndView(); UserForm user = (UserForm) request.getSession().getAttribute(LoginFilter.USER_INFO); mv.setViewName("index"); mv.addObject("user", user); request.getSession().setAttribute("test", "123"); return mv; } } public class LoginFilter implements Filter { public static final String USER_INFO = "user"; @Override public void init(FilterConfig filterConfig) throws ServletException {} @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; Object userInfo = request.getSession().getAttribute(USER_INFO); String requestUrl = request.getServletPath(); if (!"/toLogin".equals(requestUrl) && !requestUrl.startsWith("/login") && userInfo == null) { request.getRequestDispatcher("/toLogin").forward(request, response); return; } filterChain.doFilter(request, response); } @Override public void destroy() {} } @Configuration public class LoginConfig { @Bean public FilterRegistrationBean sessionFilterRegistration() { FilterRegistrationBean registration = new FilterRegistrationBean(); registration.setFilter(new LoginFilter()); registration.addUrlPatterns("/*"); registration.addInitParameter("paramName", "paramValue"); registration.setName("sessionFilter"); registration.setOrder(1); return registration; } } <!-- login.html (Thymeleaf) --> <html xmlns:th="http://www.thymeleaf.org"> <head><title>enjoy login</title></head> <body> <div style="text-align:center"> <h1>请登陆</h1> <form th:action="@{/login}" th:object="${user}" method="post"> <p>用户名: <input type="text" th:field="*{username}"/></p> <p>密码: <input type="text" th:field="*{password}"/></p> <p><input type="submit" value="Submit"/> <input type="reset" value="Reset"/></p> <input type="hidden" th:field="*{backurl}" hidden="hidden"/> </form> </div> </body> </html> public class SSOFilter implements Filter { private RedisTemplate redisTemplate; public static final String USER_INFO = "user"; public SSOFilter(RedisTemplate redisTemplate) { this.redisTemplate = redisTemplate; } @Override public void init(FilterConfig filterConfig) throws ServletException {} @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; Object userInfo = request.getSession().getAttribute(USER_INFO); String requestUrl = request.getServletPath(); if (!"/toLogin".equals(requestUrl) && !requestUrl.startsWith("/login") && userInfo == null) { String ticket = request.getParameter("ticket"); if (ticket != null) { userInfo = redisTemplate.opsForValue().get(ticket); } if (userInfo == null) { response.sendRedirect("http://127.0.0.1:8080/toLogin?url=" + request.getRequestURL().toString()); return; } request.getSession().setAttribute(USER_INFO, (UserForm) userInfo); redisTemplate.delete(ticket); } filterChain.doFilter(request, response); } @Override public void destroy() {} } Finally, the article compares CAS and OAuth2, summarizing their different goals and usage scenarios, and invites readers to ask questions.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaredisspringAuthenticationCASSSOSession
IT Architects Alliance
Written by

IT Architects Alliance

Discussion and exchange on system, internet, large‑scale distributed, high‑availability, and high‑performance architectures, as well as big data, machine learning, AI, and architecture adjustments with internet technologies. Includes real‑world large‑scale architecture case studies. Open to architects who have ideas and enjoy sharing.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.