SpringBoot 实现CAS Server统一登录认证
作者:mmseoamin日期:2024-01-19

SpringBoot 集成CAS Server

一、CAS Service服务介绍

​ CAS(Central Authentication Service)中心授权服务,是一个开源项目,目的在于为Web应用系统提供一种可靠的单点登录。

​ 在整个认证的流程中的整个流程大概是:首先由CAS Client(我们的客户端应用)发起请求,CAS Client 会重定向到CAS Server进行登录,CAS Server进行账户校验且多个CAS Client 之间可以共享登录的 session ,Server 和 Client 是一对多的关系。基于CAS的SSO访问流程步骤:

  1. 访问服务: CAS Client 客户端发送请求访问应用系统提供的服务资源。
  2. 定向认证: CAS Client 客户端会重定向用户请求到 CAS Server 服务器。
  3. 用户认证: 用户在浏览器端输入用户验证信息,CAS Server服务端完成用户身份认证。
  4. 发放票据: CAS Server服务器会产生一个随机的 Service Ticket 。
  5. 验证票据: CAS Server服务器验证票据 Service Ticket 的合法性,验证通过后,允许客户端访问服务。
  6. 传输用户信息: CAS Server 服务器验证票据通过后,传输用户认证结果信息给客户端。

SpringBoot 实现CAS Server统一登录认证,在这里插入图片描述,第1张

​ 从结构上看,CAS 包含两个部分: CAS Server 和 CAS Client 。 CAS Server 需要独立部署,主要负责对用户的认证工作; CAS Client 负责处理对客户端受保护资源的访问请求,需要登录时,重定向到 CAS Server。

​ CAS Client 与受保护的客户端应用部署在一起,以 Filter 方式保护受保护的资源。对于访问受保护资源的每个 Web 请求, CAS Client 会分析该请求的 Http 请求中是否包含 Service Ticket。如果没有,则说明当前用户尚未登录,于是将请求重定向到指定好的 CAS Server 登录地址,并传递 Service (也就是要访问的目的资源地址),以便登录成功过后转回该地址。

​ 在流程图中的第三步输入认证信息,登陆成功后,CAS Server 随机产生一个相当长度、唯一、不可伪造的 Service Ticket,并缓存以待将来验证。之后系统自动重定向到 Service 所在地址,并为客户端浏览器设置一个 Ticket Granted Cookie(TGC),CAS Client 在拿到 Service 和新产生的 Ticket 过后,在第 5,6 步中与 CAS Server 进行身份核实,以确保 Service Ticket 的合法性。

二、CAS Server服务的搭建

2.1 下载cas-overlay-template

​ 这里为大家提供一个 5.1版本的 git地址:

5.3版本的git地址

网上都能找到很多下载方式的,或者私信我 我测试使用的是cas-overlay-template-5.3

​ 获取到项目后zip的方式解压出来后的目录如下:

SpringBoot 实现CAS Server统一登录认证,在这里插入图片描述,第2张

2.2 使用外部Tomcat部署CAS Server

  • 解压出来文件夹之后,就可以进行打包运行了。在解压的目录下打开命令行 到安装目录下 使用build.cmd run 来进行编译打包。过程可能需要花点时间,

    SpringBoot 实现CAS Server统一登录认证,在这里插入图片描述,第3张

    • 在打包完成之后就会启动我们的CAS Server,但是服务现在是没有正常启动的,因为Cas server 配置证书路径是基于linux的,而我们是在windows环境下部署,目录结构不一致导致无法找到相应的文件,如果是linux环境的话就可以成功启动了。

      SpringBoot 实现CAS Server统一登录认证,在这里插入图片描述,第4张

      ​ 启动失败:

      SpringBoot 实现CAS Server统一登录认证,在这里插入图片描述,第5张

      打包完成之后会在解压的目录下面多了target文件夹:

      SpringBoot 实现CAS Server统一登录认证,在这里插入图片描述,第6张

      • 我们将Cas.war(或者直接使用Cas文件夹) 复制到本机的 Tomcat 的 webapp目录中,启动Tomcat即可

        SpringBoot 实现CAS Server统一登录认证,在这里插入图片描述,第7张

        启动Tomcat,通过Tomcat日志就可以看见我们的CAS Server是否启动成功了:

        SpringBoot 实现CAS Server统一登录认证,在这里插入图片描述,第8张

        然后我们就能访问部署到本地CAS Server 服务了。通过 127.0.0.1:8080/cas/login 来访问CAS服务,这里的地址需要根据自己的实际情况来定(比如你部署的Tomcat服务默认地址8080是否改变过 等情况):

        SpringBoot 实现CAS Server统一登录认证,在这里插入图片描述,第9张

        至此我们使用外部Tomcat部署CAS Server服务就成功了!

        这里可以使用默认的账户密码(账:casuser 密: Mellon)进行登录,

        至于后续可能出现的一些疑难杂症(包括静态用户的设置、http的支持等),文章下面部门章节会进行介绍。目前先简单的将CAS Server服务部署上去!

        2.3 使用IDEA部署CAS Server

        ​ 在IDEA中我们直接将项目通过maven工具加载pom文件中的jar包和package命令生成运行包target。然后在项目中建立本地项目的src/main/java 和 src/main/resources目录。最后将target包中的/cas/WEB_INF/classes/services和aplication.properties和log4j2.xml以及/cas/WEB_INF/classes/META-INF复制到resources目录中。

        • 首先通过IDEA将解压后的CAS Server文件打开进行打包

          打开文件后,首相将项目的mave配置好(File/Settings/Build,…/Build Tools/Maven 中配置好自己本地的maven地址),然后加载pom文件中的Jar包。

          maven配置好之后利用maven对项目进行打包

          SpringBoot 实现CAS Server统一登录认证,在这里插入图片描述,第10张

          • 移动相关配置文件

            项目打包完成之后,在项目中创建Java文件夹和resources文件夹,将上述提到的target文件中的4个文件复制到我们自己创建的resources目录下

            SpringBoot 实现CAS Server统一登录认证,在这里插入图片描述,第11张

          • 给CAS Server 项目配置tomcat服务

            首先给CAS Server 添加tamcat服务器

            SpringBoot 实现CAS Server统一登录认证,在这里插入图片描述,第12张

            ​ 配置好Tomcat服务的地址 以及 端口号等基本信息SpringBoot 实现CAS Server统一登录认证,在这里插入图片描述,第13张

            ​ 部署Tomcat服务器

            SpringBoot 实现CAS Server统一登录认证,在这里插入图片描述,第14张

            SpringBoot 实现CAS Server统一登录认证,在这里插入图片描述,第15张

            选择war exploded模式进行部署我们的Tomcat,选择之后,将下面的 Application context 基路径中的数据改为 / 即可。

            • 为CAS Server 配置JDK

              打开 File/Project Stucture/Project中进行设置

              SpringBoot 实现CAS Server统一登录认证,在这里插入图片描述,第16张

              所有配置都准备完毕之后 我们就可以启动Tomcat了,tomcat启动之后就能够正常访问到我们的CAS Server服务了!

              SpringBoot 实现CAS Server统一登录认证,在这里插入图片描述,第17张

              通过 127.0.0.1:8080/login 即可访问到我们的CAS Service。这里可以使用默认的账户密码(账:casuser 密: Mellon)进行登录。

              推出登录的地址: 127.0.0.1:8080/logout

              SpringBoot 实现CAS Server统一登录认证,在这里插入图片描述,第18张

              三、CAS Server的其他配置

              3.1 CAS Server 去掉https验证

              ​ 这里这样设置的目的是为了,后续我们通过项目去请求CAS Server 服务时,能够通过 http 的方式去访问我们的CAS Server服务!

              ​ 在CAS Server服务 4.2版本时对整体的架构进行了一个优化。

              允许Http访问CAS Server 的配置设置:

              • application.properties 文件中的最后一行配置
                #忽略https安全协议,使用 HTTP 协议
                cas.tgc.secure=false
                
                • src/main/resources/services中的 HTTPSandIMAPS-10000001.json文件
                  //原数据
                  {
                    "@class" : "org.apereo.cas.services.RegexRegisteredService",
                    "serviceId" : "^(https|imaps)://.*",
                    "name" : "HTTPS and IMAPS",
                    "id" : 10000001,
                    "description" : "This service definition authorizes all application urls that support HTTPS and IMAPS protocols.",
                    "evaluationOrder" : 10000
                  }
                  // 需要将  "serviceId" : "^(https|imaps)://.*",
                  //修该成为:"serviceId" : "^(https|imaps|http)://.*" 即可!
                  
                  如果你的CAS Server 服务的版本号在4.2 以下的,可以在去查询一下配置方法,这里就不在记录 4.2版本以下的修改方式!
                  

                  3.2 静态认证用户的添加

                  ​ 静态认证用户是通过 WEb-INF\classes\application.properties 文件中去配置的SpringBoot 实现CAS Server统一登录认证,在这里插入图片描述,第19张

                  ​ 在配置文件中,我们的认证用户数量可以添加配置多个,如上图所示,就配置了两个认证用户。

                  ​ 如果你是通过IDEA来实现的CAS Server 服务的部署,那么只需要修改 main/resoources/application.properties文件。

                  3.3 配置数据库查询认证用户

                  • 首先是在maven中导入相关依赖jar
                     
                            
                                org.apereo.cas
                                cas-server-support-jdbc
                                6.5.0
                            
                            
                                org.apereo.cas
                                cas-server-support-jdbc-drivers
                                6.5.0
                            
                            
                            
                                mysql
                                mysql-connector-java
                                8.0.17
                            
                        
                    
                    • 在通过application.properties文件中添加下面配置
                      # 注释静态验证的配置
                      cas.tgc.secure=false
                      cas.serviceRegistry.initFromJson=true
                       
                      #加密迭代次数
                      cas.authn.jdbc.encode[0].numberOfIterations=3
                      #该列名的值可替代上面的值,但对密码加密时必须取该值进行处理
                      cas.authn.jdbc.encode[0].numberOfIterationsFieldName=
                      #盐值固定列
                      cas.authn.jdbc.encode[0].saltFieldName=account
                      #静态盐值
                      cas.authn.jdbc.encode[0].staticSalt=.
                      cas.authn.jdbc.encode[0].sql=SELECT * FROM user WHERE account =?
                      #对处理盐值后的算法
                      cas.authn.jdbc.encode[0].algorithmName=MD5
                      cas.authn.jdbc.encode[0].passwordFieldName=password
                      cas.authn.jdbc.encode[0].expiredFieldName=expired
                      cas.authn.jdbc.encode[0].disabledFieldName=disabled
                      #数据库连接
                      cas.authn.jdbc.encode[0].url=jdbc:mysql://127.0.0.1:3306/test?useSSL=false&useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&characterEncoding=UTF-8
                      #cas.authn.jdbc.encode[0].dialect=org.hibernate.dialect.MySQL5Dialect
                      cas.authn.jdbc.encode[0].driverClass=com.mysql.jdbc.Driver
                      cas.authn.jdbc.encode[0].user=root
                      cas.authn.jdbc.encode[0].password=123456
                      

                      同时我们需要注释掉 之前配置在application.properties文件中的 静态认证用户,即: cas.auth.accept.users=xxxx数据需要注释掉。

                      3.4 解决未认证授权的服务

                      ​ 在我们的CAS Client 客户端服务,跳转到CAS Server 进行用户授权登录认证时,我们的CAS Server 服务提示:

                      “未认证授权的服务

                      CAS的服务记录是空的,没有定义服务。 希望通过CAS进行认证的应用程序必须在服务记录中明确定义。”

                      ​ 出现这种情况的原因是,我们的CAS Server 服务端中还没有定义对应的服务,也就是我们的应用服务(客户端),需要在CAS Server 服务端进行记录信息,这样才能通过Client客户端跳转到我CAS Server 服务端来进行用户认证。

                      ​ 对应客户端在CAS Server 服务端中是否注册成功,通过我们的CAS Server 服务启动时Tomcat的日志也能看出来。

                      ​ 没有CAS Client 客户端注册的情况下的日志:

                      2023-11-09 16:37:59,935 INFO [org.apereo.cas.services.AbstractServicesManager] -

                      2023-11-09 16:38:59,947 INFO [org.apereo.cas.services.AbstractServicesManager] -

                      ​ CAS Server中注册了CAS Client客户端时,Tomcat的启动日志:

                      2023-11-09 16:43:15,594 INFO [org.apereo.cas.ticket.registry.DefaultTicketRegistryCleaner] - <[0] expired tickets removed.>

                      2023-11-09 16:44:05,568 INFO [org.apereo.cas.services.AbstractServicesManager] -

                      2023-11-09 16:45:05,573 INFO [org.apereo.cas.services.AbstractServicesManager] -

                      • 详细的配置过程:

                        Client Server 的配置位置: 在 HTTPSandIMAPS-10000001.json 文件中配置我们的客户端信息。

                        {
                          "@class" : "org.apereo.cas.services.RegexRegisteredService",
                          "serviceId" : "^(https|http|imaps)://.*",
                          "name" : "HTTPS and IMAPS",
                          "id" : 10000001,
                          "description" : "This service definition authorizes all application urls that support HTTPS and IMAPS protocols.",
                          "evaluationOrder" : 10000
                        }
                        

                        配置完json数据后,还需要改动application.properties 文件中的信息,在改配置文件中添加下面两行数据,这样才能使得我们的CAS Server服务端能够读取到配置的客户端信息。

                        #是否开启json识别功能,默认为false
                        cas.serviceRegistry.initFromJson=true
                        #忽略https安全协议,使用 HTTP 协议
                        cas.tgc.secure=false
                        

                        上面两个配置完成之后,重启Tomcat服务。

                        SpringBoot 实现CAS Server统一登录认证,在这里插入图片描述,第20张

                        通过Tomcat日志可以看出,我们的配置已经生效,CAS Server服务端已经读取到配置文件中的客户端。

                        三、SpringBoot集成cas-client-core实现CAS认证

                        ​ 在部署完CAS Server 认证服务端之后,我们就需要通过CAS Client客户端集成CAS 服务实现集成认证了。在SpringBoot 中 可以通过集成CAS-Client即可对Cas认证进行集成。

                        集成这部分文章引荐:https://blog.csdn.net/uziuzi669/article/details/119486588

                        • 引入POM依赖

                          这里需要根据自己部署的CAS Server 服务版本选择合适的依赖版本

                          
                          
                              org.jasig.cas.client
                              cas-client-core
                              3.5.0
                          
                          
                          • CAS集成的核心配置类
                            package com.wxxssf.CasAuthLogin.casConfig;
                            import lombok.extern.slf4j.Slf4j;
                            import org.apache.commons.lang3.StringUtils;
                            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.Cas20ProxyReceivingTicketValidationFilter;
                            import org.jasig.cas.client.validation.Cas30ProxyReceivingTicketValidationFilter;
                            import org.springframework.beans.factory.annotation.Value;
                            import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
                            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 org.springframework.core.Ordered;
                            /**
                             * @Description: cas集成核心配置类
                             * @ClassName: CasConfig
                             */
                            @Configuration
                            @Slf4j
                            @ConditionalOnProperty(value ="cas.validation-type",havingValue = "cas") //根据应用程序配置文件中的属性值来控制Bean的创建和加载
                            public class CasConfig {
                                /**
                                *@Description 需要走cas拦截器的地址
                                */
                                @Value("${cas.urlPattern:/cas/loginByNameAndCardNo}")
                                private String filterUrl;
                                /**
                                 * 默认的cas地址,防止通过 配置信息获取不到,CAS服务端的登录地址,login为固定值
                                 */
                                @Value("${cas.server-url-prefix:https://ciap7.wisedu.com/authserver/login}")
                                private String casServerUrl;
                                /**
                                 * 应用校验访问地址(这个地址需要在cas服务端进行配置)
                                 */
                                @Value("${cas.authentication-url:https://ciap7.wisedu.com/authserver}")
                                private String authenticationUrl;
                                /**
                                 * 应用访问地址(这个地址需要在cas服务端进行配置)
                                 */
                                @Value("${cas.client-host-url:http://localhost:8090}")
                                private String appServerUrl;
                                @Bean
                                public ServletListenerRegistrationBean servletListenerRegistrationBean() {
                                    log.info(" servletListenerRegistrationBean  \n cas 单点登录配置 \n appServerUrl = " + appServerUrl + "\n casServerUrl = " + casServerUrl);
                                    ServletListenerRegistrationBean listenerRegistrationBean = new ServletListenerRegistrationBean();
                                    listenerRegistrationBean.setListener(new SingleSignOutHttpSessionListener());
                                    listenerRegistrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
                                    return listenerRegistrationBean;
                                }
                                /**
                                 * 单点登录退出
                                 */
                                @Bean
                                public FilterRegistrationBean singleSignOutFilter() {
                                    log.info(" servletListenerRegistrationBean ");
                                    FilterRegistrationBean registrationBean = new FilterRegistrationBean();
                                    registrationBean.setFilter(new SingleSignOutFilter());
                                    registrationBean.addUrlPatterns(filterUrl);
                                    registrationBean.addInitParameter("casServerUrlPrefix", casServerUrl);
                                    registrationBean.setName("CAS Single Sign Out Filter");
                                    registrationBean.setOrder(1);
                                    return registrationBean;
                                }
                                /**
                                 * 单点登录认证
                                 */
                                @Bean
                                public FilterRegistrationBean AuthenticationFilter() {
                                    log.info(" AuthenticationFilter ");
                                    FilterRegistrationBean registrationBean = new FilterRegistrationBean();
                                    registrationBean.setFilter(new AuthenticationFilter());
                                    registrationBean.addUrlPatterns(filterUrl);
                                    registrationBean.setName("CAS Filter");
                                    registrationBean.addInitParameter("casServerLoginUrl", casServerUrl);
                                    registrationBean.addInitParameter("serverName", appServerUrl);
                                    registrationBean.setOrder(1);
                                    return registrationBean;
                                }
                                /**
                                 * 决定票据验证过滤器的版本,默认30,old是20版
                                 */
                                @Value("${cas.filterVersion:new}")
                                private String filterVersion;
                                /**
                                 * 单点登录校验
                                 */
                                @Bean
                                public FilterRegistrationBean Cas30ProxyReceivingTicketValidationFilter() {
                                    FilterRegistrationBean registrationBean = new FilterRegistrationBean();
                                    if (StringUtils.isNotBlank(filterVersion) && filterVersion.equals("old")){
                                        log.info(" Cas20ProxyReceivingTicketValidationFilter ");
                                        registrationBean.setFilter(new Cas20ProxyReceivingTicketValidationFilter());
                                    }else {
                                        log.info(" Cas30ProxyReceivingTicketValidationFilter ");
                                        registrationBean.setFilter(new Cas30ProxyReceivingTicketValidationFilter());
                                    }
                                    registrationBean.addUrlPatterns(filterUrl);
                                    registrationBean.setName("CAS Validation Filter");
                                    registrationBean.addInitParameter("casServerUrlPrefix", authenticationUrl);
                                    registrationBean.addInitParameter("serverName", appServerUrl);
                                    registrationBean.setOrder(1);
                                    return registrationBean;
                                }
                                /**
                                 * 单点登录请求包装
                                 */
                                @Bean
                                public FilterRegistrationBean httpServletRequestWrapperFilter() {
                                    log.info(" httpServletRequestWrapperFilter ");
                                    FilterRegistrationBean registrationBean = new FilterRegistrationBean();
                                    //registrationBean.setFilter(new HttpServletRequestWrapperFilter());
                                    registrationBean.setFilter(new HttpServletRequestWrapperFilter());
                                    registrationBean.addUrlPatterns(filterUrl);
                                    registrationBean.setName("CAS HttpServletRequest Wrapper Filter");
                                    registrationBean.setOrder(1);
                                    return registrationBean;
                                }
                            }
                            

                            ​ 上面的单点登录校验器,需要我们创建票据验证 TicketValidationFiter ,但是需要注意的是票据验证过滤器有两种类型分别是:Cas30ProxyReceivingTicketValidationFilter 和 Cas20ProxyReceivingTicketValidationFilter。

                            ​ 在认证票据是会出现报错的情况,这时候就需要考虑这个票据验证器TicketValidationFiter的版本问题,CAS Server 的版本是否兼容Filter,从而引起冲突问题,具体使用哪一种票据验证器,需要根据实际情况去调整,这个票据验证器的选择也是通过application.properties配置文件中去实现的动态选择。

                            • 上面的各个连接地址是通过application.properties文件进行动态配置读取的,示例如下:
                              #===Cas集成认证===
                              #需要走拦截器的地址 /api/loginByNameAndCardNo :验票拦截路径
                              cas.urlPattern = /cas/loginByNameAndCardNo
                              # 客户端如果要登录,会跳转到CAS服务端的登录地址(认证地址) : 认证中心登录页面地址   
                              # http://192.168.0.145:8080/cas/login
                              cas.server-url-prefix = http://192.168.0.145:8080/cas/login
                              # CAS 服务端地址(认证平台地址):认证中心地址  
                              # http://192.168.0.145:8080/cas
                              cas.authentication-url = http://192.168.0.145:8080/cas
                              # 客户端在CAS服务端登录成功后,自动从CAS服务端跳转回客户端的地址 :应用地址,也就是自己的系统地址。 https://cwfw.mtxy.edu.cn
                              cas.client-host-url = http://192.168.0.145:8998
                              # Ticket校验器使用 Cas30ProxyReceivingTicketValidationFilter :动态开启 cas 单点登录
                              cas.validation-type = cas
                              # 验票器版本
                              cas.filterVersion = new
                              
                              • CAS认证用户信息Vo类
                                package com.wxxssf.CasAuthLogin.Vo;
                                import lombok.Setter;
                                import java.util.Map;
                                /**
                                 * @Description: Cas认证用户信息
                                 */
                                @Getter
                                @Setter
                                public class CasUserInfo {
                                    /** 用户名 */
                                    private String userName;
                                    /** 用户 */
                                    private String userAccount;
                                    /** 用户信息 */
                                    private Map attributes;
                                }
                                
                                • 认证通过后返回数据,获取用户信息的工具类封装
                                  package com.wxxssf.CasAuthLogin.casUtils;
                                  import com.wxxssf.CasAuthLogin.Vo.CasUserInfo;
                                  import lombok.extern.slf4j.Slf4j;
                                  import org.jasig.cas.client.authentication.AttributePrincipal;
                                  import org.jasig.cas.client.validation.Assertion;
                                  import javax.servlet.http.HttpServletRequest;
                                  import java.security.Principal;
                                  import java.util.Map;
                                  /**
                                   * @Description: Cas认证工具类
                                   */
                                  @Slf4j
                                  public class CasUtil {
                                      /**
                                       * cas client 默认的session key  _const_cas_assertion_
                                       */
                                      public final static String CAS = "_const_cas_assertion_";
                                      /**
                                       * 封装CasUserInfo
                                       */
                                      public static CasUserInfo getCasUserInfoFromCas(HttpServletRequest request) {
                                          System.out.println("request.toString() = " + request.toString());
                                          Object object = request.getSession().getAttribute(CAS);
                                          if (null == object) {
                                              return null;
                                          }
                                          Assertion assertion = (Assertion) object;
                                          return buildCasUserInfoByCas(assertion);
                                      }
                                      /**
                                       * 构建CasUserInfo
                                       */
                                      private static CasUserInfo buildCasUserInfoByCas(Assertion assertion) {
                                          if (null == assertion) {
                                              log.error(" Cas没有获取到用户 ");
                                              return null;
                                          }
                                          CasUserInfo casUserInfo = new CasUserInfo();
                                          String userName = assertion.getPrincipal().getName();
                                          log.info(" cas对接登录用户= " + userName);
                                          log.info("用户消息:"+assertion.getPrincipal().toString());
                                          casUserInfo.setUserAccount(userName);
                                          //获取属性值
                                          Map attributes = assertion.getPrincipal().getAttributes();
                                          Object name = attributes.get("cn");
                                          casUserInfo.setUserName(name == null ? userName : name.toString());
                                          casUserInfo.setAttributes(attributes);
                                          return casUserInfo;
                                      }
                                      /**
                                       * @Description new:获取用户信息全部数据展示示例:
                                       *
                                       * userInfo = {
                                       * "userAccount":"20220037",
                                       * "attributes":{"isFromNewLogin":"false",
                                       *      "authenticationDate":"2023-07-27T09:15:46.799+08:00[GMT+08:00]",
                                       *      "loginType":"1",
                                       *      "successfulAuthenticationHandlers":"com.wisedu.minos.config.login.RememberMeUsernamePasswordHandler",
                                       *      "cn":"xxx",
                                       *      "userName":"xxx",
                                       *      "samlAuthenticationStatementAuthMethod":"urn:oasis:names:tc:SAML:1.0:am:unspecified",
                                       *      "credentialType":"MyRememberMeCaptchaCredential",
                                       *      "uid":"20220037",
                                       *      "authenticationMethod":"com.wisedu.minos.config.login.RememberMeUsernamePasswordHandler",
                                       *      "longTermAuthenticationRequestTokenUsed":"false",
                                       *      "containerId":"ou=1000001,ou=People",
                                       *      "cllt":"userNameLogin",
                                       *      "dllt":"generalLogin"},
                                       * "userName":"xxx"}
                                      */
                                      public static CasUserInfo getUserInfo(HttpServletRequest request){
                                          Principal userPrincipal = request.getUserPrincipal();
                                          CasUserInfo casUserInfo = new CasUserInfo();
                                          if (userPrincipal != null && userPrincipal instanceof AttributePrincipal){
                                              AttributePrincipal attributePrincipal = (AttributePrincipal) userPrincipal;
                                              //获取用户信息中公开的Attributes部分
                                              Map map = attributePrincipal.getAttributes();
                                              String cn = (String)map.get("cn");
                                              String user_name = (String)map.get("userName");
                                              casUserInfo.setUserAccount(userPrincipal.getName());
                                              casUserInfo.setAttributes(map);
                                              casUserInfo.setUserName(cn == null ? userPrincipal.getName() : cn );
                                          }
                                          return casUserInfo;
                                      }
                                      // TODO: 2023-07-24   AttributePrincipal类和Assertion  区别~~!!
                                  }
                                  
                                  • 单点登录接口
                                    /**
                                       * cas 单点登录
                                       *
                                       * @param request 请求头(姓名+身份证号)
                                       * @param ticket cas 票据
                                       * @return
                                       */
                                      @GetMapping(value = "/api/loginByNameAndCardNo")
                                      @ApiOperation("cas单点登录")
                                      public String loginByNameAndCardNo(HttpServletRequest request) {
                                        CasUserInfo userInfo = CasUtil.getCasUserInfoFromCas(request);
                                        log.info("userInfo = " + JSONObject.toJSON(userInfo));
                                        String url = "main";
                                        MadStudent student = new MadStudent();
                                        student.setName(userInfo.getAttributes().get("Name").toString());
                                        student.setCardNo(userInfo.getAttributes().get("IdCard").toString());
                                      	// 登录用户校验 
                                      	// xxxxx
                                      	// 用户数据为 true
                                      	// 跳转页面
                                        return "url";
                                        } else {
                                          return "redirect:" + casUrl;
                                        }
                                      }
                                    

                                    四、其他方式实现的认证案例记录(略):

                                    ​ 注:该部分内容仅仅为自己记录使用,可跳过!

                                    4.1 基于深信服IDTrust 实现Cas认证

                                    • 方式一:基于JDK的java.net包中已经提供了访问Http协议的 HttpURLConnection 类实现
                                      <%@ page import="java.net.URL" %>
                                      <%@ page import="java.net.URLConnection" %>
                                      <%@ page import="javax.net.ssl.HttpsURLConnection" %>
                                      <%@ page import="java.io.InputStream" %>
                                      <%@ page import="java.io.ByteArrayOutputStream" %>
                                      <%@ page import="java.net.HttpURLConnection" %>
                                      <%@ page import="javax.net.ssl.SSLSession" %>
                                      <%@ page import="javax.net.ssl.HostnameVerifier" %>
                                      <%@ page import="java.net.URLEncoder" %><%--
                                        Created by IntelliJ IDEA.
                                        User: AD
                                        To change this template use File | Settings | File Templates.
                                      --%>
                                      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
                                      <%!
                                          private static void trustAllHttpsCertificates() throws Exception {
                                              javax.net.ssl.TrustManager[] trustAllCerts = new javax.net.ssl.TrustManager[1];
                                              javax.net.ssl.TrustManager tm = new miTM();
                                              trustAllCerts[0] = tm;
                                              javax.net.ssl.SSLContext sc = javax.net.ssl.SSLContext
                                                      .getInstance("SSL");
                                              sc.init(null, trustAllCerts, null);
                                              javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(sc
                                                      .getSocketFactory());
                                          }
                                          static class miTM implements javax.net.ssl.TrustManager,
                                                  javax.net.ssl.X509TrustManager {
                                              public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                                                  return null;
                                              }
                                              public boolean isServerTrusted(
                                                      java.security.cert.X509Certificate[] certs) {
                                                  return true;
                                              }
                                              public boolean isClientTrusted(
                                                      java.security.cert.X509Certificate[] certs) {
                                                  return true;
                                              }
                                              public void checkServerTrusted(
                                                      java.security.cert.X509Certificate[] certs, String authType)
                                                      throws java.security.cert.CertificateException {
                                                  return;
                                              }
                                              public void checkClientTrusted(
                                                      java.security.cert.X509Certificate[] certs, String authType)
                                                      throws java.security.cert.CertificateException {
                                                  return;
                                              }
                                          }
                                      %>
                                      <%
                                          String infoMessage =null;
                                          HostnameVerifier hv = new HostnameVerifier() {
                                              public boolean verify(String urlHostName, SSLSession session) {
                                                  String infoMessage ="Warning: URL Host: " + urlHostName + " vs. " + session.getPeerHost();
                                                  System.out.println("info = " + infoMessage);
                                                  return true;
                                              }
                                          };
                                          trustAllHttpsCertificates();
                                          HttpsURLConnection.setDefaultHostnameVerifier(hv);
                                      	String service = "http://xxxxx:xx/casLoginTicket.jsp"; //回调地址
                                          String encode = URLEncoder.encode(service); 
                                          URL url = new URL("https://xxxx/cas/login?service="+encode);  //认证地址
                                          HttpsURLConnection httpsURLConnection = (HttpsURLConnection) url.openConnection();
                                          httpsURLConnection.setDoInput(true);
                                          httpsURLConnection.setRequestMethod("GET");
                                          httpsURLConnection.setRequestProperty("Content-Type","application/json;charset=utf-8");
                                          System.out.println("准备执行李连接!!");
                                          httpsURLConnection.connect();
                                          InputStream inputStream = httpsURLConnection.getInputStream();
                                          byte[] buff = new byte[1024];
                                          int len = -1;
                                          StringBuffer stringBuffer = new StringBuffer();
                                          ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                                          while((len = inputStream.read(buff)) != -1){
                                              stringBuffer.append(new String(buff,0,len,"utf-8"));
                                              byteArrayOutputStream.write(buff,0,len);
                                          }
                                          System.out.println("stringBuffer = " + stringBuffer);
                                          System.out.println("byteArrayOutputStream.toString() = " + byteArrayOutputStream.toString());
                                          //关闭资源
                                          byteArrayOutputStream.close();
                                          inputStream.close();
                                          httpsURLConnection.disconnect();
                                          //response.getWriter().write( stringBuffer.toString());
                                      %>
                                      
                                      
                                          加载中请稍等....
                                      
                                      
                                      

                                      <%=encode%>

                                      <%=stringBuffer%>

                                      • 方式二:基于cn.hutool 的http 包中已经提供了访问Http协议的 Hutool-http 类实现
                                        <%@ page import="cn.hutool.http.HttpRequest" %>
                                        <%@ page import="cn.hutool.http.HttpResponse" %>
                                        <%@ page import="java.net.URLEncoder" %>
                                        <%--
                                          Created by IntelliJ IDEA.
                                          User: AD
                                          To change this template use File | Settings | File Templates.
                                        --%>
                                        <%
                                            System.out.println("进入集成跳转页面!!");
                                            String service = "http://xxxx:8888/casLoginTicket.jsp";  //回调地址
                                            String encode = URLEncoder.encode(service);
                                            HttpResponse result = HttpRequest
                                                    .get("https://xxxx/cas/login?service="+encode) //CAS认证地址
                                                    .header("Content-Type", "application/json;charset=UTF-8")  
                                                    .timeout(60 * 1000)
                                                    .execute();
                                            int status = result.getStatus();
                                            System.out.println("status = " + status);
                                            String body = result.body();
                                            System.out.println("body = " + body);
                                            response.getWriter().write(body);
                                        %>
                                        <%@ page contentType="text/html;charset=UTF-8" language="java" %>
                                        
                                        
                                            加载中请稍等....
                                        
                                        
                                        

                                        <%=status%>

                                        <%=body%>

                                        4.2 基于职教云平台的统一认证案例

                                        • 统一认证访问主路径 jsp文件 (该平台的认证是需要提前申请入驻,然后绑定对应回调地址和认证地址之后才能进行认证)
                                          <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
                                          <%--<%@ page import="org.apache.commons.lang.StringUtils"%>--%>
                                          <%--<%@ page import="com.ibatis.sqlmap.client.SqlMapClient,com.ufgov.midas.yy.util.*,java.util.*"%>--%>
                                          <%--<%@ page import="com.ufgov.midas.qx.util.*,com.ufgov.midas.qx.sqlmap.*,java.sql.*,com.ufgov.midas.pt.common.DAOFactory" %>--%>
                                          <%--<%@ page import="org.ly.uap.client.authentication.AttributePrincipal"%>--%>
                                          <%@ page import="sun.misc.*"%>
                                          <%--应用访问主路径jsp--%>
                                          <%
                                          	//认证地址:
                                              String url ="https://xxxxxxx/provider/oauth2/authorize?" +
                                                      "response_type=code" +
                                                      "&client_id=XXXX"+
                                                      "&redirect_uri=http://XXXXX:8888/ASXYSSOLoginSuccessNEW.jsp"+
                                                      "&scope=openid";
                                              //response.sendRedirect(url);
                                          %>