eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (cat=Spring Boot)
announcement - icon

Refactor Java code safely — and automatically — with OpenRewrite.

Refactoring big codebases by hand is slow, risky, and easy to put off. That’s where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.

Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions — one for newcomers and one for experienced users. You’ll see how recipes work, how to apply them across projects, and how to modernize code with confidence.

Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.

Course – LJB – NPI EA (cat = Core Java)
announcement - icon

Code your way through and build up a solid, practical foundation of Java:

>> Learn Java Basics

Course – LSS – NPI (cat=Spring Security)
announcement - icon

If you're working on a Spring Security (and especially an OAuth) implementation, definitely have a look at the Learn Spring Security course:

>> LEARN SPRING SECURITY

1. Overview

In this tutorial, we’ll be looking at the Apereo Central Authentication Service (CAS) and we’ll see how a Spring Boot service can use it for authentication. CAS is an enterprise Single Sign-On (SSO) solution that is also open source.

What is SSO? When you log in to YouTube, Gmail and Maps with the same credentials, that’s Single Sign-On. We’re going to demonstrate this by setting up a CAS server and a Spring Boot app. The Spring Boot app will use CAS for authentication.

2. CAS Server Setup

2.1. CAS Installation and Dependencies

The server uses the Maven (Gradle) War Overlay style to ease setup and deployment:

git clone https://github.com/apereo/cas-overlay-template.git cas-server

This command will clone the cas-overlay-template into the cas-server directory.

Some of the aspects we’ll be covering include JSON service registration and JDBC database connection. So, we’ll add their modules to the dependencies section of build.gradle file:

compile "org.apereo.cas:cas-server-support-json-service-registry:${casServerVersion}"
compile "org.apereo.cas:cas-server-support-jdbc:${casServerVersion}"

Let’s make sure to check the latest version of casServer.

2.2. CAS Server Configuration

Before we can start the CAS server, we need to add some basic configurations. Let’s start by creating a cas-server/src/main/resources folder and in this folder. This will be followed by the creation of application.properties in the folder, too:

server.port=8443
spring.main.allow-bean-definition-overriding=true
server.ssl.key-store=classpath:/etc/cas/thekeystore
server.ssl.key-store-password=changeit

Let’s proceed with the creation of the key-store file referenced in the configuration above. First, we need to create the folders /etc/cas and /etc/cas/config in cas-server/src/main/resources.

Then, we need to change the directory to cas-server/src/main/resources/etc/cas and run the command to generate thekeystore:

keytool -genkey -keyalg RSA -alias thekeystore -keystore thekeystore -storepass changeit -validity 360 -keysize 2048

In order for us not to have an SSL handshake error, we should use localhost as the value of first and last name. We should use the same for the organisation name and unit as well. Furthermore, we need to import the thekeystore into the JDK/JRE we’ll be using to run our client app:

keytool -importkeystore -srckeystore thekeystore -destkeystore $JAVA11_HOME/jre/lib/security/cacerts

The password for the source and destination keystore is changeit. On Unix systems, we may have to run this command with admin (sudo) privilege. After importing, we should restart all instances of Java that’s running or restart the system.

We’re using JDK11 because it’s required by CAS version 6.1.x. Also, we defined the environment variable $JAVA11_HOME that points to its home directory. We can now start the CAS server:

./gradlew[.bat] run -Dorg.gradle.java.home=$JAVA11_HOME

When the application starts, we’ll see “READY” printed on the terminal and the server will be available at https://localhost:8443.

2.3. CAS Server User Configuration

We can’t log in yet as we’ve not configured any user. CAS has different methods of managing configuration, including the standalone mode. Let’s create a config folder cas-server/src/main/resources/etc/cas/config in which we’ll create a properties file cas.properties. Now, we can define a static user in the properties file:

cas.authn.accept.users=casuser::Mellon

We have to communicate the location of the config folder to CAS server for the settings to take effect. Let’s update tasks.gradle so we can pass the location as a JVM argument from the command line:

task run(group: "build", description: "Run the CAS web application in embedded container mode") {
    dependsOn 'build'
    doLast {
        def casRunArgs = new ArrayList<>(Arrays.asList(
          "-server -noverify -Xmx2048M -XX:+TieredCompilation -XX:TieredStopAtLevel=1".split(" ")))
        if (project.hasProperty('args')) {
            casRunArgs.addAll(project.args.split('\\s+'))
        }
        javaexec {
            main = "-jar"
            jvmArgs = casRunArgs
            args = ["build/libs/${casWebApplicationBinaryName}"]
            logger.info "Started ${commandLine}"
        }
    }
}

We then save the file and run:

./gradlew run
  -Dorg.gradle.java.home=$JAVA11_HOME
  -Pargs="-Dcas.standalone.configurationDirectory=/cas-server/src/main/resources/etc/cas/config"

Please note that the value of cas.standalone.configurationDirectory is an absolute path. We can now go to https://localhost:8443 and log in with username casuser and password Mellon.

3. CAS Client Setup

We’ll use Spring Initializr to generate a Spring Boot client app. It’ll have Web, Security, Freemarker and DevTools dependencies. Besides, we’ll also add the dependency for Spring Security CAS module to its pom.xml:

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-cas</artifactId>
    <versionId>5.3.0.RELEASE</versionId>
</dependency>

Finally, let’s add the following Spring Boot properties to configure the app:

server.port=8900
spring.freemarker.suffix=.ftl

4. CAS Server Service Registration

Clients applications must register with the CAS server ahead of any authentication. CAS server supports the use of YAML, JSON, MongoDB and LDAP client registries.

In this tutorial, we’ll use the JSON Service Registry method. Let’s create yet another folder cas-server/src/main/resources/etc/cas/services. It’s this folder that’ll house the service registry JSON files.

We’ll create a JSON file that contains the definition of our client application. The name of the file, casSecuredApp-8900.json, follows the pattern serviceName-Id.json:

{
  "@class" : "org.apereo.cas.services.RegexRegisteredService",
  "serviceId" : "http://localhost:8900/login/cas",
  "name" : "casSecuredApp",
  "id" : 8900,
  "logoutType" : "BACK_CHANNEL",
  "logoutUrl" : "http://localhost:8900/exit/cas"
}

The serviceId attribute defines a regex URL pattern for the client application. The pattern should match the URL of the client application.

The id attribute should be unique. In other words, there shouldn’t be two or more services with the same id registered to the same CAS server. Having duplicate id will lead to conflicts and overriding of configurations.

We also configure the logout type to be BACK_CHANNEL and the URL to be http://localhost:8900/exit/cas so that we can do single logout later.
Before the CAS server can make use of our JSON configuration file, we have to enable the JSON registry in our cas.properties:
cas.serviceRegistry.initFromJson=true
cas.serviceRegistry.json.location=classpath:/etc/cas/services

5. CAS Client Single Sign-On Configuration

The next step for us is to configure Spring Security to work with the CAS server. We should also check the full flow of interactions, called a CAS sequence.

Let’s add the following bean configurations to the CasSecuredApplication class of our Spring Boot app:

@Bean
public CasAuthenticationFilter casAuthenticationFilter(
  AuthenticationManager authenticationManager,
  ServiceProperties serviceProperties) throws Exception {
    CasAuthenticationFilter filter = new CasAuthenticationFilter();
    filter.setAuthenticationManager(authenticationManager);
    filter.setServiceProperties(serviceProperties);
    return filter;
}

@Bean
public ServiceProperties serviceProperties() {
    logger.info("service properties");
    ServiceProperties serviceProperties = new ServiceProperties();
    serviceProperties.setService("http://cas-client:8900/login/cas");
    serviceProperties.setSendRenew(false);
    return serviceProperties;
}

@Bean
public TicketValidator ticketValidator() {
    return new Cas30ServiceTicketValidator("https://localhost:8443");
}

@Bean
public CasAuthenticationProvider casAuthenticationProvider(
  TicketValidator ticketValidator,
  ServiceProperties serviceProperties) {
    CasAuthenticationProvider provider = new CasAuthenticationProvider();
    provider.setServiceProperties(serviceProperties);
    provider.setTicketValidator(ticketValidator);
    provider.setUserDetailsService(
      s -> new User("[email protected]", "Mellon", true, true, true, true,
      AuthorityUtils.createAuthorityList("ROLE_ADMIN")));
    provider.setKey("CAS_PROVIDER_LOCALHOST_8900");
    return provider;
}

The ServiceProperties bean has the same URL as the serviceId in casSecuredApp-8900.json. This is important because it identifies this client to the CAS server.

The sendRenew property of ServiceProperties is set to false. This means a user only needs to present login credentials to the server once.

The AuthenticationEntryPoint bean will handle authentication exceptions. Thus, it’ll redirect the user to the login URL of the CAS server for authentication.

In summary, the authentication flow goes:

  1. A user attempts to access a secure page, which triggers an authentication exception
  2. The exception triggers AuthenticationEntryPoint. In response, the AuthenticationEntryPoint will take the user to the CAS server login page – https://localhost:8443/login
  3. On successful authentication, the server redirects back to the client with a ticket
  4. CasAuthenticationFilter will pick up the redirect and call CasAuthenticationProvider
  5. CasAuthenticationProvider will use TicketValidator to confirm the presented ticket on CAS server
  6. If the ticket is valid, the user will get a redirection to the requested secure URL

Finally, let’s configure HttpSecurity to secure some routes in WebSecurityConfig. In the process, we’ll also add the authentication entry point for exception handling:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests().antMatchers( "/secured", "/login").authenticated()
      .and()
      .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint())
      .and()
      .addFilterBefore(singleSignOutFilter, CasAuthenticationFilter.class)
}

6. CAS Client Single Logout Configuration

So far, we’ve dealt with single sign-on; let’s now consider CAS single logout (SLO).

Applications that use CAS for managing user authentication can log out a user from two places:

  • The client application can logout a user from itself locally – this will not affect the user’s login status in other applications using the same CAS server
  • The client application can also log out the user from the CAS server – this will cause the user to be logged out from all other client apps connected to the same CAS server.

We’ll first put in place logout on the client application and then extend it to single logout on the CAS server.

In order to make obvious what goes on behind the scene, we’ll create a logout() method to handle the local logout. On success, it’ll redirect us to a page with a link for single logout:

@GetMapping("/logout")
public String logout(
  HttpServletRequest request, 
  HttpServletResponse response, 
  SecurityContextLogoutHandler logoutHandler) {
    Authentication auth = SecurityContextHolder
      .getContext().getAuthentication();
    logoutHandler.logout(request, response, auth );
    new CookieClearingLogoutHandler(
      AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY)
      .logout(request, response, auth);
    return "auth/logout";
}

In the single logout process, the CAS server will first expire the user’s ticket and then send an async request to all registered client apps. Each client app that receives this signal will perform a local logout. Thereby accomplishing the goal of logout once, it will cause a log out everywhere.

Having said that, let’s add some bean configurations to our client app. Specifically, in the CasSecuredApplicaiton:

@Bean
public SecurityContextLogoutHandler securityContextLogoutHandler() {
    return new SecurityContextLogoutHandler();
}

@Bean
public LogoutFilter logoutFilter() {
    LogoutFilter logoutFilter = new LogoutFilter("https://localhost:8443/logout",
      securityContextLogoutHandler());
    logoutFilter.setFilterProcessesUrl("/logout/cas");
    return logoutFilter;
}

@Bean
public SingleSignOutFilter singleSignOutFilter() {
    SingleSignOutFilter singleSignOutFilter = new SingleSignOutFilter();
    singleSignOutFilter.setLogoutCallbackPath("/exit/cas");
    singleSignOutFilter.setIgnoreInitConfiguration(true);
    return singleSignOutFilter;
}

The logoutFilter will intercept requests to /logout/cas and redirect the application to the CAS server. The SingleSignOutFilter will intercept requests coming from the CAS server and perform the local logout.

7. Connecting the CAS Server to a Database

We can configure the CAS server to read credentials from a MySQL database. We’ll use the test database of a MySQL server that’s running in a local machine. Let’s update cas-server/src/main/resources/application.yml:

cas:
    authn:
        accept:
            users:
        jdbc:
            query[0]:
                sql: SELECT * FROM users WHERE email = ?
                url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
                dialect: org.hibernate.dialect.MySQLDialect
                user: root
                password: root
                ddlAuto: none
                driverClass: com.mysql.cj.jdbc.Driver
                fieldPassword: password
                passwordEncoder:
                    type: NONE

Also, configure same in the cas-secured-app cas-secured-app/src/main/resources/application.properties:

spring.jpa.generate-ddl=false
spring.datasource.url= jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

We set the cas.authn.accept.users to blank. This will deactivate the use of static user repositories by the CAS server.

According to the SQL above, users’ credentials are stored in the users table. The email column is what represents the users’ principal (username).

Please make sure to check the list of supported databases, available drivers and dialects. We also set the password encoder type to NONE. Other encryption mechanisms and their peculiar properties are also available.

Note that the user in the database of the CAS server must be the same as that of the client application.

Let’s update CasAuthenticationProvider to have the same username as the CAS server:

@Bean
public CasUserDetailsService getUser(){
    return new CasUserDetailsService();
}

@Bean
public CasAuthenticationProvider casAuthenticationProvider(
  TicketValidator ticketValidator,
  ServiceProperties serviceProperties) {
    CasAuthenticationProvider provider = new CasAuthenticationProvider();
    provider.setServiceProperties(serviceProperties);
    provider.setTicketValidator(ticketValidator);
    provider.setUserDetailsService(getUser());
    provider.setKey("CAS_PROVIDER_LOCALHOST_8900");
    return provider;
}
The CasAuthenticationProvider requires a UserDetailsService to load user details based on the CAS ticket. The UserDetailsService is responsible for retrieving user information from a data source, such as a database. In the loadUserByUsername method of the UserDetailsService implementation, you can customize the logic to load user details based on the provided username.
public class CasUserDetailsService implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // Get the user from the database.
        CasUser casUser = getUserFromDatabase(username);

        // Create a UserDetails object.
        UserDetails userDetails = new User(
            casUser.getEmail(),
            casUser.getPassword(),
           Collections.singletonList(new SimpleGrantedAuthority("ROLE_ADMIN")));

        return userDetails;
    }

    private CasUser getUserFromDatabase(String username) {
       return userRepository.findByEmail(username);
    }
}

The loadUserByUsername method is a part of the CasUserDetailsService class. This method is responsible for loading a user’s details based on their username. You can find more information regarding Authentication with a Database-backed UserDetailsService.

Once the CAS ticket is validated and the user details are loaded, the CasAuthenticationProvider creates an authenticated Authentication object, which can then be used for authorization and access control in the application.

CasAuthenticationProvider does not use the password for authentication. Nonetheless, its username has to match that of the CAS server for authentication to be successful. CAS server requires a MySQL server to be running on localhost at port 3306. The username and password should be root.

Restart the CAS server and the Spring Boot app once again. Then use the new credentials for authentication.

8. Conclusion

We have looked at how to use CAS SSO with Spring Security and many of the configuration files involved. There are many other aspects of CAS SSO that is configurable. Ranging from themes and protocol types to authentication policies.

The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.
Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (tag=Refactoring)
announcement - icon

Modern Java teams move fast — but codebases don’t always keep up. Frameworks change, dependencies drift, and tech debt builds until it starts to drag on delivery. OpenRewrite was built to fix that: an open-source refactoring engine that automates repetitive code changes while keeping developer intent intact.

The monthly training series, led by the creators and maintainers of OpenRewrite at Moderne, walks through real-world migrations and modernization patterns. Whether you’re new to recipes or ready to write your own, you’ll learn practical ways to refactor safely and at scale.

If you’ve ever wished refactoring felt as natural — and as fast — as writing code, this is a good place to start.

Course – LSS – NPI (cat=Security/Spring Security)
announcement - icon

I just announced the new Learn Spring Security course, including the full material focused on the new OAuth2 stack in Spring Security:

>> CHECK OUT THE COURSE

eBook Jackson – NPI EA – 3 (cat = Jackson)