Spring Security Series tutorial 28 -- implementing CAS single sign on with spring security Part 2 -- building CAS client

Keywords: Java Spring Spring Boot Spring Security

preface

In the previous chapter, I took you to build the CAS Server side project, that is, we built a unified single sign on authentication center. Next, we can build the CAS client project, and then realize the interactive authentication between the client and the server, so as to complete single sign on.

Next, you will follow brother Yige to build CAS client and finally realize single sign on!

1, Build CAS client

1. Create a new project

In the previous Spring Security project, we created a new module as the CAS Client project, as shown in the following figure.

2. Introduce dependency

Then, the related dependencies are introduced into the pom.xml file of this module.

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
        </dependency>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

The most important dependency is spring security CAS. Its internal dependencies are as follows:

3. Configure CAS Client

Create an application.yml file and add CAS Client related configurations, mainly those associated with CAS Server. Please fill in the address information of your CAS Server here.

cas:
  #Configure the server
  server:
    #The main path of cas server, which points to our cas server
    prefix: https://cas.yyg.cn:8443/cas
    #Point to the login page of cas server
    login: ${cas.server.prefix}/login
    #Point to the exit login page of cas server
    logout: ${cas.server.prefix}/logout
  #Configure the client  
  client:
    #Configure cas client url prefix
    prefix: http://client.cas.yyg.cn:8080
    #Configure client login address
    login: ${cas.client.prefix}/login/cas
    #Configure the exit login interface as a relative path
    relative: /logout/cas
    #Configure the exit login interface as an absolute path
    logout: ${cas.client.prefix}${cas.client.relative}
  user:
    #Configure the user name to log in to the cas server
    inmemory: yyg

4. Create configuration class

In the previous section, I have created the configuration information related to CAS Client. In order to facilitate us to reference the configuration information, we can create a corresponding configuration class CasSecurityConfig, and in this class, we can create some filters related to single sign on.

@Configuration
public class CasSecurityConfig {

    @Value("${cas.server.prefix}")
    private String casServerPrefix;

    @Value("${cas.server.login}")
    private String casServerLogin;

    @Value("${cas.server.logout}")
    private String casServerLogout;

    @Value("${cas.client.login}")
    private String casClientLogin;

    @Value("${cas.client.logout}")
    private String casClientLogout;

    @Value("${cas.client.relative}")
    private String casClientLogoutRelative;

    @Value("${cas.user.inmemory}")
    private String casUserInMemory;

    /**
     * Configure properties of CAS Client
     *
     * @return
     */
    @Bean
    public ServiceProperties serviceProperties() {
        ServiceProperties serviceProperties = new ServiceProperties();
        // Consistent with the URL monitored by CasAuthenticationFilter
        serviceProperties.setService(casClientLogin);
        // Whether to turn off single sign on is false by default, so it can not be set.
        serviceProperties.setSendRenew(false);
        return serviceProperties;
    }

    /**
     * Configure CAS authentication entry and provide the redirection address of user browser
     *
     * @param sp
     * @return
     */
    @Bean
    @Primary
    public AuthenticationEntryPoint authenticationEntryPoint(ServiceProperties sp) {
        CasAuthenticationEntryPoint entryPoint = new CasAuthenticationEntryPoint();
        // Login address of CAS Server authentication
        entryPoint.setLoginUrl(casServerLogin);
        entryPoint.setServiceProperties(sp);
        return entryPoint;
    }

    /**
     * To configure the ticket verification function, you need to provide the address of the CAS Server verification ticket
     *
     * @return
     */
    @Bean
    public TicketValidator ticketValidator() {
        // By default, Cas20ProxyTicketValidator is used, and the validation entry is ${casServerPrefix}/proxyValidate
        return new Cas20ProxyTicketValidator(casServerPrefix);
    }

    /**
     * Create a user in memory and assign permissions
     *
     * @return
     */
    @Bean
    public UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(User.withUsername(casUserInMemory).password("").roles("USER").build());
        return manager;
    }

    /**
     * Set cas authentication processing logic
     *
     * @param sp
     * @param ticketValidator
     * @param userDetailsService
     * @return
     */
    @Bean
    public CasAuthenticationProvider casAuthenticationProvider(ServiceProperties sp, TicketValidator ticketValidator, UserDetailsService userDetailsService) {
        CasAuthenticationProvider provider = new CasAuthenticationProvider();
        provider.setServiceProperties(sp);
        provider.setTicketValidator(ticketValidator);
        provider.setUserDetailsService(userDetailsService);
        provider.setKey("yyg");
        return provider;
    }

    /**
     * A special filter for CAS authentication is provided, and the authentication logic of the filter is provided by CasAuthenticationProvider
     *
     * @param sp
     * @param ap
     * @return
     */
    @Bean
    public CasAuthenticationFilter casAuthenticationFilter(ServiceProperties sp, AuthenticationProvider ap) {
        CasAuthenticationFilter filter = new CasAuthenticationFilter();
        filter.setServiceProperties(sp);
        filter.setAuthenticationManager(new ProviderManager(Arrays.asList(ap)));
        return filter;
    }

    /**
     * Configure the single sign on filter to accept the logout request sent by the cas server
     *
     * @return
     */
    @Bean
    public SingleSignOutFilter singleSignOutFilter() {
        SingleSignOutFilter singleSignOutFilter = new SingleSignOutFilter();
        singleSignOutFilter.setCasServerUrlPrefix(casServerPrefix);
        singleSignOutFilter.setIgnoreInitConfiguration(true);
        return singleSignOutFilter;
    }

    /**
     * Forward logout request to cas server
     *
     * @return
     */
    @Bean
    public LogoutFilter logoutFilter() {
        LogoutFilter logoutFilter = new LogoutFilter(casServerLogout, new SecurityContextLogoutHandler());
        // Set the path of the client logout request
        logoutFilter.setFilterProcessesUrl(casClientLogoutRelative);
        return logoutFilter;
    }

}

5. Create Security configuration class

Then create a WebSecurityConfig configuration class to intercept the authentication of related interfaces and configure the filters related to single sign on.

@EnableWebSecurity(debug = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private AuthenticationProvider authenticationProvider;

    @Autowired
    private AuthenticationEntryPoint entryPoint;
   
    @Autowired
    private CasAuthenticationFilter casAuthenticationFilter;

    @Autowired
    private SingleSignOutFilter singleSignOutFilter;

    @Autowired
    private LogoutFilter requestSingleLogoutFilter;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) {

        auth.authenticationProvider(authenticationProvider);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/admin/**")
                .hasRole("ADMIN")
                .antMatchers("/user/**")
                .hasRole("USER")
                .antMatchers("/login/cas", "/favicon.ico", "/error")
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .exceptionHandling()
                //Set authentication entry
                .authenticationEntryPoint(entryPoint)
                .and()
                //Add filters to perform single sign on processing logic
                .addFilter(casAuthenticationFilter)
                //Process authentication logic
                .addFilterBefore(singleSignOutFilter, CasAuthenticationFilter.class)
                //Process logout
                .addFilterBefore(requestSingleLogoutFilter, LogoutFilter.class);
    }

}

6. Create test interface

Next, create a UserController and test it. According to the previous WebSecurityConfig configuration class, the / user interface requires that visitors must have user role permissions to access, so it is a protected resource.

@RestController
@RequestMapping("/user")
public class UserController {

    @GetMapping
    public String hello() {

        return "hello, user";
    }

}

7. Create project entry class

/**
 * cas Client to achieve authentication with cas server
 */
@SpringBootApplication
public class Demo13Application {

    public static void main(String[] args) {

        SpringApplication.run(Demo13Application.class, args);
    }

}

8. Client project structure

So far, I have written the core code of CAS Client project. You can follow my project creation process to create multiple CAS Client projects. There is only one CAS Server project and multiple CAS Client projects.

The code structure of the whole project is as follows:

9. Start operation project

Next, we start the client project and conduct interface test to see if we can jump to CAS Server for unified authentication when we are not logged in.

We enter it directly in the browser http://client.cas.yyg.cn:8080/user Address, which is the test interface defined in our CAS Client project. It can only be accessed with user role permission. It can not be accessed directly without login.

Before we enter http://client.cas.yyg.cn:8080/user After the address is, on the browser console, we can find that a 302 redirect occurs when accessing the interface, which will redirect from the / user interface to the / cas/login login interface of CAS Server. Here, we need to enter the user name and password previously configured in the application.properties configuration file of CAS Server, yyg:123

When we enter the correct user name and password, CAS Server will redirect back from CAS Server to CAS Client after successful authentication, so as to access / user interface.

In this way, the interactive authentication between CAS Client and CAS Server is realized.

2, Single sign off

After realizing single sign on, we can also realize single sign off.

At this time, we can pass the request https://cas.yyg.cn:8443/cas/logout Address to perform single sign off, so that when a CAS client project exits, all other clients in the CAS Server also log off.

So far, Yige has simply implemented single sign on and single sign off based on CAS framework. We use CAS Server as a unified central Certification Center, and then create several CAS Client projects to realize the requirement of single sign on.

Have you learned? If you think this series of articles are helpful to you, please praise it in the comment area!

Posted by richcrack on Wed, 03 Nov 2021 21:31:12 -0700