Information Security 13 min read

Implementing Single Sign-On (SSO) with CAS and Session Management in Distributed Backend Systems

This article explains the challenges of multiple product logins, reviews traditional session mechanisms, discusses session sharing solutions in clustered environments, and presents a CAS‑based single sign‑on implementation with Java code examples, highlighting differences between CAS and OAuth2 for secure authentication.

Architect's Guide
Architect's Guide
Architect's Guide
Implementing Single Sign-On (SSO) with CAS and Session Management in Distributed Backend Systems

Background: When a company has many products, users need to log in to each system, causing poor experience, higher password management costs, and reduced security; a unified single sign‑on (SSO) can greatly improve usability and efficiency.

Traditional session mechanism: HTTP is stateless, so servers create a SessionID (e.g., JSESSIONID) stored in a cookie or URL rewrite. The server checks the SessionID on each request, creates a session if missing, and stores session data in memory as a hash table.

Session sharing in a cluster: In distributed deployments, a user may be routed to different servers, leading to lost sessions. Two main solutions are session replication and centralized session storage, typically using Redis to store sessions centrally.

SSO solution with CAS: A central authentication service (CAS) issues a ticket after login; other applications validate the ticket, retrieve user information from Redis, set a local session cookie, and redirect back to the original URL, achieving cross‑domain single sign‑on.

Key Java code examples (Spring MVC) are shown below:

public class UserForm implements Serializable {
private static final long serialVersionUID = 1L;
private String username;
private String password;
private String backurl;
// getters and setters ...
}
@Controller
public class IndexController {
@Autowired
private RedisTemplate redisTemplate;
@GetMapping("/toLogin")
public String toLogin(Model model, HttpServletRequest request) {
// login logic ...
}
@PostMapping("/login")
public void login(@ModelAttribute UserForm user, HttpServletRequest request, HttpServletResponse response) throws IOException {
// authentication and ticket generation ...
}
@GetMapping("/index")
public ModelAndView index(HttpServletRequest request) {
// display user info ...
}
}
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, servletResponse);
}
}
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 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()); return; }
request.getSession().setAttribute(USER_INFO, userInfo);
redisTemplate.delete(ticket);
}
filterChain.doFilter(request, servletResponse);
}
}
@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;
}
}

Difference between CAS and OAuth2: CAS provides centralized authentication for web applications (client‑side resource protection), while OAuth2 authorizes third‑party services to access server resources, focusing on server‑side resource protection.

backendJavaRedisAuthenticationCASSSOsession
Architect's Guide
Written by

Architect's Guide

Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.

0 followers
Reader feedback

How this landed with the community

login 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.