CAS单点登录实现(包含原理配置实现及简易demo)

CAS单点登录-简介

CAS 简介

CAS ( Central Authentication Service ) 是 Yale 大学发起的一个企业级的、开源的项目,旨在为 Web 应用系统提供一种可靠的单点登录解决方法(属于 Web SSO )。

SSO 简介

单点登录( Single Sign-On , 简称 SSO )是目前比较流行的服务于企业业务整合的解决方案之一, SSO 使得在多个应用系统中,用户只需要 登录一次 就可以访问所有相互信任的应用系统。

CAS 的基本原理

从结构体系看, CAS 包括两部分: CAS Server 和 CAS Client 。(服务端/客户端)

服务端CAS Server 负责完成对用户的认证工作 , 需要独立部署 , CAS Server 会处理用户名 / 密码等凭证(Credentials) 。

客户端CAS Client 与受保护的客户端应用部署在一起,以 Filter 方式保护受保护的资源。CAS Client 负责处理对客户端受保护资源的访问请求,需要对请求方进行身份认证时,重定向到 CAS Server 进行认证。(原则上,客户端应用不再接受任何的用户名密码等 Credentials )。

基于CAS的SSO访问流程步骤:

访问服务: CAS Client 客户端发送请求访问应用系统提供的服务资源。

定向认证: CAS Client 客户端会重定向用户请求到 CAS Server 服务器。

用户认证: 用户在浏览器端输入用户验证信息,CAS Server服务端完成用户身份认证。

发放票据: CAS Server服务器会产生一个随机的 Service Ticket 。

验证票据: CAS Server服务器验证票据 Service Ticket 的合法性,验证通过后,允许客户端访问服务。

传输用户信息: CAS Server 服务器验证票据通过后,传输用户认证结果信息给客户端。

下面是 CAS 最基本的协议过程:

CAS单点登录实现(包含原理配置实现及简易demo)

搭建CAS Server

首先下载cas-overlay,进入cas的git项目,选择版本,下载zip包并解压。(要求至少为jdk8,tomcat 8+)

https://github.com/apereo/cas-overlay-template

本demo中使用的cas server版本为4.1(maven版本),较高版本的server(如5.3)在代码拉取后自带了脚本,可直接用于打包构建。

(5.3之后的都是gradle项目,5.3以之前都是maven 项目,build.cmd为win版自动构建+运行的脚本,sh为linux版)

CAS单点登录实现(包含原理配置实现及简易demo)

下面说明不使用脚本的server构建方式。

使用idea打开项目后,在较新的版本中,cas的很多配置都放在了cas.properties里,这个文件可以放在服务器的任何位置,但是需要修改propertyFileConfigurer.xml这个配置文件的目标路径,以告诉cas系统cas.properties需要去哪个位置加载。

下述截图标明了配置文件路径,右侧需对应修改cas.properties配置文件的存放路径,demo中我将配置文件放置于E:\prop中,直接修改路径即可。

CAS单点登录实现(包含原理配置实现及简易demo)

点击package将本项目打包,会生成一个上图路径target中的war文件:cas.war。该文件即打包好的cas server文件,将该war放入tomcat安装路径下的webapps文件夹中,启动tomcat,便会同时部署web应用程序cas server。

由于我的tomcat端口由8080修改为9527,当tomcat启动后,访问路径 http://localhost:9527/cas/login ,能见到cas登录页面时,说明本demo的cas服务端已安装部署成功。此时可尝试输入测试账号及密码,测试是否能实现登录功能。(静态账号casuser,密码Mellon)

CAS单点登录实现(包含原理配置实现及简易demo)

也可以自己修改账号密码,在deployerConfigContext.xml文件内进行修改,如下图

CAS单点登录实现(包含原理配置实现及简易demo)

至此,CAS Server的demo服务端搭建已完成。

编写CAS Client

为验证单点登录的有效性,新建立两个springboot项目,作为客户端2和客户端3。分别完成以下代码编写。当两个客户端及服务端1均启动时,若客户端2登录后,客户端3只需刷新页面即可同时处于已登录状态。

①引入CAS client依赖

在pom.xml中引入CAS Client的依赖包。代码如下:

<dependency>

<groupId>net.unicon.cas</groupId>

<artifactId>cas-client-autoconfig-support</artifactId>

<version>2.3.0-GA</version>

</dependency>





 

②配置

在application.properties或者application.yml中添加相关配置,主要配置内容包括服务器的相关地址,客户端的相关地址等。我这里是application.yml,配置内容如下:

(注意配置时的端口区分,本demo中,客户端2采用端口8890,客户端3采用端口9990)

cas:
#后端服务地址
client-host-url: http://127.0.0.1:8890
#cas认证中心地址
server-url-prefix: http://127.0.0.1:9527/cas
#cas认证中心登录地址
server-login-url: http://127.0.0.1:9527/cas/login
#Ticket校验器使用Cas30ProxyReceivingTicketValidationFilter
validation-type: cas3





 

③在启动类中添加启用注解

//启用CAS

@EnableCasClient

@SpringBootApplication

public class SpringBootSsoApplication { //省略部分内容 }

④编写测试接口Controller层

import org.jasig.cas.client.authentication.AttributePrincipal;
import org.jasig.cas.client.util.AbstractCasFilter;
import org.jasig.cas.client.validation.Assertion;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

/**
* Author Haozhonghao
* Date 2021/12/20 10:00
* Version 1.0
*/
@RequestMapping("/casTest2")
@Controller
public class CASTestController {
@Value(value = "${cas.server-url-prefix}")
private String serverUrlPrefix = "";
@Value(value = "${cas.client-host-url}")
private String clientHostUrl = "";

@GetMapping("/user2")
@ResponseBody
public String user(HttpServletRequest request) {
Assertion assertion = (Assertion) request.getSession().getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION);
String loginName = null;
if (assertion != null) {
AttributePrincipal principal = assertion.getPrincipal();
loginName = principal.getName();
System.out.println("访问者2:" + loginName);
}
return "访问者2:" + loginName;
}

@GetMapping("/logout")
public String logout(HttpSession session) {
session.invalidate();
return "redirect:" + serverUrlPrefix + "/logout?service=" + clientHostUrl + "/casTest2/user2";
}

@GetMapping("/test2")
public String test() {
return "test2....";
}
}

实现CAS Client单点登录过程

测试过程:

初步测试时,开启服务端1、客户端2和客户端3。打开浏览器,输入地址http://127.0.0.1:9990/casTest3/user3,出现如下报错:(输入客户端2的接口地址同样报错)

CAS单点登录实现(包含原理配置实现及简易demo)

显示错误信息:权限配置问题

  1. Application Not Authorized to Use CAS
  2. The application you attempted to authenticate toisnot authorized to use CAS.

解决办法:

修改Tomcat/webapps/cas/WEB-INF/classes/services目录下的HTTPSandIMAPS-10000001.Json文件:直接复制替换

{

"@class": "org.jasig.cas.services.RegexRegisteredService",

"serviceId" : "^(https|http|imaps)://.*",

"name": "https://localhost",

"id": 1,

"evaluationOrder": 0,

"logoutType": "BACK_CHANNEL",

"proxyPolicy" : {

"allowedToProxy": true,

"@class" : "org.jasig.cas.services.RegexMatchingRegisteredServiceProxyPolicy",

"pattern" : "^(https?)://localhost.*"

}

}

修改后重启服务端及客户端

CAS单点登录实现(包含原理配置实现及简易demo)

此时访问客户端2接口地址,能够正常显示如下界面,允许输入用户名及密码,便于测试仍然使用默认账号及密码(账号:casuser,密码:Mellon)

CAS单点登录实现(包含原理配置实现及简易demo)

输入相应用户名及密码后,登陆成功:

CAS单点登录实现(包含原理配置实现及简易demo)

输入客户端3接口地址后访问,直接登陆成功

CAS单点登录实现(包含原理配置实现及简易demo)

单点登录简易demo效果至此实现成功。为形成完整的登录登出流程,后续还需要实现cas单点登出。

注:图所显示的错误Non-secure Connection,是由于没有使用HTTPS协议的关系,而默认的登陆界面有对此进行验证的代码,而在实际项目中的登陆界面一般需要自己写,通过修改webapps\cas\WEB-INF\view\jsp\default\ui下的casLoginView.jsp即可。将下图所示代码删掉即可去除错误警告。

CAS单点登录实现(包含原理配置实现及简易demo)

实现CAS Client单点登出过程

实现单点登出,需要在前文代码及配置的基础上进行。

首先需要添加服务端配置。

CAS服务端需进行相应的更改,在cas.properties中将下述配置改为true(若没有下述条目配置,则直接复制粘贴入文件中),添加配置后方可实现登出后的重定向,重定向地址为测试接口Controller层中编写的地址,当调用logout登出后,cas服务端发现该客户端不在登录状态,就会再次跳转到登录页,成功登录之后,便会再次跳转到目标接口路径。

# Specify whether CAS should redirect to the specified service parameter on /logout requests

cas.logout.followServiceRedirects=true

如果不添加此配置,就无法实现登出后的跳转,效果图如下(仅显示登出成功,而不会跳转到登录页)

CAS单点登录实现(包含原理配置实现及简易demo)

其次是要注意测试接口Controller层中的注解使用。注意不要使用@RestController注解本类,而是使用@Controller注解,否则会导致重定向地址仅仅渲染在页面上,而不会实现页面跳转。

原因分析

@RestController注解相当于@ResponseBody + @Controller合在一起的作用

重定向失效即是@ResponseBody注解引起的。 @ResponseBody是作用在方法上的,@ResponseBody 表示该方法的返回结果直接写入 HTTP response body 中,一般在异步获取数据时使用【也就是AJAX】,在使用 @RequestMapping后,返回值通常解析为跳转路径,但是加上 @ResponseBody 后返回结果不会被解析为跳转路径,而是直接写入 HTTP response body 中。 比如异步获取 json 数据,加上 @ResponseBody 后,会直接返回 json 数据。@RequestBody 将 HTTP 请求正文插入方法中,使用适合的 HttpMessageConverter 将请求体写入某个对象。

不会被解析成跳转路径,那么 return "redirect:” 重定向也就失效了。

因此解决方法就是把@RestController改成@Controller注解。

解决上述两点问题之后,重新启动服务端1、客户端2、客户端3。

实现单点登录操作后,任选其中一方,访问其logout接口:

CAS单点登录实现(包含原理配置实现及简易demo)

访问成功后,客户端2页面成功跳转至登录界面,回到客户端3,刷新页面,由于单点登出的效果,客户端3也处于离线状态,单点登出效果实现成功!

CAS单点登录实现(包含原理配置实现及简易demo)

实际cas单点登录的应用场景,不可能只使用一个测试账号登录,因此需要实现cas与数据库相结合,实现自定义账号的单点登录登出,方有实际使用意义。这部分还在做,做好了再发。

上一篇:dpdk课程学习之练习笔记七(vpp环境搭建及plugin demo测试)


下一篇:静态代理与动态代理模式demo实现