Mastering Single Sign-On: From Session Basics to CAS Implementation in Java

This article explains the limitations of traditional HTTP session handling, explores session sharing strategies for clustered environments, and walks through a complete Java CAS‑based single sign‑on solution with code examples, highlighting the differences between CAS and OAuth2.

Java High-Performance Architecture
Java High-Performance Architecture
Java High-Performance Architecture
Mastering Single Sign-On: From Session Basics to CAS Implementation in Java

Background

When a company has many products, users must switch between systems, which hurts experience and raises password‑management costs. Implementing a unified authentication (single sign‑on) can greatly improve usability and security.

Traditional Session Mechanism and Authentication

Cookie Interaction

HTTP is stateless, so servers create a new session for each request. The session ID (JSESSIONID) is stored in a cookie or URL rewrite, allowing the server to associate requests with a specific user.

JSESSIONID is kept in browser memory; if cookies are disabled, the ID appears in the URL. Different browser windows receive different session IDs, preventing cross‑window sharing.

Server‑Side Session Mechanism

The server checks incoming requests for a JSESSIONID. If present, it retrieves the session from memory; otherwise it creates a new session and returns the ID to the client.

Server looks up the cookie value (sessionId).

Retrieves session data from server‑side storage.

If not found, creates a new session and writes a cookie to the response.

Session‑Based Authentication Flow

Because HTTP is stateless, most web authentication relies on this session pattern, which has inherent drawbacks.

Session Challenges in Clustered Environments and Solutions

With growing traffic, applications are deployed on multiple servers behind a load balancer, causing a user's requests to hit different nodes. Since sessions are stored on a single server, the next request may miss the session.

Session Sharing Strategies

Two main approaches

Session replication – copy session data to all nodes.

Session centralized storage – store sessions in a shared service (e.g., Redis).

Session replication copies session data on login, update, or logout, but it is costly, hard to maintain, and can introduce latency.

Session centralized storage places session data in a single service (commonly Redis), avoiding synchronization overhead.

Login Challenges in Multi‑Service Environments and SSO Solutions

Why SSO Emerged

Enterprises with many systems require users to log in separately to each, leading to poor management and user experience. Single sign‑on lets a user authenticate once and access all systems.

CAS Underlying Principle

For different domains, cookies cannot be shared, so a dedicated domain (e.g., auth.com) issues a ticket that links to a session ID stored in Redis.

When a protected site (b.com) is accessed, it redirects to auth.com for login.

User logs in at auth.com; a ticket is generated and stored with the session ID in Redis.

b.com receives the ticket, retrieves the session ID from Redis, creates a local session, and redirects the user back.

CAS vs. OAuth2

OAuth2 is a third‑party authorization protocol that lets a client access resources without the user sharing credentials.

CAS (Central Authentication Service) is a web‑centric SSO framework that authenticates users for multiple web applications.

CAS focuses on verifying a user's access to a client’s resources, while OAuth2 protects the resource server’s data.

CAS Demo Code

User entity class:

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
}

Login controller:

@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, 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);
        }
    }
}

Login filter:

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);
    }
    // init and destroy omitted
}

SSO filter (centralized session storage):

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().toString());
                return;
            }
            request.getSession().setAttribute(USER_INFO, userInfo);
            redisTemplate.delete(ticket);
        }
        filterChain.doFilter(request, servletResponse);
    }
    // init and destroy omitted
}

With these components, a Java web application can provide seamless single sign‑on across multiple services.

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.

JavaBackend DevelopmentAuthenticationCASSession ManagementSSO
Java High-Performance Architecture
Written by

Java High-Performance Architecture

Sharing Java development articles and resources, including SSM architecture and the Spring ecosystem (Spring Boot, Spring Cloud, MyBatis, Dubbo, Docker), Zookeeper, Redis, architecture design, microservices, message queues, Git, etc.

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.