SpringBoot 实现CAS Server统一登录认证

这篇具有很好参考价值的文章主要介绍了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访问流程步骤:

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

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

  • 引入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;
    }
  }

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

:该部分内容仅仅为自己记录使用,可跳过!文章来源地址https://www.toymoban.com/news/detail-794904.html

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统一登录认证的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【SpringBoot】自从集成spring-security-oauth2后,实现统一认证授权so easy!

    principal:能唯一标识用户身份的属性,一个用户可以有多个principal 如登录的唯一标识, 用户可以使用用户名或手机或邮箱进行登录 ,这些principal是让别人知道的 credential:凭证,用户才知道的,简单的说就是 密码 如:手机开锁,可以使用 屏幕密码 也可以使用 人脸识别 ,

    2023年04月24日
    浏览(66)
  • Springboot+redis实现token的登录认证

    1.在用户登录后,如果要访问其他路径下的资源的话,我们是否还需要再验证一遍呢?而且我们登陆上系统长时间不操作的话还需不需要再次验证?所以这种情况下就很需要token来实现登录功能。并通过redis(redis是一个key-value存储系统,它支持存储的value类型相对更多,包括

    2023年04月09日
    浏览(70)
  • springBoot JWT实现websocket的token登录拦截认证

    功能:所有关于websocket的请求必须登录,实现websocket需要登录后才可使用,不登录不能建立连接。 后台spring security配置添加websocket的请求可以匿名访问,关于websocket的请求不要认证就可以随意访问,去除匿名访问后,前端在与websocket建立链接无法在请求头里直接加入Authoriz

    2024年02月13日
    浏览(37)
  • Spring Authorization Server入门 (十三) 实现联合身份认证,集成Github与Gitee的OAuth登录

    什么是联合身份认证?       通过Spring Security OAuth2 Client(Login)模块集成第三方登录至自己的认证服务中,使用联合身份认证只需要请求认证服务,不通过前端来跳转三方的授权申请链接,而是统一通过认证服务来跳转,只需要维护Spring Authorization Server中身份认证提供商

    2024年02月05日
    浏览(36)
  • C#窗体程序连接SQL Server数据库实现账号登录、账号注册、修改密码、账号注销和实名认证(不定时更新)

    这是本人用Visual Studio2019做的一个C#窗体登录程序,如标题所示,它包含了账号登录、注册账号、修改密码、注销账号和实名认证五个功能。对于有一定基础知识的小伙伴来说,应该不算太难,里面有注释说明,可能咋一看感觉代码运行的逻辑有点乱,不过没关系,相信对你会

    2024年02月02日
    浏览(55)
  • springboot整合security,mybatisPlus,thymeleaf实现登录认证及用户,菜单,角色权限管理

    本系统为springboot整合security,mybatisPlus,thymeleaf实现登录认证及用户,菜单,角色权限管理。页面为极简模式,没有任何渲染。 源码:https://gitee.com/qfp17393120407/spring-boot_thymeleaf 架构截图 此处以用户表为例,其他表数据可在源码获取。 用户表 共用属性 共用属性自动填充配置

    2024年02月07日
    浏览(39)
  • springboot整合Sa-Token实现登录认证和权限校验(万字长文)

    目前在国内的后端开发中,常用的安全框架有spring security、shiro。现在,介绍一款由国人开发的安全框架Sa-Token。这个框架完全由国人开发,所提供的Api文档和一些设置都是比较符合国人的开发习惯的,本次就来介绍一下如何在spring boot框架中整合Sa-Token框架,以实现我们最常

    2024年04月27日
    浏览(33)
  • 若依微服务集成CAS,实现单点登录

    若依(RuoYi)微服务是一款基于Spring Cloud Alibaba开发的企业级微服务框架,采用前后端分离方式,使用了常用的微服务组件,如Feign、Nacos、Sentinel、Seata等,提供了丰富的微服务治理功能,如服务注册、发现、路由、负载均衡、熔断降级、限流等。借助若依微服务框架可以让我

    2024年02月12日
    浏览(31)
  • 多系统使用CAS实现SSO登录案例

    实现SSO有很多种方案,比较简单且可行的一般都是如上两种再加上CAS,一般企业内部多个系统之间如果想实现SSO,比较推荐的方式就是今天要介绍的CAS,Central Authentication Service,中央身份认证服务。 CAS方案主要包含如下两个部分: CAS Server,作为认证服务的中心,需要单独部

    2024年02月15日
    浏览(33)
  • SpringBoot 基于 OAuth2 统一身份认证流程详解

    了解OAUTH2统一认证基本概念 了解OAUTH2协议流程 了解OAUTH2各种模式类型 了解Spring Security OAuth设计 2. 分析 传统登陆认证介绍 单点登陆认证介绍 OAuth2简介 OAuth2角色 OAuth2协议流程介绍 OAuth2授权类型 OAuth2授权码模式流程 OAuth2简化模式 OAuth2密码模式 OAuth2客户端模式 Spring Security

    2024年02月15日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包