Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >CAS+Springboot单点登录

CAS+Springboot单点登录

作者头像
丁D
发布于 2022-08-12 07:01:15
发布于 2022-08-12 07:01:15
1.5K00
代码可运行
举报
文章被收录于专栏:老铁丁D老铁丁D
运行总次数:0
代码可运行

本文源码demo https://github.com/348786639/cas

什么是单点登录

单点登录就是登录一次处处已登录。 单点登录就是假设我有两个系统 比如 淘宝和天猫,我登录了淘宝,当我访问天猫的时候不用在登录一次。

CAS原理

文字描述 假设我们用3个系统 系统A,系统B,和认证中心 1、访问系统A,第一次访问没登陆,系统A重定向用户认证中心(带上service) 2、用户访问认证中心没有带上TGC(还没登陆),认证中心返回登陆页面 3、用户输入账号密码,进行登陆 4、认证中心进行登陆逻辑校验,成功就向客户端写cookie(TGC),并生成TGT缓存在服务器本地, 用TGT签发ST 5、用户认证中心重定向到第一次访问的带上的service(带上ST) 6、系统A的拦截器收到请求后,拿出ST,向认证中心询问ST是否有效(这个步骤对用户透明,直接使用http访问) 7、认证中心回复有效,并返回用户名字,和一些其他属性 8、系统A收到回复,建立本地Session

当用户第二次访问系统A的时候,由于第一次已经建立了本地session,所以成功登陆

1、当用户访问系统B的时候,没有本地session,系统B将请求重定向到用户认证中心(带上service) 2、这个时候用于我们第一次访问系统A的时候,用户认证中心,已经写下TGC,所以访问的是会带上TGC,认证中心根据TGC找到TGT,说明已经登陆过了 3、认证中心重定向到service地址带上ST 4、系统B收到ST向认证中心询问,ST是否有效,(这个步骤对用户透明,直接使用http访问) 5、认证中心回复有效,并返回用户名字,和一些其他属性 6、系统B收到回复,建立本地Session 参考 https://blog.csdn.net/ban_tang/article/details/80015946 https://www.cnblogs.com/notDog/p/5252973.html

CAS Server搭建

1、从github下载解压(我选择5.2分支) https://github.com/apereo/cas-overlay-template

2、导入idea

目录结构如上图,我们自己建src/java/main、resource目录,并修改project structure

3、修改pom文件 由于我们使用overlay方式进行开发的,war引用的依赖我们在开发中是依赖不到的,所以我们如果使用到Cas的jar还是需要引用的,我们可以将生命周期设置为provided。

由于这里我要查询数据库和集成mybaits,所以也依赖了

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<?xml version="1.0" encoding="UTF-8"?> 
<project xmlns="http://maven.apache.org/POM/4.0.0" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd "> 
<modelVersion>4.0.0</modelVersion> 
<groupId>org.apereo.cas</groupId> 
<artifactId>cas-overlay</artifactId> 
<packaging>war</packaging> 
<version>1.0</version> 
<!-- 使用overlay 方式是在war的基础上进行开发的,,war里面pom问题引用的我们在开发的时候是引用不到的, --> 
<!-- 所以我们如果需要使用cas的jar包,我们还需要在这个文件中进行引用,scope 为provided就行, --> 
<!-- dependencies 这部分是我们自己的依赖,,只是将仓库删除,不然下载不了包,, --> 
<dependencies> 
<dependency> 
<groupId>org.apereo.cas</groupId> 
<artifactId>cas-server-webapp${app.server}</artifactId> 
<version>${cas.version}</version> 
<type>war</type> 
<scope>runtime</scope> 
</dependency> 
<!--json服务注册--> 
<dependency> 
<groupId>org.apereo.cas</groupId> 
<artifactId>cas-server-support-json-service-registry</artifactId> 
<version>${cas.version}</version> 
<scope>provided</scope> 
</dependency> 
<dependency> 
<groupId>org.apereo.cas</groupId> 
<artifactId>cas-server-core-authentication</artifactId> 
<version>${cas.version}</version> 
<scope>provided</scope> 
</dependency> 
<dependency> 
<groupId>org.apereo.cas</groupId> 
<artifactId>cas-server-core-authentication-api</artifactId> 
<version>${cas.version}</version> 
<scope>provided</scope> 
</dependency> 
<dependency> 
<groupId>mysql</groupId> 
<artifactId>mysql-connector-java</artifactId> 
<version>5.1.44</version> 
</dependency> 
<dependency> 
<groupId>org.mybatis</groupId> 
<artifactId>mybatis-spring</artifactId> 
<version>2.0.3</version> 
</dependency> 
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> 
<dependency> 
<groupId>org.mybatis</groupId> 
<artifactId>mybatis</artifactId> 
<version>3.4.6</version> 
</dependency> 
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter --> 
<!--引入Spring SPI机制 --> 
<dependency> 
<groupId>org.springframework.boot</groupId> 
<artifactId>spring-boot-starter</artifactId> 
<version>1.5.2.RELEASE</version> 
<exclusions> 
<exclusion> 
<groupId>org.springframework.boot</groupId> 
<artifactId>spring-boot-starter-logging</artifactId> 
</exclusion> 
</exclusions> 
</dependency> 
<dependency> 
<groupId>org.apereo.cas</groupId> 
<artifactId>cas-server-core-configuration</artifactId> 
<version>${cas.version}</version> 
<scope>provided</scope> 
</dependency> 
<dependency> 
<groupId>org.apereo.cas</groupId> 
<artifactId>cas-server-support-rest</artifactId> 
<version>${cas.version}</version> 
<scope>provided</scope> 
</dependency> 
<dependency> 
<groupId>org.apereo.cas</groupId> 
<artifactId>cas-server-core-webflow-api</artifactId> 
<version>${cas.version}</version> 
<scope>provided</scope> 
</dependency> 
<dependency> 
<groupId>org.apereo.cas</groupId> 
<artifactId>cas-server-core-webflow</artifactId> 
<version>${cas.version}</version> 
<scope>provided</scope> 
</dependency> 
<!-- https://mvnrepository.com/artifact/org.apereo.cas/cas-server-support-actions --> 
<dependency> 
<groupId>org.apereo.cas</groupId> 
<artifactId>cas-server-support-actions</artifactId> 
<version>${cas.version}</version> 
<scope>provided</scope> 
</dependency> 
<dependency> 
<groupId>org.projectlombok</groupId> 
<artifactId>lombok</artifactId> 
<version>1.12.6</version> 
<scope>provided</scope> 
</dependency> 
</dependencies> 
<!--下面是复制war原本的 只是删除仓库--> 
<build> 
<plugins> 
<plugin> 
<groupId>com.rimerosolutions.maven.plugins</groupId> 
<artifactId>wrapper-maven-plugin</artifactId> 
<version>0.0.4</version> 
<configuration> 
<verifyDownload>true</verifyDownload> 
<checksumAlgorithm>MD5</checksumAlgorithm> 
</configuration> 
</plugin> 
<plugin> 
<groupId>org.springframework.boot</groupId> 
<artifactId>spring-boot-maven-plugin</artifactId> 
<version>${springboot.version}</version> 
<configuration> 
<mainClass>${mainClassName}</mainClass> 
<addResources>true</addResources> 
<executable>${isExecutable}</executable> 
<layout>WAR</layout> 
</configuration> 
<executions> 
<execution> 
<goals> 
<goal>repackage</goal> 
</goals> 
</execution> 
</executions> 
</plugin> 
<plugin> 
<groupId>org.apache.maven.plugins</groupId> 
<artifactId>maven-war-plugin</artifactId> 
<version>2.6</version> 
<configuration> 
<warName>cas</warName> 
<failOnMissingWebXml>false</failOnMissingWebXml> 
<recompressZippedFiles>false</recompressZippedFiles> 
<archive> 
<compress>false</compress> 
<manifestFile>${manifestFileToUse}</manifestFile> 
</archive> 
<overlays> 
<overlay> 
<groupId>org.apereo.cas</groupId> 
<artifactId>cas-server-webapp${app.server}</artifactId> 
<!--原有的服务不再初始化进去--> 
<excludes> 
<!-- <exclude>WEB-INF/classes/services/*</exclude>--> 
<!-- <exclude>WEB-INF/classes/application.*</exclude>--> 
<!-- <exclude>WEB-INF/classes/log4j2.*</exclude>--> 
</excludes> 
</overlay> 
</overlays> 
</configuration> 
</plugin> 
<plugin> 
<groupId>org.apache.maven.plugins</groupId> 
<artifactId>maven-compiler-plugin</artifactId> 
<version>3.3</version> 
</plugin> 
</plugins> 
<finalName>cas</finalName> 
</build> 
<properties> 
<cas.version>5.2.6</cas.version> 
<springboot.version>1.5.12.RELEASE</springboot.version> 
<!-- app.server could be -jetty, -undertow, -tomcat, or blank if you plan to provide appserver --> 
<app.server>-tomcat</app.server> 
<mainClassName>org.springframework.boot.loader.WarLauncher</mainClassName> 
<isExecutable>false</isExecutable> 
<manifestFileToUse>${project.build.directory}/war/work/org.apereo.cas/cas-server-webapp${app.server}/META-INF/MANIFEST.MF</manifestFileToUse> 
<maven.compiler.source>1.8</maven.compiler.source> 
<maven.compiler.target>1.8</maven.compiler.target> 
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 
</properties> 
<profiles> 
<profile> 
<activation> 
<activeByDefault>true</activeByDefault> 
</activation> 
<id>default</id> 
<dependencies> 
<dependency> 
<groupId>org.apereo.cas</groupId> 
<artifactId>cas-server-webapp${app.server}</artifactId> 
<version>${cas.version}</version> 
<type>war</type> 
<scope>runtime</scope> 
</dependency> 
<!-- 
...Additional dependencies may be placed here... 
--> 
</dependencies> 
</profile> 
<profile> 
<activation> 
<activeByDefault>false</activeByDefault> 
</activation> 
<id>exec</id> 
<properties> 
<mainClassName>org.apereo.cas.web.CasWebApplication</mainClassName> 
<isExecutable>true</isExecutable> 
<manifestFileToUse></manifestFileToUse> 
</properties> 
<build> 
<plugins> 
<plugin> 
<groupId>com.soebes.maven.plugins</groupId> 
<artifactId>echo-maven-plugin</artifactId> 
<version>0.3.0</version> 
<executions> 
<execution> 
<phase>prepare-package</phase> 
<goals> 
<goal>echo</goal> 
</goals> 
</execution> 
</executions> 
<configuration> 
<echos> 
<echo>Executable profile to make the generated CAS web application executable.</echo> 
</echos> 
</configuration> 
</plugin> 
</plugins> 
</build> 
</profile> 
<profile> 
<activation> 
<activeByDefault>false</activeByDefault> 
</activation> 
<id>bootiful</id> 
<properties> 
<app.server>-tomcat</app.server> 
<isExecutable>false</isExecutable> 
</properties> 
<dependencies> 
<dependency> 
<groupId>org.apereo.cas</groupId> 
<artifactId>cas-server-webapp${app.server}</artifactId> 
<version>${cas.version}</version> 
<type>war</type> 
<scope>runtime</scope> 
</dependency> 
</dependencies> 
</profile> 
<profile> 
<activation> 
<activeByDefault>false</activeByDefault> 
</activation> 
<id>pgp</id> 
<build> 
<plugins> 
<plugin> 
<groupId>com.github.s4u.plugins</groupId> 
<artifactId>pgpverify-maven-plugin</artifactId> 
<version>1.1.0</version> 
<executions> 
<execution> 
<goals> 
<goal>check</goal> 
</goals> 
</execution> 
</executions> 
<configuration> 
<pgpKeyServer>hkp://pool.sks-keyservers.net</pgpKeyServer> 
<pgpKeysCachePath>${settings.localRepository}/pgpkeys-cache</pgpKeysCachePath> 
<scope>test</scope> 
<verifyPomFiles>true</verifyPomFiles> 
<failNoSignature>false</failNoSignature> 
</configuration> 
</plugin> 
</plugins> 
</build> 
</profile> 
</profiles> 
</project> 

4、创建登陆处理类 这里我第一次先将账号密码写死,后面使用了jdbc去查询数据库,后面又集成了mybatis,所以这个比较乱,自行修改

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.ding; 
import org.apereo.cas.authentication.HandlerResult; 
import org.apereo.cas.authentication.PreventedException; 
import org.apereo.cas.authentication.UsernamePasswordCredential; 
import org.apereo.cas.authentication.handler.support.AbstractUsernamePasswordAuthenticationHandler; 
import org.apereo.cas.authentication.principal.PrincipalFactory; 
import org.apereo.cas.services.ServicesManager; 
import org.springframework.jdbc.core.JdbcTemplate; 
import org.springframework.jdbc.datasource.DriverManagerDataSource; 
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 
import javax.security.auth.login.FailedLoginException; 
import java.security.GeneralSecurityException; 
import java.util.HashMap; 
import java.util.Map; 
/** 
* 自定义登陆逻辑参考和页面 
* https://blog.csdn.net/u010588262/article/details/80014083 
* https://blog.csdn.net/weixin_37548740/article/details/104053834 
*/ 
public class Login extends AbstractUsernamePasswordAuthenticationHandler { 
private SysUserMapper sysUserMapper; 
public Login(String name, ServicesManager servicesManager, PrincipalFactory principalFactory, Integer order,SysUserMapper sysUserMapper) { 
super(name, servicesManager, principalFactory, order); 
this.sysUserMapper = sysUserMapper; 
} 
@Override 
protected HandlerResult authenticateUsernamePasswordInternal(UsernamePasswordCredential usernamePasswordCredential, String s) throws GeneralSecurityException, PreventedException { 
//使用jdbc试一下 和使用mybatis试试 
DriverManagerDataSource d=new DriverManagerDataSource(); 
d.setDriverClassName("com.mysql.jdbc.Driver"); 
d.setUrl("jdbc:mysql://127.0.0.1:3306/blog"); 
d.setUsername("root"); 
d.setPassword("123456"); 
JdbcTemplate template=new JdbcTemplate(); 
template.setDataSource(d); 
String username=usernamePasswordCredential.getUsername(); 
String pd=usernamePasswordCredential.getPassword(); 
Map<String,Object> user = template.queryForMap("SELECT `password` FROM t_sys_user WHERE user_name = ?", username); 
String pad = sysUserMapper.findUserName(username); 
System.out.printf(pad); 
//查询数据库加密的的密码 
if(username==null || username.equals("admin1111")){ 
throw new FailedLoginException("没有该用户"); 
} 
//返回多属性 
Map<String, Object> map=new HashMap<>(); 
map.put("email", "34865666@qq.com"); 
map.put("phone", "18850588888"); 
if(username.equals("admin") && pd.equals("123456")){ 
return createHandlerResult(usernamePasswordCredential, principalFactory.createPrincipal(username, map), null); 
} 
throw new FailedLoginException("Sorry, login attemp failed."); 
} 
} 

5、新增登陆配置器

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.ding; 
import org.apereo.cas.authentication.AuthenticationEventExecutionPlan; 
import org.apereo.cas.authentication.AuthenticationEventExecutionPlanConfigurer; 
import org.apereo.cas.authentication.AuthenticationHandler; 
import org.apereo.cas.authentication.principal.DefaultPrincipalFactory; 
import org.apereo.cas.configuration.CasConfigurationProperties; 
import org.apereo.cas.services.ServicesManager; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.beans.factory.annotation.Qualifier; 
import org.springframework.boot.context.properties.EnableConfigurationProperties; 
import org.springframework.context.annotation.Bean; 
import org.springframework.context.annotation.Configuration; 
/** 
* 還需要在下面这个问题配置 不然spring不会管理这个文件 
* O:\cas\cas-overlay-template-5.2\cas-overlay-template-5.2\src\main\resource\META-INF\spring.factories 
*/ 
@Configuration("CustomAuthConfig") 
@EnableConfigurationProperties(CasConfigurationProperties.class) 
public class CustomAuthConfig implements AuthenticationEventExecutionPlanConfigurer { 
@Autowired 
private CasConfigurationProperties casProperties; 
@Autowired 
@Qualifier("servicesManager") 
private ServicesManager servicesManager; 
@Autowired 
private SysUserMapper sysUserMapper; 
@Bean 
public AuthenticationHandler myAuthenticationHandler() { 
final Login handler = new Login(Login.class.getSimpleName(), servicesManager, new DefaultPrincipalFactory(), 10,sysUserMapper); 
return handler; 
} 
@Override 
public void configureAuthenticationExecutionPlan(AuthenticationEventExecutionPlan plan) { 
plan.registerAuthenticationHandler(myAuthenticationHandler()); 
} 
} 

还需要在src\main\resource\META-INF\spring.factories进行配置 这个文件我们可以去overlay复制过来修改

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 
com.ding.CustomAuthConfig,\ 
com.ding.MyBatisDataSourceConfig 

6、resource建立services(注册客户端)

去将overlay的\services\HTTPSandIMAPS-10000001.json复制过来进行修改 这里由于我还没有配置https,先使用http所以serviceId配置http 我们可以通过这个进行自定义登陆页面 “theme”: “blog”,会找 resource\templates\blog\casLoginView.html

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{ 
"@class" : "org.apereo.cas.services.RegexRegisteredService", 
"serviceId" : "^(https||http|imaps)://.*", 
"name" : "HTTPS and IMAPS", 
"id" : 10000001, 
"description" : "自定义登陆页面 theme 获取templates/blog/casLoginView.html", 
"evaluationOrder" : 10000, 
"theme": "blog", 
"attributeReleasePolicy": { 
"@class": "org.apereo.cas.services.ReturnAllAttributeReleasePolicy" 
} 
} 

7、配置application.properties配置文件 这个文件我们可以去overlay复制过来修改

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
## 
# CAS Server Context Configuration 
# 
server.context-path=/cas 
server.port=8080 
#####start#########签发证书,如果是用spring boot之类嵌入式的容器,则需要改这里的配置,如果是直接部在tomcat中,则需要把tomcat改成https的################### 
#server.ssl.key-store=file:/etc/cas/thekeystore 
#server.ssl.key-store-password=changeit 
#server.ssl.key-password=changeit 
# server.ssl.ciphers= 
# server.ssl.client-auth= 
# server.ssl.enabled= 
# server.ssl.key-alias= 
# server.ssl.key-store-provider= 
# server.ssl.key-store-type= 
# server.ssl.protocol= 
# server.ssl.trust-store= 
# server.ssl.trust-store-password= 
# server.ssl.trust-store-provider= 
# server.ssl.trust-store-type= 
server.max-http-header-size=2097152 
server.use-forward-headers=true 
server.connection-timeout=20000 
server.error.include-stacktrace=ALWAYS 
server.compression.enabled=true 
server.compression.mime-types=application/javascript,application/json,application/xml,text/html,text/xml,text/plain 
server.tomcat.max-http-post-size=2097152 
server.tomcat.basedir=build/tomcat 
server.tomcat.accesslog.enabled=true 
server.tomcat.accesslog.pattern=%t %a "%r" %s (%D ms) 
server.tomcat.accesslog.suffix=.log 
server.tomcat.max-threads=10 
server.tomcat.port-header=X-Forwarded-Port 
server.tomcat.protocol-header=X-Forwarded-Proto 
server.tomcat.protocol-header-https-value=https 
server.tomcat.remote-ip-header=X-FORWARDED-FOR 
server.tomcat.uri-encoding=UTF-8 
#####end#########签发证书,如果是用spring boot之类嵌入式的容器,则需要改这里的配置,如果是直接部在tomcat中,则需要把tomcat改成https的################### 
spring.http.encoding.charset=UTF-8 
spring.http.encoding.enabled=true 
spring.http.encoding.force=true 
## 
# CAS Cloud Bus Configuration 
# 
spring.cloud.bus.enabled=false 
# spring.cloud.bus.refresh.enabled=true 
# spring.cloud.bus.env.enabled=true 
# spring.cloud.bus.destination=CasCloudBus 
# spring.cloud.bus.ack.enabled=true 
endpoints.enabled=false 
endpoints.sensitive=true 
endpoints.restart.enabled=false 
endpoints.shutdown.enabled=false 
management.security.enabled=true 
management.security.roles=ACTUATOR,ADMIN 
management.security.sessions=if_required 
management.context-path=/status 
management.add-application-context-header=false 
security.basic.authorize-mode=role 
security.basic.enabled=false 
security.basic.path=/cas/status/** 
## 
# CAS Web Application Session Configuration 
# 
server.session.timeout=300 
server.session.cookie.http-only=true 
server.session.tracking-modes=COOKIE 
## 
# CAS Thymeleaf View Configuration 
# 
spring.thymeleaf.encoding=UTF-8 
spring.thymeleaf.cache=true 
spring.thymeleaf.mode=HTML 
## 
# CAS Log4j Configuration 
# 
# logging.config=file:/etc/cas/log4j2.xml 
server.context-parameters.isLog4jAutoInitializationDisabled=true 
## 
# CAS AspectJ Configuration 
# 
spring.aop.auto=true 
spring.aop.proxy-target-class=true 
## 
# CAS Authentication Credentials 
# 
#cas.authn.accept.users=casuser::Mellon 
#开启识别json文件,默认false 
#自动扫描服务配置,默认开启 
#cas.serviceRegistry.watcherEnabled=true 
#120秒扫描一遍 
#cas.serviceRegistry.repeatInterval=120000 
#延迟15秒开启 
#cas.serviceRegistry.startDelay=15000 
#资源加载路径 
cas.serviceRegistry.config.location=classpath:/services 
cas.tgc.secure=false 
cas.serviceRegistry.initFromJson=true 
# 默认主题 
#cas.theme.defaultThemeName=blog 

8、自定义登陆页面 创建templates\blog目录及casLoginView.html文件 src\main\resource\templates\blog\casLoginView.html

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<!DOCTYPE html> 
<html> 
<head> 
<meta charset="UTF-8"/> 
<meta name="viewport" content="width=device-width, initial-scale=1"/> 
<meta http-equiv="X-UA-Compatible" content="IE=edge"/> 
<link rel="stylesheet" th:href="@{${#themes.code('blog.css.file')}}"/> 
<script th:src="@{${#themes.code('blog.js.file')}}"></script> 
<title th:text="${#themes.code('demo.pageTitle')}"></title> 
</head> 
<!--里面以${#themes.code('blog.js.file')}形式获取的参数是从主题同名文件blog.properties中获取的:properties中获取的--> 
<body> 
<h1 th:text="${#themes.code('demo.pageTitle')}"></h1> 
<div> 
<form method="POST" th:object="${credential}"> 
<div th:if="${#fields.hasErrors('*')}"> 
<span th:each="err : ${#fields.errors('*')}" th:utext="${err}"/> 
</div> 
<h2 th:utext="#{screen.welcome.instructions}"></h2> 
<section class="row"> 
<label for="username" th:utext="#{screen.welcome.label.netid}"/> 
<div th:unless="${openIdLocalId}"> 
<input class="required" 
id="username" 
size="25" 
tabindex="1" 
type="text" 
th:disabled="${guaEnabled}" 
th:field="*{username}" 
th:accesskey="#{screen.welcome.label.netid.accesskey}" 
autocomplete="off"/> 
</div> 
</section> 
<section class="row"> 
<label for="password" th:utext="#{screen.welcome.label.password}"/> 
<div> 
<input class="required" 
type="password" 
id="password" 
size="25" 
tabindex="2" 
th:accesskey="#{screen.welcome.label.password.accesskey}" 
th:field="*{password}" 
autocomplete="off"/> 
</div> 
</section> 
<section> 
<input type="hidden" name="execution" th:value="${flowExecutionKey}"/> 
<input type="hidden" name="_eventId" value="submit"/> 
<input type="hidden" name="geolocation"/> 
<input class="btn btn-submit btn-block" 
name="submit" 
accesskey="l" 
th:value="#{screen.welcome.button.login}" 
tabindex="6" 
type="submit"/> 
<a th:href="@{/reg}">点我注册</a> 
</section> 
</form> 
</div> 
</body> 
</html> 

CAS Client搭建

1、新建一个spring boot项目 2、引入CAS客户端的依赖

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<dependencies> 
<dependency> 
<groupId>org.springframework.boot</groupId> 
<artifactId>spring-boot-starter-web</artifactId> 
</dependency> 
<dependency> 
<groupId>org.mybatis.spring.boot</groupId> 
<artifactId>mybatis-spring-boot-starter</artifactId> 
<version>2.1.2</version> 
</dependency> 
<dependency> 
<groupId>org.springframework.boot</groupId> 
<artifactId>spring-boot-starter-test</artifactId> 
<scope>test</scope> 
</dependency> 
<dependency> 
<groupId>mysql</groupId> 
<artifactId>mysql-connector-java</artifactId> 
<scope>runtime</scope> 
</dependency> 
<!-- 要有一个验证登陆成功跳转页面,使用使用thymeleaf--> 
<dependency> 
<groupId>org.springframework.boot</groupId> 
<artifactId>spring-boot-starter-thymeleaf</artifactId> 
</dependency> 
<!--CAS客户端依赖--> 
<dependency> 
<groupId>org.jasig.cas.client</groupId> 
<artifactId>cas-client-core</artifactId> 
<version>3.5.0</version> 
</dependency> 
</dependencies> 

3、新增CAS客户端拦截器bean的配置

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.ding.cas; 
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.AssertionThreadLocalFilter; 
import org.jasig.cas.client.util.HttpServletRequestWrapperFilter; 
import org.jasig.cas.client.validation.Cas30ProxyReceivingTicketValidationFilter; 
import org.springframework.beans.factory.annotation.Autowired; 
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 java.util.ArrayList; 
import java.util.HashMap; 
import java.util.List; 
import java.util.Map; 
/** 
* 查看下面这个链接和开放平台 
* https://www.cnblogs.com/whm-blog/p/11248304.html 
*/ 
@Configuration 
public class CasConfigure { 
@Autowired 
private CasClientProperties casClientProperties; 
/** 
* 用于实现单点登出功能 
*/ 
@Bean 
public ServletListenerRegistrationBean<SingleSignOutHttpSessionListener> singleSignOutHttpSessionListener() { 
ServletListenerRegistrationBean<SingleSignOutHttpSessionListener> listener = new ServletListenerRegistrationBean<>(); 
listener.setEnabled(true); 
listener.setListener(new SingleSignOutHttpSessionListener()); 
listener.setOrder(1); 
return listener; 
} 
/** 
* 该过滤器用于实现单点登出功能,单点退出配置,一定要放在其他filter之前 
*/ 
@Bean 
public FilterRegistrationBean singleSignOutFilter() { 
FilterRegistrationBean filterRegistration = new FilterRegistrationBean(); 
filterRegistration.setFilter(new SingleSignOutFilter()); 
filterRegistration.setEnabled(true); 
filterRegistration.addUrlPatterns(casClientProperties.getFilterUrl ()); 
filterRegistration.addInitParameter("casServerUrlPrefix", casClientProperties.getCasServerLoginUrl()); 
filterRegistration.addInitParameter("serverName", casClientProperties.getServerName()); 
filterRegistration.setOrder(3); 
return filterRegistration; 
} 
/** 
* 该过滤器负责用户的认证工作 
* @return 
*/ 
@Bean 
public FilterRegistrationBean authenticationFilterRegistrationBean() { 
FilterRegistrationBean authenticationFilter = new FilterRegistrationBean(); 
authenticationFilter.setFilter(new AuthenticationFilter()); 
Map<String, String> initParameters = new HashMap<>(); 
initParameters.put("casServerLoginUrl", casClientProperties.getCasServerLoginUrl()); 
initParameters.put("ignorePattern", "/openApi/|/recall/|/test/|/health|/open/|/error*|/webjars/|/swagger*|/Mei*|/assets*|/v2/api*"); 
initParameters.put("serverName", casClientProperties.getServerName()); 
authenticationFilter.setInitParameters(initParameters); 
authenticationFilter.setOrder(4); 
List<String> urlPatterns = new ArrayList<>(); 
urlPatterns.add(casClientProperties.getFilterUrl()); 
authenticationFilter.setUrlPatterns(urlPatterns); 
return authenticationFilter; 
} 
/** 
* 该过滤器负责对Ticket的校验工作 
* @return 
*/ 
@Bean 
public FilterRegistrationBean cas20ProxyReceivingTicketValidationFilter() { 
FilterRegistrationBean registrationBean = new FilterRegistrationBean(); 
registrationBean.setFilter(new Cas30ProxyReceivingTicketValidationFilter()); 
registrationBean.addUrlPatterns("/*"); 
registrationBean.addInitParameter("casServerUrlPrefix", casClientProperties.getCasServerUrlPrefix()); 
registrationBean.addInitParameter("serverName", casClientProperties.getServerName()); 
registrationBean.addInitParameter("useSession", String.valueOf(true)); 
registrationBean.addInitParameter("exceptionOnValidationFailure", String.valueOf(false)); 
registrationBean.addInitParameter("redirectAfterValidation", String.valueOf(true)); 
registrationBean.setEnabled(casClientProperties.isEnable()); 
registrationBean.setOrder(4); 
return registrationBean; 
} 
/** 
* 该过滤器对HttpServletRequest请求包装, 可通过HttpServletRequest的getRemoteUser()方法获得登录用户的登录名 
* @return 
*/ 
@Bean 
public FilterRegistrationBean casHttpServletRequestWrapperFilter(){ 
FilterRegistrationBean authenticationFilter = new FilterRegistrationBean(); 
authenticationFilter.setFilter(new HttpServletRequestWrapperFilter()); 
authenticationFilter.setOrder(6); 
List<String> urlPatterns = new ArrayList<String>(); 
urlPatterns.add(casClientProperties.getFilterUrl()); 
authenticationFilter.setUrlPatterns(urlPatterns); 
return authenticationFilter; 
} 
/** 
* 该过滤器使得可以通过org.jasig.cas.client.util.AssertionHolder来获取用户的登录名。 
* 比如AssertionHolder.getAssertion().getPrincipal().getName()。 
* 这个类把Assertion信息放在ThreadLocal变量中,这样应用程序不在web层也能够获取到当前登录信息 
* @return 
*/ 
@Bean 
public FilterRegistrationBean casAssertionThreadLocalFilter(){ 
FilterRegistrationBean authenticationFilter = new FilterRegistrationBean(); 
authenticationFilter.setFilter(new AssertionThreadLocalFilter()); 
authenticationFilter.setOrder(7); 
List<String> urlPatterns = new ArrayList<String>(); 
urlPatterns.add(casClientProperties.getFilterUrl()); 
authenticationFilter.setUrlPatterns(urlPatterns); 
return authenticationFilter; 
} 
} 
@Configuration 
public class CasClientProperties { 
/** 
* 是否开启单点登录 
*/ 
private boolean enable = true; 
/** 
* 单点登录需要访问的CAS SERVER URL入口 
*/ 
private String casServerLoginUrl; 
/** 
* 托管此应用的服务器名称,例如本机:http://localhost:8080 
*/ 
private String serverName; 
/** 
* cas服务器的开头 例如 http://localhost:8443/cas 
*/ 
private String casServerUrlPrefix; 
/** 
* 验证白名单,当请求路径匹配此表达式时,自动通过验证 
*/ 
private String ignorePattern; 
/** 
* 白名单表达式的类型 
* REGEX 正则表达式 默认的 
* CONTAINS 包含匹配 
* EXACT 精确匹配 
*/ 
private String ignoreUrlPatternType; 
private String filterUrl; 
setget方法 
} 

4、新增登陆成功页面

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Controller 
public class IndexController { 
@RequestMapping("/index") 
public String sayHello(){ 
//方案一:获取其他属性和名字 
Map<String,Object> map = AssertionHolder.getAssertion().getPrincipal().getAttributes(); 
AssertionHolder.getAssertion().getPrincipal().getName(); 
//方案二:获取其他属性和名字 
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); 
Principal principal = request.getUserPrincipal(); 
Map<String, Object> attributes = ((AttributePrincipal) principal).getAttributes(); 
return "index"; 
} 
/** 
* 验证会不会被拦截 
* @return 
*/ 
@RequestMapping("/openApi/openApi") 
public String openApi(){ 
return "openApi"; 
} 
} 

新增index.html页面和openApi.html

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<!DOCTYPE html> 
<html lang="en" xmlns="http://www.thymeleaf.org"> 
<head> 
<meta charset="UTF-8"> 
<title>Title</title> 
</head> 
<body> 
index 
</body> 
</html> 

CAS 安全性

TGC的安全性

对于一个CAS用户来说,最重要是要保护它的 TGC ,如果 TGC 不慎被 CAS Server 以外的实体获得, Hacker 能够找到该 TGC ,然后冒充 CAS 用户访问所有授权资源。

从基础模式可以看出, TGC 是 CAS Server 通过 SSL 方式发送给终端用户,因此,要截取 TGC 难度非常大,从而确保 CAS 的安全性。所以CAS的安全性是依赖于SSL的。

TGC 面临的风险主要并非传输窃取。比如你登陆了之后,没有 Logout ,离开了电脑,别人就可以打开你的浏览器,直接访问你授权访问的应用 ,设置一个 TGC 的有效期,可以减少被别人盗用

Service Ticket安全性

首要明白, Service Ticket 是通过 Http 传送的,所有网络中的其他人可以 Sniffer 到其他人的 Ticket 。

CAS 协议从几个方面让 Service Ticket 变得更加安全。

1、Service Ticket 只能使用一次。 CAS 协议规定,无论 Service Ticket 验证是否成功, CAS Server 都会将服务端的缓存中清除该 Ticket ,从而可以确保一个 Service Ticket 不能被使用两次。

2、Service Ticket 在一段时间内失效。 假设用户拿到 Service Ticket 之后,他请求 helloservice 的过程又被中断了, Service Ticket 就被空置了,事实上,此时, Service Ticket 仍然有效。 CAS 规定 Service Ticket 只能存活一定的时间,然后 CAS Server 会让它失效。

url带jsessionid处理

如下面链接,当我们从cas登录成功后跳回页面,url后面带上jsessionid,这样看起来很别扭 http://127.0.0.1:10086/index;jsessionid=5D9A11B35C145518155D141B771368F0

去掉jsessionid方法 我们只需要在cas登录页面form表单提交的时候设置method=”POST”,post要大写,小写不行

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<form method="POST" th:object="${credential}"></form> 

CAS返回多属性

默认情况下CAS只会返回username给客户端,但是在实际情况下,一个username是不能满足我们的要求的,我们可能需要邮件,电话号码,权限等数据,所以我们需要对CAS进行改造 1、修改Cas server的services文件

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
修改services可以设置不同的客户端返回不同的属性 
使用ReturnAllAttributeReleasePolicy表示返回所有的属性,当然也可以限制返回部分属性,限制哪些属性不能返回。 
{ 
"@class" : "org.apereo.cas.services.RegexRegisteredService", 
"serviceId" : "^(https||http|imaps)://.*", 
"name" : "HTTPS and IMAPS", 
"id" : 10000001, 
"description" : "自定义登陆页面 theme 获取templates/blog/casLoginView.html", 
"evaluationOrder" : 10000, 
"theme": "blog", 
"attributeReleasePolicy": { 
"@class": "org.apereo.cas.services.ReturnAllAttributeReleasePolicy" 
} 
} 

2、在CAS server修改登陆处理逻辑

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
。。。。 
。。。。 
//返回多属性 
Map<String, Object> map=new HashMap<>(); 
map.put("email", "34865666@qq.com"); 
map.put("phone", "18850588888"); 
if(username.equals("admin") && pd.equals("123456")){ 
return createHandlerResult(usernamePasswordCredential, principalFactory.createPrincipal(username, map), null); 
} 
throw new FailedLoginException("Sorry, login attemp failed."); 

3、修改客户端 原本我以为只要执行上面2个步骤就行了。但是实际上,我在客户端还是获取不到。 这里我的CAS是5.2.26版本,原本是使用CAS2.0协议的,需要改成CAS3.0协议,

我们需要在检查Ticket的时候使用Cas30ProxyReceivingTicketValidationFilter,原本是使用Cas20ProxyReceivingTicketValidationFilter不行

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/** 
* 该过滤器负责对Ticket的校验工作 
* @return 
*/ 
@Bean 
public FilterRegistrationBean cas20ProxyReceivingTicketValidationFilter() { 
FilterRegistrationBean registrationBean = new FilterRegistrationBean(); 
registrationBean.setFilter(new Cas30ProxyReceivingTicketValidationFilter()); 
registrationBean.addUrlPatterns("/*"); 
registrationBean.addInitParameter("casServerUrlPrefix", casClientProperties.getCasServerUrlPrefix()); 
registrationBean.addInitParameter("serverName", casClientProperties.getServerName()); 
registrationBean.addInitParameter("useSession", String.valueOf(true)); 
registrationBean.addInitParameter("exceptionOnValidationFailure", String.valueOf(false)); 
registrationBean.addInitParameter("redirectAfterValidation", String.valueOf(true)); 
registrationBean.setEnabled(casClientProperties.isEnable()); 
registrationBean.setOrder(4); 
return registrationBean; 

4.客戶端获取数据

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@RequestMapping("/index") 
public String sayHello(){ 
//方案一:获取其他属性和名字 
Map<String,Object> map = AssertionHolder.getAssertion().getPrincipal().getAttributes(); 
AssertionHolder.getAssertion().getPrincipal().getName(); 
//方案二:获取其他属性和名字 
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); 
Principal principal = request.getUserPrincipal(); 
Map<String, Object> attributes = ((AttributePrincipal) principal).getAttributes(); 
return "index"; 
} 

参考

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-05-19 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
CAS单点登录系列之极速入门与实战教程(4.2.7)
单点登录(Single sign on),英文名称缩写SSO,SSO的意思就是在多系统的环境中,登录单方系统,就可以在不用再次登录的情况下访问相关受信任的系统。也就是说只要登录一次单体系统就可以。
SmileNicky
2020/04/13
6.1K0
Springboot 集成 Shiro 和 CAS 实现单点登录(服务端篇CAS5)
先说一个需求场景,比如:一个企业的内部有N多个子系统,每个子系统都有一套自己的用户名和密码,那么企业的员工要登录N个子系统,这样一个员工 就要记住N个用户名和密码,就算各个子系统的用户名和密码都是统一的,登录每个子系统都要输入用户名和密码进行登录也是一个繁琐的操作过程,那么单点登录功能由此便应运而生了。 单点登录(Single Sign On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一。SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。
BUG弄潮儿
2022/02/10
1.8K0
Springboot 集成 Shiro 和 CAS 实现单点登录(服务端篇CAS5)
springboot 集成CAS 实现单点登录
最近新参与的项目用到了cas单点登录,我还不会,这怎么能容忍!空了学习并搭建了一个spring-boot 集成CAS 的demo。实现了单点登录与登出。
Mshu
2020/06/22
9.3K3
单点登录(二)| OAuth 授权框架及 CAS 在为 Web 应用系统提供的解决方案实践
OAuth2是一个授权框架,或称为授权标准,可以使第三方应用程序或客户端获得对http服务上用户账号信息的有限访问权限。
杰哥的IT之旅
2020/06/18
5.7K0
单点登录(二)| OAuth 授权框架及 CAS 在为 Web 应用系统提供的解决方案实践
CAS单点登录-自定义认证登录策略(五)
在上一节中我们使用了CAS的提供的JDBC 方式的登录认证,基本上能够满足我们多种需求的认证。 但是如果CAS框架提供的方案还是不能满足我们的需要,比如我们不仅需要用户名和密码,还要验证其他信息,比如邮箱,手机号,但是邮箱,手机信息在另一个数据库,还有在一段时间内同一IP输入错误次数限制等。这里就需要我们自定义认证策略,自定义CAS的web认证流程。
用户1212940
2022/04/13
1.6K0
CAS单点登录-自定义认证登录策略(五)
springboot集成CAS单点登录客户端
5.定义本地过滤器,作用是通过从单点登录服务器获取用户账号,将登录的用户账号存到session中。(CAS服务端在认证通过后,会把当前认证通过的登陆用户名传递到子系统,当然,认证通过的用户名有可能与子系统的用户名不一样,那子系统就需要一个认证通过的用户名与子系统用户的映射,在子系统拿到通过认证的用户名,再找到对应的子系统用户)
全栈程序员站长
2022/08/28
1.6K0
前后端分离模式下,SpringBoot + CAS 单点登录实现方案
修改apache-tomcat-8.5.53\webapps\cas\WEB-INF\classes\services目录下的HTTPSandIMAPS-10000001.json,在serviceId中添加http即可
码猿技术专栏
2023/05/01
4.4K1
前后端分离模式下,SpringBoot + CAS 单点登录实现方案
CAS单点登录-基础搭建HelloWorld(二)
官方提供了手脚架工具,可以自定去定义自己的项目,但里面用到了一个query的cdn需要访问国外网站
用户1212940
2022/04/13
6090
CAS单点登录-基础搭建HelloWorld(二)
CAS 5.3.1系列之自定义Shiro认证策略(四)
CAS官方文档是介绍基于配置实现shiro认证的,可以参考官方文档,不过我们也可以通过自定义认证策略的方式实现jdbc认证,pom先加入相关jar
SmileNicky
2020/05/06
1.3K0
SpringBoot+Vue 前后端分离实现单点登录方案
文章目录 前言 一、CAS是什么? 二、搭建客户端系统 引入CAS 客户端后端搭建 总结 前言 什么是单点登录?单点登录全称Single Sign On(以下简称SSO),是指在多系统应用群中登录一个系统,便可在其他所有系统中得到授权而无需再次登录,包括单点登录与单点注销两部分,如图(不标准,只是方便理解)。 一、CAS是什么? CAS 是 Yale 大学发起的一个开源项目,旨在为 Web 应用系统提供一种可靠的单点登录方法,CAS 在 2004 年 12 月正式成为 JA-SIG 的一个项目。CAS 具
java思维导图
2022/08/26
2K0
SpringBoot+Vue 前后端分离实现单点登录方案
CAS单点登录-动态添加services(七)
前面我们整合客户端的时候,需要在cas服务端注册,使用的是json文件的方式,直接通过配置文件完成配置,但是也存在一定的不方便性。 假如,我们以域名配置的,比如:http://app1.cas.com 注册,那么又有新的模块为 http://app2.cas.com 我们总不能每次修改配置,重启cas服务吧。这很不现实,官网给出了如下的解决方式,将数据库来存储这些数据。
用户1212940
2022/04/13
1.4K0
CAS单点登录-动态添加services(七)
CAS 5.3.1系列之客户端对接(五)
我们要接入客户端可以常用第三方的库cas-client-autoconfig-support来对接,比较快捷,迅速实现,或者可以用cas-client-support-springboot集成到boot项目
SmileNicky
2020/05/06
2.5K0
Spring Boot+CAS 单点登录,如何对接数据库?
我们用 CAS Server 做单点登录,CAS Server 主要是负责认证的,也就是它主要解决登录问题。登录成功之后,还有一个权限处理的问题,权限的问题则交由各个 CAS Client 自行处理,并不在 CAS Server 中完成。
江南一点雨
2020/06/10
2.1K0
Spring Boot+CAS 单点登录,如何对接数据库?
SSO CAS
访问服务 -> 丁香认证 -> 用户认证 -> 发放票据 -> 验证票据 -> 传递用户信息
matt
2022/10/25
1.2K0
CAS 5.3.1系列之自定义JDBC认证策略(三)
CAS官方文档是介绍基于配置实现jdbc认证的,可以参考我博客:CAS 5.3.1系列之支持JDBC认证登录(二),不过我们也可以通过自定义认证策略的方式实现jdbc认证,pom先加入相关jar
SmileNicky
2020/05/04
1.6K0
SSO统一身份认证——CAS Client客户端创建(九)
单点登录(SingleSignOn,SSO),就是通过用户的一次性鉴别登录。当用户在身份认证服务器上登录一次以后,即可获得访问单点登录系统中其他关联系统和应用软件的权限,同时这种实现是不需要管理员对用户的登录状态或其他信息进行修改的,这意味着在多个应用系统中,用户只需一次登录就可以访问所有相互信任的应用系统。这种方式减少了由登录产生的时间消耗,辅助了用户管理,是目前比较流行的。
cn華少
2021/07/29
1.9K3
SSO统一身份认证——CAS Client客户端创建(九)
【SpringSecurity系列(二十五)】CAS 单点登录对接数据库
《深入浅出Spring Security》一书已由清华大学出版社正式出版发行,感兴趣的小伙伴戳这里->->>深入浅出Spring Security,一本书学会 Spring Security。
江南一点雨
2021/07/15
1.1K0
【SpringSecurity系列(二十五)】CAS 单点登录对接数据库
CAS 5.3.1系列之支持JDBC认证登录(二)
在项目中,我们肯定是不能用默认的静态账号密码,所以我们需要实现对jdbc或者其它认证方式的支持,将cas-overlay-template-5.2\pom.xml复制到项目里,将application.properties复制到resources文件夹
SmileNicky
2020/05/05
1.5K0
CAS单点登录-自定义认证之JDBC+MD5(四)
在不同公司,可能有很多业务需求或者架构不一样导致我们实现验证的方式不一样,那么cas为我们提供了很多认证的模式(当然也可以自定义),其中常用的有:
用户1212940
2022/04/13
9300
CAS单点登录-自定义认证之JDBC+MD5(四)
单点登录之CAS原理和实现
单点登录(Single Sign On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一。SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。
全栈程序员站长
2022/06/26
1.5K0
单点登录之CAS原理和实现
推荐阅读
相关推荐
CAS单点登录系列之极速入门与实战教程(4.2.7)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验