springboot Integrated CAS for Single Sign On

Recently, new projects have used CAS single sign-on, and I won't, how can I tolerate it?Learn nothing and build a demo for spring-boot integrated CAS.Single sign-on and sign-out are implemented.

Single Sign On is the full name of SSO.
Meaning: In multiple trusted systems, as long as you log in to one system, other systems can access it.

CAS It is a widely used single sign-on implementation, which is divided into client CAS Client and server CAS Service. Client is our system, server is the certification center and provided by CAS. We need to modify it slightly to start it up and use it.~~~~

https certificate

CAS Service s require https, so they need a certificate, which can be purchased or generated by themselves.
In fact, this step can also be omitted, using http when accessing, but cas will give a warning.

The steps are as simple as putting an elephant in the fridge. There are three steps altogether:

  1. Generate Key
  2. Generate Certificate
  3. Import Certificate
1. Generate Key

keytool -genkey -alias cainiao -keyalg RSA -keystore E:sslcainiao.keystore

Parameter description:

  • -genkey Generation Key
  • -keyalg specifies the key algorithm, then RSA
  • -alias Specify Alias
  • -keystore Specifies keystore storage location, where E:/ssl/directory exists

During the execution, you will be asked a lot of questions, when asked: What is your first and last name?
At this point, you need to fill in the domain name as the access address for the future, other arbitrary.
Generate a key file after executionCainiao.keystore

2. Generate certificates

keytool -export -alias cainiao -storepass 123456 -file E:/ssl/cainiao.cer -keystore E:/ssl/cainiao.keystore

Parameter description:

  • -Password set when storepass just generated the key file
  • -file Specifies the file name of the exported certificateCainiao.cer
  • -keystore Specifies the filename of the previously generated key file

After execution a directory is generatedCainiao.cercertificate

3. Import Certificates

keytool -import -alias cainiao -keystore C:/"Program Files"/Java/jdk1.8.0_181/jre/lib/security/cacerts -file E:/ssl/cainiao.cer -trustcacerts

Import Certificate into JDK Trust Library
Put the original $JAVA_HOME/jre/lib/security/cacerts file should be deleted first, otherwise Keystore was tampered with, or password was incorrect.

Here's the whole process:

PS E:\ssl> keytool -genkey -alias cainiao -keyalg RSA -keystore E:\ssl\cainiao.keystore
 Enter the keystore password:
Enter the new password again:
What is your first and last name?
  [Unknown]:  www.cainiao.com
 What is the name of your organization?
  [Unknown]:  cainian
 What is your organization name?
  [Unknown]:  cainiao
 What is the name of your city or region?
  [Unknown]:  wx
 What is the name of your province/municipality?
  [Unknown]:  js
 What is the double-letter country/region code for this unit?
  [Unknown]:  CN
 CN=Www.cainiao.comIs OU=cainian, O=cainiao, L=wx, ST=js, C=CN correct?
  [No]: y

Enter the key password for <cainiao>
        (Press Enter if the password is the same as the keystore password):
Enter the new password again:

------------------------------------------------------------------------------------

PS E:\ssl> keytool -export -alias cainiao -storepass 123456 -file E:/ssl/cainiao.cer -keystore E:/ssl/cainiao.keystore
 Stored in file <E:/ssl/Cainiao.cer>Certificates in

------------------------------------------------------------------------------------

PS E:\ssl> keytool -import -alias cainiao -keystore C:/"Program Files"/Java/jdk1.8.0_181/jre/lib/security/cacerts -file E:/ssl/cainiao.cer -trustcacerts
 Enter the keystore password:
Owner: CN=www.cainiao.com, OU=cainian, O=cainiao, L=wx, ST=js, C=CN
 Publisher: CN=www.cainiao.com, OU=cainian, O=cainiao, L=wx, ST=js, C=CN
 Serial number: 509d1aea
 Valid from Wed Jun 17 22:02:55 CST 2020 to Tue Sep 15 22:02:55 CST 2020
 Certificate fingerprint:
         MD5:  5B:B2:7C:D7:B7:31:C5:7C:1C:BC:F7:DA:A8:2D:1C:B2
         SHA1: F6:76:55:55:D7:48:E3:9F:3A:B6:EE:68:1F:BE:DC:DE:51:B1:33:E5
         SHA256: 24:53:18:CD:E8:95:65:D8:6E:6A:7B:8E:79:CB:91:BD:F4:2E:C3:99:59:D1:76:12:A8:95:45:2A:4B:03:E4:AD
 Signature algorithm name: SHA256withRSA
 Principal public key algorithm: 2048-bit RSA key
 Version: 3

Extensions:

#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 70 B3 D5 76 36 EA 54 BA   75 C1 A1 5C DA 76 82 0E  p..v6.T.u..\.v..
0010: 4D F4 C9 05                                        M...
]
]

Do you trust this certificate? [No]:y
 Certificate added to keystore

Finally, hosts configuration 127.0.0.1Www.cainiao.com

Set up CAS service

Template needs to be pulled from github https://github.com/apereo/cas...
After 5.3 are gradle projects, before 5.3 are maven projects, and I download version 5.3.

1. >Remove the <repositories>warehouse address from the pom. Warehouse addresses abroad are slower.You know.
2. >Build/src/main/resources directory under root directory
3. > Copy the generated key file to the / src/main/resources directory
4. >Will overlays/Org.apereo.cas.cas-server-webapp-tomcat-5.3.14/WEB-INF/classes/Application.propertiesCopy the file to the directory you created in step 2.
5. >Modify the copied/src/main/resources/Application.propertiesFill in the document truthfully according to the certificate information above.

server.ssl.key-store=classpath:cainiao.keystore
server.ssl.key-store-password=123456
server.ssl.key-password=123456

6. > Connect to the mysql database and add dependencies to the pom

<dependency>
     <groupId>org.apereo.cas</groupId>
     <artifactId>cas-server-support-jdbc</artifactId>
     <version>${cas.version}</version>
</dependency>
<dependency>
     <groupId>mysql</groupId>
     <artifactId>mysql-connector-java</artifactId>
     <version>5.1.21</version>
</dependency>

Perhaps you will find that there is a xmlsectool-2.0.0.jar package that can not be downloaded from Ali Yun's warehouse. It needs to be downloaded from the maven central warehouse and installed into the local warehouse. It is not directly placed in the local warehouse. The jar packages must be installed into the local warehouse using commands.

MVNInstall:install-file -Dfile="E: Download xmlsectool-2.0.0.jar" "-DgroupId=Net.shibboleth.tool"" -DartifactId=xmlsectool "-Dversion=2.0.0" -Dpackaging=jar"

Install jar packages to local warehouse notes

7. > in copied/src/main/resources/Application.propertiesThe following information is being added to the file

#Query account password sql, must contain password field
cas.authn.jdbc.query[0].sql=select * from sys_user where username=?
#Specify the sql query field name above (must)
cas.authn.jdbc.query[0].fieldPassword=password
#Specify expiration field, 1 is expiration, if expiration requires password change
cas.authn.jdbc.query[0].fieldExpired=expired
#Is not available, 1 is not available,
cas.authn.jdbc.query[0].fieldDisabled=disabled
#Knowledge of database dialect hibernate
cas.authn.jdbc.query[0].dialect=org.hibernate.dialect.MySQLDialect
#Database Driver
cas.authn.jdbc.query[0].driverClass=com.mysql.jdbc.Driver
#Database Connection 
cas.authn.jdbc.query[0].url=jdbc:mysql://localhost:3306/cas?useUnicode=true&characterEncoding=UTF-8
#Database User Name
cas.authn.jdbc.query[0].user=root
#Database Password
cas.authn.jdbc.query[0].password=123456
#Default encryption policy, specify algorithm by encodingAlgorithm, default NONE is not encrypted
cas.authn.jdbc.query[0].passwordEncoder.type=DEFAULT
cas.authn.jdbc.query[0].passwordEncoder.characterEncoding=UTF-8
cas.authn.jdbc.query[0].passwordEncoder.encodingAlgorithm=MD5

Attach database sql, user information table

-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  `expired` int(11) DEFAULT NULL,
  `disabled` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES ('1', 'admin', '21232f297a57a5a743894a0e4a801fc3', '0', '1');
INSERT INTO `sys_user` VALUES ('2', 'cainiao', '6b757206058785025cd90c8d865c8e43', '1', '0');
INSERT INTO `sys_user` VALUES ('3', 'mashu', 'd1f21ceb3f710ebbd9f408274aee1193', '0', '0');

The user name is MD5-encrypted in the database as is the password.
This completes the CAS service, which is used in the root directoryBuild.cmdRun Command Start
Access Address is started when READY branner appears https://www.cainiao.com:8443/cas/login

mashu logged in normally, cainiao needed to change password, admin was disabled, as expected.

Set up CAS client

Create a spring boot project.
1. Join cas client Dependency, I choose the latest version 2.3.0-GA

<dependency>
    <groupId>net.unicon.cas</groupId>
    <artifactId>cas-client-autoconfig-support</artifactId>
    <version>2.3.0-GA</version>      
</dependency>

2. Comment @EnableCasClient on the startup class
3. InApplication.propertiesAdd Configuration in

#Address of cas server
cas.server-url-prefix=https://www.cainiao.com:8443/cas
#Login address of cas server
cas.server-login-url=https://www.cainiao.com:8443/cas/login
#Client Access Address
cas.client-host-url=http://www.mashu.com:8080
cas.validation-type=CAS3

4. Add hosts configuration to configure the client's access address to hosts

127.0.0.1 www.mashu.com

The client is then configured.

Single sign-on

I'll write a controller to visit.

@RestController
public class TestController {

    @RequestMapping("/hello")
    public String hello() {
        return "word";
    }
}

Visit http://www.mashu.com:8080/hello

Uncertified Authorized Services

Reporting error:

The reason is that the server does not allow requests for the client's http protocol.The following changes need to be made to the server to allow him to compromise.
1. >Modify overlays/Org.apereo.casCas-server-webapp-tomcat-5.3.14/WEB-INF/classes/services/HTTPSandIMAPS-10000001.json file

"serviceId" changed from'^(https|imaps): //. *'to'^(https|imaps|http): //. *'

2. >InApplication.propertiesAdd to the file:

#Allow http
cas.tgc.secure=false
cas.serviceRegistry.initFromJson=true

Visit again http://www.mashu.com 8080/hello, you can see the login page that has been forwarded to the server with the address.

Enter account password mashu/mashu, and come back after successful login!Hahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahahaha

Multi-system login

Start another client and open the edit configurations settings for idea.Check Allow parallel run

modifyApplication.properties, the port of the service and other information, then click Start, you can start (8081/8080) two clients at the same time

server.port=8081
#Address of cas server
cas.server-url-prefix=https://www.cainiao.com:8443/cas
#Login address of cas server
cas.server-login-url=https://www.cainiao.com:8443/cas/login
#Client Access Address
cas.client-host-url=http://www.mshu.com:8081
cas.validation-type=CAS3

Access the second client http://www.mshu.com : 8081/hello, (need to configure host first), logged in directly, and completed single sign-on here.

Single Sign Out

Add two profiles;

1. CasProperties.java

import org.springframework.boot.context.properties.ConfigurationProperties;
import javax.validation.constraints.NotNull;

@ConfigurationProperties(prefix = "cas",ignoreUnknownFields = true)
public class CasProperties {

    /**
     * CAS server URL E.g. https://example.com/cas or https://cas.example. Required.
     * CAS Server url cannot be empty
     */
    @NotNull
    private String serverUrlPrefix;

    /**
     * CAS server login URL E.g. https://example.com/cas/login or https://cas.example/login. Required.
     * CAS The connection above the server login address plus/login cannot be empty
     */
    @NotNull
    private String serverLoginUrl;

    /**
     * CAS-protected client application host URL E.g. https://myclient.example.com Required.
     * Address of current client
     */
    @NotNull
    private String clientHostUrl;

    /**
     * Ignore rules, access to those addresses does not require a login
     */
    private String ignorePattern;

    /**
     * Custom UrlPatternMatcherStrategy Validation
     */
    private String ignoreUrlPatternType;

    public String getServerUrlPrefix() {
        return serverUrlPrefix;
    }

    public void setServerUrlPrefix(String serverUrlPrefix) {
        this.serverUrlPrefix = serverUrlPrefix;
    }

    public String getServerLoginUrl() {
        return serverLoginUrl;
    }

    public void setServerLoginUrl(String serverLoginUrl) {
        this.serverLoginUrl = serverLoginUrl;
    }

    public String getClientHostUrl() {
        return clientHostUrl;
    }

    public void setClientHostUrl(String clientHostUrl) {
        this.clientHostUrl = clientHostUrl;
    }

    public String getIgnorePattern() {
        return ignorePattern;
    }

    public void setIgnorePattern(String ignorePattern) {
        this.ignorePattern = ignorePattern;
    }

    public String getIgnoreUrlPatternType() {
        return ignoreUrlPatternType;
    }

    public void setIgnoreUrlPatternType(String ignoreUrlPatternType) {
        this.ignoreUrlPatternType = ignoreUrlPatternType;
    }
}

2. Configs.java

import org.jasig.cas.client.authentication.AuthenticationFilter;
import org.jasig.cas.client.session.SingleSignOutFilter;
import org.jasig.cas.client.session.SingleSignOutHttpSessionListener;
import org.jasig.cas.client.util.HttpServletRequestWrapperFilter;
import org.jasig.cas.client.validation.Cas30ProxyReceivingTicketValidationFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.EventListener;
import java.util.HashMap;
import java.util.Map;

@Configuration
@EnableConfigurationProperties(CasProperties.class)
public class Configs {

    @Autowired
    private CasProperties configProps;

    /**
     * Configure logout filters
     * @return
     */
    @Bean
    public FilterRegistrationBean filterSingleRegistration() {
        final FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new SingleSignOutFilter());
        // Set Matching Path
        registration.addUrlPatterns("/*");
        Map<String,String>  initParameters = new HashMap<String, String>();
        initParameters.put("casServerUrlPrefix", configProps.getServerUrlPrefix());
        registration.setInitParameters(initParameters);
        // Set Loading Order
        registration.setOrder(1);
        return registration;
    }

    /**
     * Configure filter validator Cas30ProxyReceivingTicketValidation Filter is used here
     * @return
     */
    @Bean
    public FilterRegistrationBean filterValidationRegistration() {
        final FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new Cas30ProxyReceivingTicketValidationFilter());
        // Set Matching Path
        registration.addUrlPatterns("/*");
        Map<String,String> initParameters = new HashMap<String, String>();
        initParameters.put("casServerUrlPrefix", configProps.getServerUrlPrefix());
        initParameters.put("serverName", configProps.getClientHostUrl());
        initParameters.put("useSession", "true");
        registration.setInitParameters(initParameters);
        // Set Loading Order
        registration.setOrder(2);
        return registration;
    }

    /**
     * Configure Authorization Filter
     * @return
     */
    @Bean
    public FilterRegistrationBean filterAuthenticationRegistration() {
        final FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new AuthenticationFilter());
        // Set Matching Path
        registration.addUrlPatterns("/*");
        Map<String,String>  initParameters = new HashMap<String, String>();
        initParameters.put("casServerLoginUrl", configProps.getServerLoginUrl());
        initParameters.put("serverName", configProps.getClientHostUrl());

        if(configProps.getIgnorePattern() != null && !"".equals(configProps.getIgnorePattern())){
            initParameters.put("ignorePattern", configProps.getIgnorePattern());
        }

        //Customize UrlPatternMatcherStrategy validation rules
        if(configProps.getIgnoreUrlPatternType() != null && !"".equals(configProps.getIgnoreUrlPatternType())){
            initParameters.put("ignoreUrlPatternType", configProps.getIgnoreUrlPatternType());
        }

        registration.setInitParameters(initParameters);
        // Set Loading Order
        registration.setOrder(3);
        return registration;
    }

    /**
     * request wraper Filter
     * @return
     */
    @Bean
    public FilterRegistrationBean filterWrapperRegistration() {
        final FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new HttpServletRequestWrapperFilter());
        // Set Matching Path
        registration.addUrlPatterns("/*");
        // Set Loading Order
        registration.setOrder(4);
        return registration;
    }

    /**
     * Add listener
     * @return
     */
    @Bean
    public ServletListenerRegistrationBean<EventListener> singleSignOutListenerRegistration(){
        ServletListenerRegistrationBean<EventListener> registrationBean = new ServletListenerRegistrationBean<EventListener>();
        registrationBean.setListener(new SingleSignOutHttpSessionListener());
        registrationBean.setOrder(1);
        return registrationBean;
    }
}

Logout address: https://www.cainiao.com 8443/cas/logout, exit server.
Accessing the client again found that it automatically jumped to the login page, that is, the client automatically quit successfully.

Some questions

At first I wanted to add a certificate to the client and access it using https.Avoid making modifications on the server side to support http,
When I add a certificate, single sign-on works fine, but the sign-out function always fails, with the server exiting and the client not exiting.
I always thought there was a problem with the logout of the client configuration, and it did not work for half a day. Then I removed the client's certificate and it succeeded.Imagine that the certificate we generated could not be approved by the server, because the server broadcasts to the client when it logs out, whereas the HTTPS and IMAPS-10000001.json file we modified earlier only acts on client-to-server requests.Contrary to logout.

When I used the spring boot configuration certificate, none of the spring-boot-starter-parent versions above 2.1.0.RELEASE worked.Errors will be reported.

Tags: Java SSL JDBC Database

Posted on Sun, 21 Jun 2020 14:05:25 -0400 by bonzie