900字范文,内容丰富有趣,生活中的好帮手!
900字范文 > CAS4.1单点登录实现(包含原理配置实现及简易demo)

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

时间:2019-05-23 02:13:17

相关推荐

CAS4.1单点登录实现(包含原理配置实现及简易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 Server

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

/apereo/cas-overlay-template

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

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

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

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

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

点击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)

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

至此,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校验器使用Cas30ProxyReceivingTicketValidationFiltervalidation-type: cas3

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

//启用CAS@EnableCasClient@SpringBootApplicationpublic 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 /12/20 10:00* Version 1.0*/@RequestMapping("/casTest2")@Controllerpublic class CASTestController {@Value(value = "${cas.server-url-prefix}")private String serverUrlPrefix = "";@Value(value = "${cas.client-host-url}")private String clientHostUrl = "";@GetMapping("/user2")@ResponseBodypublic 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的接口地址同样报错)

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

Application Not Authorized to Use CASThe 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.*"}}​

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

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

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

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

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

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

实现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

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

其次是要注意测试接口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接口:

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

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

结合mysql数据库,实现CAS单点登出的客户及服务的交互过程

主要修改部分为 CAS Server的配置部分,通过在服务端配置数据源及相关验证,实现自定义用户及密码的登录替换,具体步骤及实施方式如下:

①新增cas登录的相关库表,具体库表语句如下:

# 创建测试数据库

CREATE DATABASE cas;USE cas;# 创建测试表CREATE TABLE `cas_t_user` (`id` int(11) NOT NULL AUTO_INCREMENT,`user_name` varchar(50) DEFAULT NULL,`password` varchar(255) DEFAULT NULL,PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;# 插入两条测试用户数据INSERT INTO `cas_t_user` VALUES (1,'admin','96e79218965eb72c92a549dd5a330112');INSERT INTO `cas_t_user` VALUES (2,'user','96e79218965eb72c92a549dd5a330112');

②修改配置文件内容,并新增部分配置内容

修改%tomcat_home%/webapps/cas/WEB_INF/deployerConfigContext.xml

首先注释掉下述代码(注释默认用户名和密码)

<!-- 配置数据库验证bean,注释原有handler,取消默认用户名及密码--><!-- <bean id="primaryAuthenticationHandler"--><!-- class="org.jasig.cas.authentication.AcceptUsersAuthenticationHandler">--><!-- <property name="users">--><!-- <map>--><!-- <entry key="casuser" value="Mellon"/>--><!-- </map>--><!-- </property>--><!-- </bean>-->

添加下述代码,MD5密码验证,数据库配置,认证类配置

<!-- 配置passwordEncoder --><bean id="MD5PasswordEncoder"class="org.jasig.cas.authentication.handler.DefaultPasswordEncoder"><constructor-arg index="0" value="MD5"/></bean><!-- 添加数据源 --><bean id="dataSource"class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://127.0.0.1:3306/cas?characterEncoding=UTF-8&amp;autoReconnect=true"/><property name="username" value="root"/><property name="password" value="root"/></bean><!-- 配置认证类 --><bean id="primaryAuthenticationHandler" class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler"><!--dataSource指向上面配置的dataSource bean--><property name="dataSource" ref="dataSource"></property><property name="sql" value="select password from cas_t_user where user_name=?"></property><!--passwordEncoder 指向上面配置的 passwordEncoder bean--><property name="passwordEncoder" ref="MD5PasswordEncoder"></property></bean>

③添加相关jar包

目录:apache-tomcat-8.5.73\webapps\cas\WEB-INF\lib在上面目录下添加以下三个jar包:

mysql-connector-java-5.1.46.jar

cas-server-support-jdbc-4.1.0.jar

c3p0-0.9.5.5.jar

(最开始使用了cas-server-support-jdbc-4.0.0.jar,发现输入正确的用户及密码时,无法实现正常页面跳转,且密码栏会被清空,切换为与本demo中的casServer版本完全匹配的jar后此问题被修复)

重启tomcat的服务端1,重启客户端2、客户端3进行测试

输入admin/111111

其中用户的密码为MD5之后的值“111111”的MD5值为:“96e79218965eb72c92a549dd5a330112”

效果如下图

可见,此时我们自己在数据库中添加的用户可以实现单点登录及单点登出。

客户端采用cas-client-core 3.5.0版本依赖,实现cas单点登录登出效果

前述文中阐述了使用模板依赖cas-client-autoconfig-support 2.3.0-GA的客户端实现方式:

<dependency><groupId>net.unicon.cas</groupId><artifactId>cas-client-autoconfig-support</artifactId><version>2.3.0-GA</version></dependency>

而实际上,cas-client的客户端接入方式不止一种,下面将简述另一种客户端接入方式:采用cas-client-core包的客户端接入。

首先引入依赖:

<dependency><groupId>org.jasig.cas.client</groupId><artifactId>cas-client-core</artifactId><version>3.5.0</version></dependency>

与前文 SpringBoot 集成 CAS Client 略有不同,本集成不需要在 SpringBoot 启动类上加入启用 CAS Client 的 @EnableCasClient 注解。如果加了这个注解, CAS Client是不能实现根据配置文件开关的,每次想要关掉,必须修改代码,注释调注解才行。

然后编写配置类:

import lombok.extern.slf4j.Slf4j;import org.jasig.cas.client.authentication.AuthenticationFilter;import org.jasig.cas.client.session.SingleSignOutFilter;import org.jasig.cas.client.session.SingleSignOutHttpSessionListener;import org.jasig.cas.client.util.HttpServletRequestWrapperFilter;import org.jasig.cas.client.validation.Cas30ProxyReceivingTicketValidationFilter;import org.springframework.beans.factory.annotation.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;/*** Author Haozhonghao* Date /1/10 10:34* Version 1.0* CAS集成核心配置类*/@Configuration@Slf4j@ConditionalOnProperty(value = "cas.loginType", havingValue = "cas")public class CasFilterConfig {/*** 需要走cas拦截的地址(/* 所有地址都拦截)*/@Value("${cas.urlPattern}")private String filterUrl;/*** 默认的cas地址,防止通过 配置信息获取不到*/@Value("${cas.server-url-prefix}")private String casServerUrl;/*** 应用访问地址(这个地址需要在cas服务端进行配置)*/@Value("${cas.authentication-url}")private String authenticationUrl;/*** 应用访问地址(这个地址需要在cas服务端进行配置)*/@Value("${cas.client-host-url}")private String appServerUrl;@Beanpublic ServletListenerRegistrationBean servletListenerRegistrationBean() {log.info(" \n cas 单点登录配置 \n appServerUrl = " + appServerUrl + "\n casServerUrl = " + casServerUrl);log.info(" servletListenerRegistrationBean ");ServletListenerRegistrationBean listenerRegistrationBean = new ServletListenerRegistrationBean();listenerRegistrationBean.setListener(new SingleSignOutHttpSessionListener());listenerRegistrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE);return listenerRegistrationBean;}/*** 单点登录退出*/@Beanpublic 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;}/*** 单点登录认证*/@Beanpublic 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;}/*** 单点登录校验*/@Beanpublic FilterRegistrationBean Cas30ProxyReceivingTicketValidationFilter() {log.info(" Cas30ProxyReceivingTicketValidationFilter ");FilterRegistrationBean registrationBean = new FilterRegistrationBean();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;}/*** 单点登录请求包装*/@Beanpublic FilterRegistrationBean httpServletRequestWrapperFilter() {log.info(" httpServletRequestWrapperFilter ");FilterRegistrationBean registrationBean = new FilterRegistrationBean();registrationBean.setFilter(new HttpServletRequestWrapperFilter());registrationBean.addUrlPatterns(filterUrl);registrationBean.setName("CAS HttpServletRequest Wrapper Filter");registrationBean.setOrder(1);return registrationBean;}}

接着,编写yml配置文件:

server:port: 9797cas:# 认证中心登录页面地址server-url-prefix: http://127.0.0.1:9527/cas/login# 应用地址,也就是自己的系统地址。client-host-url: http://127.0.0.1:9797# 认证中心地址authentication-url: http://127.0.0.1:9527/cas# 动态开启 cas 单点登录loginType: cas# cas 验票拦截路径urlPattern: /*

最后编写测试接口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 /1/10 10:45* Version 1.0*/@RequestMapping("/casTest5")@Controllerpublic class CASDemoController {/*** cas 单点登录*/@Value(value = "${cas.server-url-prefix}")private String serverUrlPrefix = "";@Value(value = "${cas.client-host-url}")private String clientHostUrl = "";@GetMapping("/user5")@ResponseBodypublic 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("访问者5:" + loginName);}return "访问者5:" + loginName;}@GetMapping("/logout")public String logout(HttpSession session) {//将session设置为失效session.invalidate();return "redirect:" + serverUrlPrefix + "/logout?service=" + clientHostUrl + "/casTest5/user5";}}

进行测试,cas-client-core3.5.0版本比较稳定,可以兼容多个版本的 CAS Server,包括前文我们在本地构建的cas-server-4.1.0。

登录,输入admin/111111,登录效果如图,实现完成。

cas客户端demo至此功能已全部实现。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。