Can Multiple Company Systems Share a Single Account? A Deep Dive into Session Sharing and SSO with CAS

This article examines the challenges of managing user authentication across many corporate systems, explains traditional session mechanisms, explores session sharing solutions for clustered environments, and provides a detailed walkthrough of implementing single sign‑on using CAS with Java and Redis, including code samples and a comparison with OAuth2.

Java Architect Handbook
Java Architect Handbook
Java Architect Handbook
Can Multiple Company Systems Share a Single Account? A Deep Dive into Session Sharing and SSO with CAS

Background

Multiple independent systems require users to log in repeatedly, leading to poor user experience, higher password‑management costs, and reduced overall security. A unified authentication mechanism is needed.

Traditional session mechanism and authentication

Cookie and server interaction

HTTP is stateless; a Session is created per user. The server generates a JSESSIONID cookie, stores session data in memory, and returns the cookie to the client. If cookies are disabled, the session ID can be passed via URL rewriting.

Server‑side session process

The server checks whether the request contains a JSESSIONID. If present, the corresponding session is retrieved from memory.

If the request lacks a session ID, the server creates a new session, generates a session ID, and sends it back in the response.

Session‑based authentication flow

Client request → server creates or retrieves a session → user information is stored in the session → subsequent requests use the session ID. This works for single‑node deployments.

Session challenges in clustered environments and solutions

Load balancers distribute requests across multiple servers. Because sessions are stored locally, a user may first hit server A (creating a session) and later hit server B, where the session cannot be found.

Session replication

Session data is copied to all servers whenever it changes. This ensures consistency but incurs high implementation cost, maintenance difficulty, and potential latency.

Centralized session storage

All servers read and write session data to a single external store, typically Redis. This eliminates synchronization overhead and avoids multiple session copies.

Single Sign‑On (SSO) with CAS

CAS fundamentals

CAS is a ticket‑based SSO framework. When a protected service (e.g., b.com) receives a request without a session, it redirects the user to an authentication domain (e.g., ouath.com). After successful login, CAS issues a ticket, stores <ticket, sessionId> in Redis, and redirects back to the original service. The service validates the ticket, retrieves the session ID from Redis, creates a local session, and grants access.

CAS single sign‑on flow
CAS single sign‑on flow

CAS vs OAuth2

OAuth2 is a third‑party authorization protocol that allows a client to access resources on behalf of a user without exposing the user’s credentials. CAS focuses on authenticating the user for web applications (Web SSO). CAS protects client‑side resources, while OAuth2 protects server‑side resources.

Implementation demo (Java Spring)

User entity

public class UserForm implements Serializable {
    private static final long serialVersionUID = 1L;
    private String username;
    private String password;
    private String backurl;
    // getters and setters omitted
}

Login controller (session‑based)

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

Login filter

public class LoginFilter implements Filter {
    public static final String USER_INFO = "user";

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
        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;
        }
        chain.doFilter(request, response);
    }
}

Filter registration

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

SSO filter (CAS ticket validation)

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 req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
        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);
        }
        chain.doFilter(request, response);
    }
}

SSO controller

@Controller
public class IndexController {
    @Autowired
    private RedisTemplate redisTemplate;

    @GetMapping("/index")
    public ModelAndView index(HttpServletRequest request) {
        ModelAndView mv = new ModelAndView();
        UserForm user = (UserForm) request.getSession().getAttribute(SSOFilter.USER_INFO);
        mv.setViewName("index");
        mv.addObject("user", user);
        request.getSession().setAttribute("test", "123");
        return mv;
    }
}

Conclusion

Traditional in‑memory sessions do not scale across clustered deployments. Centralized session storage (e.g., Redis) combined with a CAS‑based SSO architecture provides a practical, maintainable approach to unified authentication. Understanding the functional differences between CAS and OAuth2 helps select the appropriate protocol for a given security requirement.

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.

BackendJavaRedisAuthenticationCASSSOSession
Java Architect Handbook
Written by

Java Architect Handbook

Focused on Java interview questions and practical article sharing, covering algorithms, databases, Spring Boot, microservices, high concurrency, JVM, Docker containers, and ELK-related knowledge. Looking forward to progressing together with you.

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.