Implementing CAS Single Sign‑On with a Custom Authentication Filter in Spring Boot
This article explains how to integrate the CAS (Central Authentication Service) single sign‑on solution into a Spring Boot backend by adding necessary dependencies, configuring a series of CAS filters, creating a custom MyAuthenticationFilter to handle AJAX requests, and demonstrating a Vue.js frontend that interacts with the authentication flow, complete with screenshots of the login, ticket validation, and logout processes.
Introduction Single Sign‑On (SSO) allows a user to log in once and gain access to multiple systems without re‑authenticating. The article focuses on CAS, an open‑source SSO solution.
What is CAS? CAS provides an enterprise‑grade SSO mechanism. It includes a CAS Server and various client libraries supporting Java, .NET, PHP, and more.
Building the Client System
1. Add Maven dependencies:
<dependency>
<groupId>org.jasig.cas.client</groupId>
<artifactId>cas-client-core</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.10.5</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-cas</artifactId>
</dependency>2. Configure client properties such as casServerLoginUrl , casServerLogoutUrl , and clienthosturl in a Spring configuration class.
3. Register filters for CAS sign‑out, SSL ignore, authentication, ticket validation, and character encoding using FilterRegistrationBean beans.
Custom Authentication Filter The default CAS AuthenticationFilter redirects on authentication failure, which breaks AJAX calls. A custom MyAuthenticationFilter extends AbstractCasFilter and returns a JSON response with code 202 when the request is an XMLHttpRequest , allowing the frontend to handle the redirect manually.
public class MyAuthenticationFilter extends AbstractCasFilter {
private String casServerLoginUrl;
private boolean renew = false;
private boolean gateway = false;
// ... other fields ...
@Override
protected void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
// ... check session, ticket, gateway ...
if (!CommonUtils.isNotBlank(ticket) && !wasGatewayed) {
String xRequested = request.getHeader("x-requested-with");
if ("XMLHttpRequest".equals(xRequested)) {
response.getWriter().write("{\"code\":202, \"msg\":\"no ticket and no assertion found\"}");
} else {
String urlToRedirectTo = CommonUtils.constructRedirectUrl(casServerLoginUrl, getServiceParameterName(), modifiedServiceUrl, renew, gateway);
authenticationRedirectStrategy.redirect(request, response, urlToRedirectTo);
}
return;
}
filterChain.doFilter(request, response);
}
// setters omitted for brevity
}Controller Example A simple Spring MVC controller provides /test to return the logged‑in user and /logout to redirect to the CAS logout URL.
@Controller
public class TestController {
@GetMapping("/test")
@ResponseBody
public Result
login(HttpServletRequest request) {
return new Result<>(new UserDomain(request.getRemoteUser()));
}
@RequestMapping("/logout")
public String logout() {
return "redirect:https://127.0.0.1:8443/cas/logout";
}
}Frontend Integration A Vue.js component makes an AJAX call to /test . If the response code is 202, it redirects the browser to the ticket‑checking endpoint; otherwise it displays the username.
export default {
data() { return { name: 'ss' }; },
mounted() {
this.$http.get('/test', { headers: { 'x-requested-with': 'XMLHttpRequest' } })
.then(response => {
if (response.data.code === 202) {
window.location.href = 'http://127.0.0.1:1235/checkTicket';
} else if (response.data.code === 200) {
this.name = response.data.data.name;
}
})
.catch(error => console.log(error));
},
methods: {
logout() { window.location.href = 'http://127.0.0.1:1234/logout'; }
}
};Result The system correctly redirects unauthenticated users to the CAS login page, allows seamless SSO across two client applications, and provides a secure logout that clears the CAS session. Screenshots in the original article illustrate the before‑login state, the login flow, successful authentication, and logout.
Conclusion While many CAS demos exist, this guide addresses the challenges of front‑end/back‑end separation, cross‑origin issues, and AJAX handling, offering a practical foundation for SSO integration. Alternatives such as JWT and OAuth2 are mentioned for broader authentication strategies.
Top Architect
Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.
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.