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:
- Generate Key
- Generate Certificate
- 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.