SpringBoot 实现CAS Server统一登录认证的详细步骤

 更新时间:2024年02月07日 10:42:28   作者:王维诗里的代码i  
​ CAS(Central Authentication Service)中心授权服务,是一个开源项目,目的在于为Web应用系统提供一种可靠的单点登录,这篇文章主要介绍了SpringBoot 实现CAS Server统一登录认证,需要的朋友可以参考下

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访问流程步骤:

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

从结构上看,CAS 包含两个部分: CAS ServerCAS 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地址:<https://github.com/apereo/cas-overlay-template/tree/5.1

5.3版本的git地址

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

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

2.2 使用外部Tomcat部署CAS Server

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

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

​ 启动失败:

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

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

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

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

至此我们使用外部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对项目进行打包

移动相关配置文件

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

给CAS Server 项目配置tomcat服务

首先给CAS Server 添加tamcat服务器

​ 配置好Tomcat服务的地址 以及 端口号等基本信息

​ 部署Tomcat服务器

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

  • 为CAS Server 配置JDK

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

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

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

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

三、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 文件中去配置的

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

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

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

首先是在maven中导入相关依赖jar

 <dependencies>
        <dependency>
            <groupId>org.apereo.cas</groupId>
            <artifactId>cas-server-support-jdbc</artifactId>
            <version>6.5.0</version>
        </dependency>
        <dependency>
            <groupId>org.apereo.cas</groupId>
            <artifactId>cas-server-support-jdbc-drivers</artifactId>
            <version>6.5.0</version>
        </dependency>
        <!--数据库驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.17</version>
        </dependency>
    </dependencies>

在通过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] - <Loaded [0] service(s) from [InMemoryServiceRegistry].>
2023-11-09 16:38:59,947 INFO [org.apereo.cas.services.AbstractServicesManager] - <Loaded [0] service(s) from [InMemoryServiceRegistry].>

​ 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] - <Loaded [2] service(s) from [InMemoryServiceRegistry].>
2023-11-09 16:45:05,573 INFO [org.apereo.cas.services.AbstractServicesManager] - <Loaded [2] service(s) from [InMemoryServiceRegistry].>

详细的配置过程:

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服务。

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

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

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

集成这部分文章引荐://www.jb51.net/article/226058.htm

引入POM依赖

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

<!--Cas单点登录认证-->
<dependency>
    <groupId>org.jasig.cas.client</groupId>
    <artifactId>cas-client-core</artifactId>
    <version>3.5.0</version>
</dependency>

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<String, Object> 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<String, Object> 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<String, Object> 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());
%>
<html>
<head>
    <title>加载中请稍等....</title>
</head>
<script>
    console.log("加载中....")
</script>
<body>
<h3><%=encode%></h3>
<h3><%=stringBuffer%></h3>
</body>
<script>
</script>
</html>

方式二:基于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" %>
<html>
<head>
    <title>加载中请稍等....</title>
</head>
<script>
    console.log("加载中....")
</script>
<body>
<h3><%=status%></h3>
<h3><%=body%></h3>
</body>
<script>
    console.log("请求响应数据展示:<%=status%>")
</script>
</html>

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);
%>
<head>
    <meta charset="utf-8">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">
    <link href="css/style.css" rel="stylesheet" type="text/css">
    <script type="text/javascript" src="js/jquery-1.4.2.min.js"></script>
    <title>SSO_RZ_Welcome!!!</title>
    <style>
        .button {
           ......
        }
        .button2:hover {
            box-shadow: 0 12px 16px 0 rgba(0,0,0,0.24),0 17px 50px 0 rgba(0,0,0,0.19);
        }
        div{
          ....
        }
    </style>
    <script LANGUAGE="JavaScript">
        var url ="<%=url%>"
        // var urlQd = "https://idaastest.gzzjzhy.com/provider/oauth2/authorize?" +
        //     "response_type=code" +
        //     "&client_id=1688831259003981824"+
        //     "&redirect_uri=http://111.85.31.2:8888/ASXYSSOLoginSuccess.jsp"+
        //     "&scope=????";
        function getAuthCode(){
            // alert("跳转统一认证URL="+url)
            window.location.href=url;
        }
    </script>
</head>
<body style="background: #fff;">
<div>
</div>
</body>
</html>
<script language="javascript">getAuthCode();</script>

4.3 基于钉钉的集成认证登录

首先是认证访问页面代码

<%@ page contentType="text/html; charset=GBK" language="java" import="java.sql.*" errorPage="" %>
<%@ page import="com.alibaba.fastjson.JSONObject"%> 
<%@ page import="com.dingtalk.api.DefaultDingTalkClient"%> 
<%@ page import="com.dingtalk.api.DingTalkClient"%> 
<%@ page import="com.dingtalk.api.request.OapiGettokenRequest"%> 
<%@ page import="com.dingtalk.api.request.OapiUserGetuserinfoRequest"%> 
<%@ page import="com.dingtalk.api.response.OapiGettokenResponse"%> 
<%@ page import="com.dingtalk.api.request.OapiV2UserGetRequest"%> 
<%@ page import="com.dingtalk.api.response.OapiUserGetuserinfoResponse"%> 
<%@ page import="com.taobao.api.ApiException"%> 
<%@ page import="com.dingtalk.api.response.OapiV2UserGetResponse"%> 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html> 
<head>
<%@page import="java.util.Date"%>
<%@page import="java.text.SimpleDateFormat"%>
	<%@ page import="java.util.List" %>
	<%@ page import="java.util.Map" %>
	<%@ page import="javax.lang.model.element.NestingKind" %>
	<script language="javascript" src="./des.js"></script>
<meta http-equiv="Content-Type" content="text/html; charset=GBK" />
<title>xxxx</title>
<style>
html,body {。。。。。}
#info{}
#down-info{。。。。。}
</style>
<SCRIPT LANGUAGE=javascript>
{
	<%
	if(ckey!=""){
	  //com.ufgov.midas.pt.service.changeLogin cl = new com.ufgov.midas.pt.service.changeLogin();
	  //String skey = cl.MD5(yonghu + sj + ip);
	  String skey = (yonghu + sj + ip);
	  Date now=new Date();
	  SimpleDateFormat f=new SimpleDateFormat("yyyyMMdd");
	  String DateStr = f.format(now);
	  skey = skey.toLowerCase();
	  ckey = ckey.toLowerCase();
	  if("".equals(sj)){
		  sj = DateStr;
	  }
	  String cood = request.getParameter("msg");
	  System.out.println("获取到的cood值为=" + cood);
//与钉钉进行交互
	  if(yonghu == "" || yonghu == null){
	   // 获取access_token,注意正式代码要有异常流处理
       DingTalkClient client = new DefaultDingTalkClient("https://xxxxx/gettoken");
       OapiGettokenRequest request1 = new OapiGettokenRequest();
       request1.setAppkey("dingcz1ruaglmqxxxx");
       request1.setAppsecret("nVgvFHLiYiHxxxGmcK-Widi_xp29vIxxxXu71mCkOtxxxxs");
       request1.setHttpMethod("GET");
       OapiGettokenResponse response1 = null;
       try {
           response1 = client.execute(request1);
       } catch (ApiException e) {
           throw new RuntimeException(e);
       }
	   System.out.println("获取用户token:response1.getBody() = " + response1.getBody());
       JSONObject jsonObject =JSONObject.parseObject(response1.getBody());
       String access_token = (String) jsonObject.get("access_token");
//获取用户id
       DingTalkClient client2 = new DefaultDingTalkClient("https://xxxxx/user/getuserinfo");
       OapiUserGetuserinfoRequest request2 = new OapiUserGetuserinfoRequest();
       //todo--SSO单点登录授权码
       String requestAuthCode = cood;
       request2.setCode(requestAuthCode);
       request2.setHttpMethod("GET");
       OapiUserGetuserinfoResponse response2= null;
       try {
           response2 = client2.execute(request2, access_token);
           System.out.println("获取用户id信息info:response2.getBody() = " + response2.getBody());
       } catch (ApiException e) {
        // TODO Auto-generated catch block
            e.printStackTrace();
       }
       // 查询得到当前用户的userId
       // 获得到userId之后应用应该处理应用自身的登录会话管理(session),避免后续的业务交互(前端到应用服务端)每次都要重新获取用户身份,提升用户体验
       String userId = response2.getUserid();
	   System.out.println("userId = " + userId);
//获取用户信息
       String userInfo = null;
       try {
           DingTalkClient client3 = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/user/get");
           OapiV2UserGetRequest req = new OapiV2UserGetRequest();
//           req.setUserid("001");
           req.setUserid(userId);
           req.setLanguage("zh_CN");
           OapiV2UserGetResponse rsp = client3.execute(req, access_token);
           System.out.println("获取用户资料info:rsp.getBody() = " + rsp.getBody());
           userInfo = rsp.getBody();
       } catch (ApiException e) {
           e.printStackTrace();
       //获取userid
		   System.out.println("JSONObject.parseObject(userInfo) = " + JSONObject.parseObject(userInfo));
		   JSONObject result  = (JSONObject) JSONObject.parseObject(userInfo).get("result");
		   System.out.println(result.get("userid"));
		   //yonghu = (String) result.get("userid");
	   // 获取job_number
	   	JSONObject result2  = (JSONObject) JSONObject.parseObject(userInfo).get("result");
		  if (result2 == null){
			  System.out.println("不存在该用户");
		  }else {
			   //yonghu = (String) result2.get("job_number");
			   //工号
			   String number =(String) result2.get("job_number");
			   System.out.println("result2.get(\"job_number\") = " +number );
			   yonghu = number;
		  }
		  //role_list:id
		  JSONObject result3 = (JSONObject) JSONObject.parseObject(userInfo).get("result");
		  List roleList = (List) result3.get("role_list");
		  if (roleList.size()>0){
			  Map map =(Map) roleList.get(0);
			  System.out.println("map="+map);
			  //yonghu = map.get("id").toString();
		  }
	  }
	  %>
	  // if (code==""||code==null){
	  //   alert("免登授权码获取失败!");//免登授权码获取失败
 	 //    closeWin();
 	 //    return false;
	  // } else
	  if("<%=yonghu%>"=="" || "<%=yonghu%>"==null){
		alert("用户信息错误,请重新登录!");//可能是key值不正确
		alert("yonghu="+"<%=yonghu%>");
		closeWin();
		return false;
	 }else{
		alert("yonghu="+"<%=yonghu%>")
		alert("验证成功自动登录!!")
		alert("用户登录名正确!")
	 }
	  <%
	}
	%>
	var uid="<%=yonghu%>";
  	doLogin(productName,uid);
}
</SCRIPT> 
</head>
<body>
	<table border=1 width=100% height=100%><tr><td align="center" valign="middle">
		<div id="u8check">
			<div id="info">	客户端程序, 请稍候...</div>
		</div>
	</td></tr></table>
</body>
</html><script language="javascript">doCallU8();</script>

到此这篇关于SpringBoot 实现CAS Server统一登录认证的文章就介绍到这了,更多相关SpringBoot 统一登录认证内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • mybatis中批量插入的两种方式(高效插入)

    mybatis中批量插入的两种方式(高效插入)

    MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架。这篇文章主要介绍了mybatis中批量插入的两种方式(高效插入)的相关资料,非常不错,具有参考借鉴价值,感兴趣的朋友一起看看吧
    2016-09-09
  • java如何用正则表达式匹配与提取字符串

    java如何用正则表达式匹配与提取字符串

    一位以前的同事在群里面突然发了个需求,要通过正则表达式来取值,下面这篇文章主要给大家介绍了关于java如何用正则表达式匹配与提取字符串的相关资料,需要的朋友可以参考下
    2022-06-06
  • 一文详解Spring加载properties文件的方式

    一文详解Spring加载properties文件的方式

    这篇文章将通过一些示例为大家详细介绍Spring加载properties文件的几种方式,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2022-06-06
  • java读取excel文件的两种方法

    java读取excel文件的两种方法

    这篇文章主要为大家详细介绍了java读取excel文件的两种方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-08-08
  • IDEA设置Maven自动编译model的实现方法

    IDEA设置Maven自动编译model的实现方法

    本文主要介绍了IDEA设置Maven自动编译model的实现方法, maven结构的项目,我们在每次修改代码后都会需要手动编译,本文就可以解决这个问题,感兴趣的可以了解一下
    2023-08-08
  • 阿里面试Nacos配置中心交互模型是push还是pull原理解析

    阿里面试Nacos配置中心交互模型是push还是pull原理解析

    这篇文章主要为大家介绍了阿里面试Nacos配置中心交互模型是push还是pull原理解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07
  • java使用反射访问成员变量的值示例

    java使用反射访问成员变量的值示例

    这篇文章主要介绍了java使用反射访问成员变量的值,结合实例形式分析了java基于反射机制操作类成员变量相关实现技巧,需要的朋友可以参考下
    2019-07-07
  • 详解Java的Hibernate框架中的缓存与二级缓存

    详解Java的Hibernate框架中的缓存与二级缓存

    这篇文章主要介绍了Java的Hibernate框架中的缓存与二级缓存,Hibernate是Java的SSH三大web开发框架之一,需要的朋友可以参考下
    2015-12-12
  • SpringBoot整合Quartz及异步调用的案例

    SpringBoot整合Quartz及异步调用的案例

    Quartz是一个完全由java编写的开源作业调度框架、它的简单易用受到业内人士的一致好评,这篇文章主要介绍了SpringBoot整合Quartz及异步调用,需要的朋友可以参考下
    2023-03-03
  • springboot结合redis实现搜索栏热搜功能及文字过滤

    springboot结合redis实现搜索栏热搜功能及文字过滤

    本文主要介绍了springboot结合redis实现搜索栏热搜功能及文字过滤,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-02-02

最新评论