Understanding Session Mechanisms, Cluster Session Challenges, and SSO Solutions with a CAS Implementation
This article explains traditional HTTP session handling, the problems of session sharing in clustered and multi‑service environments, and presents practical SSO solutions—including session replication, centralized Redis storage, and a complete CAS‑based single sign‑on implementation with Java code examples.
When a company has many products, users must repeatedly log in to each system, causing poor experience and increased password‑management cost; a unified authentication (SSO) can solve this.
Traditional Session Mechanism and Authentication
HTTP is stateless, so a JSESSIONID cookie is created to identify a user session on the server. The server stores session data in memory and retrieves it on each request using the cookie or URL rewriting.
Server‑Side Session Mechanism
On each request the server checks for a JSESSIONID. If present, the corresponding session object is fetched from memory; otherwise a new session is created and the ID is sent back to the client.
Session‑Based Authentication Flow
The typical flow creates a session after successful login, stores user information in the session, and uses the session ID for subsequent authorization checks.
Cluster Session Challenges and Solutions
In a distributed deployment, a user may be routed to different servers, causing session loss because the session is stored locally. Two main solutions are:
Session replication – copy session data to all nodes (high cost, latency).
Centralized session storage – store sessions in a shared store such as Redis, allowing all nodes to read/write the same session data.
Multi‑Service Login Challenges and SSO Solution
Multiple independent systems each require login, leading to poor user experience. SSO lets a user log in once and obtain a ticket that is accepted by all systems.
CAS SSO Implementation Details
The CAS flow uses a dedicated domain (e.g., ouath.com) to issue a ticket, store ticket → sessionId mapping in Redis, and redirect the user back to the original service, which then validates the ticket and creates a local session.
Key code snippets:
public class UserForm implements Serializable {
private static final long serialVersionUID = 1L;
private String username;
private String password;
private String backurl;
// getters and setters omitted for brevity
} @Controller
public class IndexController {
@Autowired
private 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 {
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);
}
}
} public class LoginFilter implements Filter {
public static final String USER_INFO = "user";
@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);
}
} @Configuration
public class LoginConfig {
@Bean
public FilterRegistrationBean sessionFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new LoginFilter());
registration.addUrlPatterns("/*");
registration.setName("sessionFilter");
registration.setOrder(1);
return registration;
}
} <!-- login.html (Thymeleaf) -->
<!DOCTYPE HTML>
<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="text" th:field="*{backurl}" hidden="hidden"/>
</form>
</div>
</body>
</html>Differences Between CAS and OAuth2
CAS is a centralized authentication service that validates whether a user can access a protected resource (client‑side security), while OAuth2 is an authorization framework that lets a third‑party client obtain limited access to a resource owned by the user (resource‑side security).
In practice, use CAS when you need a single sign‑on across internal systems; use OAuth2 when you need to grant external applications delegated access to user data.
Overall, the article provides a complete walkthrough from basic session concepts to a working SSO demo based on CAS, including the necessary Java/Spring code and deployment considerations.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Architect's Guide
Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
