Master Session Management and SSO: From Cookies to CAS with Java Code
This article explains traditional session mechanisms, the challenges of session handling in clustered environments, explores session sharing strategies, introduces single sign‑on concepts, compares CAS and OAuth2, and provides complete Java code examples for implementing SSO with Redis.
Introduction
A product matrix with many systems makes users switch between applications, causing poor user experience and increased password management costs. Implementing a unified authentication system (SSO) can greatly improve efficiency.
Traditional Session Mechanism and Authentication
Because HTTP is stateless, each request creates a new thread on the server without preserving client context. Session introduces a server‑side object identified by a JSESSIONID cookie (or URL rewriting) to store user state.
Server checks the request for a JSESSIONID. If present, it retrieves the corresponding session data from memory.
If the cookie is missing, the server creates a new session, generates a JSESSIONID, and returns it to the client.
Session Sharing in a Cluster
When multiple servers handle requests, a user may be routed to different nodes, causing session loss because the session is stored only on the original server. Two common solutions are:
Session replication – copy session data to all nodes (high cost, latency).
Centralized session storage – store sessions in an external store such as Redis, allowing all nodes to read/write the same session data.
SSO Challenges and CAS Solution
In large enterprises each system has its own authentication, forcing users to log in repeatedly. Single sign‑on solves this by using a central authentication domain (e.g., oauth.com) that issues a ticket. The workflow is:
User accesses b.com, is redirected to oauth.com for login.
After successful login, oauth.com creates a ticket and stores the associated sessionId in Redis. b.com receives the ticket, retrieves the sessionId from Redis, creates a local session, and redirects the user back.
CAS Implementation (Demo Code)
Below are the core Java classes used in the demo.
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().isEmpty()) {
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);
return mv;
}
} 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);
}
// init and destroy omitted
}CAS vs OAuth2
CAS (Central Authentication Service) provides web‑based SSO for authenticating users across multiple applications, while OAuth2 is an authorization framework that allows third‑party apps to access resources on behalf of a user without exposing credentials.
CAS focuses on verifying the user’s identity for the client application; OAuth2 focuses on granting limited access to the resource owner’s data for the client.
Conclusion
Implementing SSO with CAS and Redis solves the session sharing problem in distributed systems and improves user experience. Understanding the differences between CAS and OAuth2 helps choose the right solution for authentication versus authorization needs.
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.
