CAS单点登录(九)——客户端接入
CAS单点登录(九)——客户端接入
在前面的CAS系列文章中,我们讲解了CAS从搭建到具体的自定义配置,但针对的都是CAS服务端的知识,今天我们就来讲解一下CAS的客户端的知识点。
我们知道CAS分为客户端和服务端,在前面的一系列文章中,我们主要讲解了CAS中主要的属性和特点,对CAS的部署和配置有了一定的了解,但是我们该如何接入CAS系统呢?这就需要我们配置CAS的客户端,从而实现CAS系统的接入。其实在前面的文章中,我们就讲解了CAS基本用法的demo,在官方的代码中也为我们提供了样例,但是比较简单,没有详细分析,今天我们就来仔细分析一下。
由于各个系统使用的语言不同,具体提供的不同CAS的客户端接入方法也不同,但是都大同小异,这里我就不一一介绍了,官方为我们提供Java、C#、PHP的客户端代码,其他语言的客户端也可以在github上找到。这里我们就主要以Java语言接入来介绍。
这里主要介绍两种方式接入,一种是Java Web,另一种是使用Spring Boot来接入CAS客户端系统。
一、Java Web配置CAS
这里我们先介绍使用的Java Web方式,这里我们还是结合原来讲解的代码cas-sample-java-webapp来分析,在cas-sample-java-webapp代码的resources下的webapp下面的web.xml中进行配置。在配置文档中,我们也可以查看到具体的配置指南。
首先我们要配置的是org.jasig.cas.client.authentication.AuthenticationFilter,文档也指出了这个类的作用是用户是否要检测,是否已经登录了,如果没有,跳转到CAS服务器端进行登录匹配认证。
同时文档也给出了相应的属性含义,这里大致介绍一下重要的属性信息。
casServerUrlPrefix:cas服务端认证URL地址,比如https://sso.anumbrella.net/cas。
casServerLoginUrl:cas登录URL,比如https://sso.anumbrella.net/cas/login,如果casServerLoginUrl设置将会覆盖casServerUrlPrefix。也就是casServerUrlPrefix和casServerLoginUrl设置一个就行了。
serverName:接入服务(客户端)的URL地址,包含协议和端口,比如:https://client.anumbrella.net:9443。
renew:指定是否应将renew = true发送到CAS服务器。 有效值为true / false(或根本没有值)。 请注意,不能在本地init-param设置renew。该值得作用是:CAS协议允许客户端选择是否跳出单点登录(强制重新登录),这就是renew。它允许一个客户端通知CAS服务器总是验证一个用户,不管一个单点登录的session是否存在。这是一个非常有用的属性,当一个特定的使用CAS认证机制的服务允许访问敏感资料时,它能强迫CAS重新认证一个用户,确保登录的是一个正确的用户。
ignorePattern:定义认证时候忽略的URL信息。
ignoreUrlPatternType:定义URL忽略模式,默认为REGEX模式,还包含CONTAINS、EXACT、FULL_REGEX。具体信息如下:
除了这个验证类外,还有一org.jasig.cas.client.authentication.Saml11AuthenticationFilter,这个类的作用同上面org.jasig.cas.client.authentication.AuthenticationFilter一样,是对用户进行检测,是否已经登录了,如果没有,跳转到CAS服务器端进行登录匹配认证。不同之处是使用的SAML 1.1来验证。
具体配置的属性同上面一致,这里就不再复述了。
接着是org.jasig.cas.client.validation.Cas10TicketValidationFilter和
org.jasig.cas.client.validation.Saml11TicketValidationFilter类这里是指验证tickets分别使用CAS 1.0 Protocol和SAML 1.1 Protocol来验证,除此之外还有org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter、org.jasig.cas.client.validation.Cas30ProxyReceivingTicketValidationFilter
分别是使用CAS 2.0 Protocol和CAS 3.0 Protocol来进行tickets验证。
这里的这几个类主要是验证票据(tickets)的过滤器,分别是使用不同协议来校验。
org.jasig.cas.client.validation.Cas10TicketValidationFilter
org.jasig.cas.client.validation.Saml11TicketValidationFilter
org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter
org.jasig.cas.client.validation.Cas30ProxyReceivingTicketValidationFilter
具体的配置也是大同小异。
casServerUrlPrefix:cas服务端认证URL地址,比如https://sso.anumbrella.net/cas。
serverName: 接入服务(客户端)的URL地址,包含协议和端口,比如:https://client.anumbrella.net:9443。
renew:指定是否应将renew = true发送到CAS服务器。 有效值为true / false(或根本没有值)。 请注意,不能在本地init-param设置renew。该值得作用是:CAS协议允许客户端选择是否跳出单点登录(强制重新登录),这就是renew。它允许一个客户端通知CAS服务器总是验证一个用户,不管一个单点登录的session是否存在。这是一个非常有用的属性,当一个特定的使用CAS认证机制的服务允许访问敏感资料时,它能强迫CAS重新认证一个用户,确保登录的是一个正确的用户。
redirectAfterValidation:是否在票证验证后重定向到相同的URL,但参数中没有票证,默认为ture。
useSession:是否在会话中存储session。如果不使用会话,则每个请求都需要票据。默认为true。
exceptionOnValidationFailure:是否在验证票据失败后,抛出一个异常。默认为true。
org.jasig.cas.client.util.HttpServletRequestWrapperFilter 包装httpservletrequest,以便getRemoteUser和getPrincipal返回与CAS相关的属性。
org.jasig.cas.client.util.AssertionThreadLocalFilter该过滤器使得可以通过org.jasig.cas.client.util.AssertionHolder来获取用户的登录名。 比如AssertionHolder.getAssertion().getPrincipal().getName()。这个类把Assertion信息放在ThreadLocal变量中,这样应用程序不在web层也能够获取到当前登录信息或前端传过来的信息。但它无法访问HttpServletRequest,因此无法调用getRemoteUser()。
org.jasig.cas.client.util.ErrorRedirectFilter根据异常重定向到提供的URL的过滤器。 异常和URL通过init过滤器名称/参数值配置。
上面就是CAS单点登录的相关配置,当然我们知道除了单点登录,还有单点退出,就是一个客户端主动发起退出后,其他的客户端也会相应收到服务端发送的退出请求,让其他的在线客户端也退出。如果不太熟悉概念,可以查看CAS单点登录(一)——初识SSO。
在客户端接入中,同样提供了单点退出的相关配置。
casServerUrlPrefix:cas服务端认证URL地址,比如https://sso.anumbrella.net/cas。
logoutCallbackPath:预期从CAS服务器接收注销回调请求的路径。如果您的应用程序在处理表单发布时需要访问原始输入流,那么这是必需的。如果没有配置,默认行为将检查每个表单发布的注销参数。
同样的单点退出也支持两种协议,配置大致都是相同。一种是CAS Protocol,另一种是SAML Protocol。
CAS Protocol
SAML Protocol
上面便是全部的CAS的相关配置相关信息,包括客户端和服务端,更多具体相关的属性可以参考demo。
二、Spring Boot配置CAS
其实Spring Boot整合CAS客户端和前面Java Web的配置大致相同,前者是在XML中进行配置,而Spring Boot这里则是在配置文件中配置,然后通过注入Java的方式来实现配置的。这里有一个Spring Boot用户登录的基本实例,我们将结合它来进行单点登录的配置。
我们使用这个简单的基于Spring Boot的用户登录系统,将给它来接入CAS系统,这里的登录验证是使用的spring-boot-starter-data-jpa,来验证表user中的用户信息。现在我们介入CAS客户端代码,首先导入依赖。
<!--CAS Client-->
<dependency>
<groupId>org.jasig.cas.client</groupId>
<artifactId>cas-client-core</artifactId>
<version>3.5.1</version>
</dependency>
1
2
3
4
5
6
首先我们在application.properties中添加配置,这里也就是我们上面讲解过的配置信息。
# 监听退出的接口,即所有接口都会进行监听
spring.cas.sign-out-filters=/*
# 需要拦截的认证的接口
spring.cas.auth-filters=/*
spring.cas.validate-filters=/*
spring.cas.request-wrapper-filters=/*
spring.cas.assertion-filters=/*
# 表示忽略拦截的接口,也就是不用进行拦截
spring.cas.ignore-filters=/test
spring.cas.cas-server-login-url=https://sso.anumbrella.net:8443/cas/login
spring.cas.cas-server-url-prefix=https://sso.anumbrella.net:8443/cas/
spring.cas.redirect-after-validation=true
spring.cas.use-session=true
spring.cas.server-name=https://client.anumbrella.net:9443
1
2
3
4
5
6
7
8
9
10
11
12
13
14
然后再新建bean类SpringCasAutoconfig,读取配置文件中的信息。
package net.anumbrella.sso.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.Arrays;
import java.util.List;
/**
* @author Anumbrella
*/
@ConfigurationProperties(prefix ="spring.cas")
public class SpringCasAutoconfig {
static final String separator = ",";
private String validateFilters;
private String signOutFilters;
private String authFilters;
private String assertionFilters;
private String requestWrapperFilters;
private String ignoreFilters; //需要放行的url,多个可以使用|分隔,遵循正则
private String casServerUrlPrefix;
private String casServerLoginUrl;
private String serverName;
private boolean useSession = true;
private boolean redirectAfterValidation = true;
public String getIgnoreFilters() {
return ignoreFilters;
}
public void setIgnoreFilters(String ignoreFilters) {
this.ignoreFilters = ignoreFilters;
}
public List<String> getValidateFilters() {
return Arrays.asList(validateFilters.split(separator));
}
public void setValidateFilters(String validateFilters) {
this.validateFilters = validateFilters;
}
public List<String> getSignOutFilters() {
return Arrays.asList(signOutFilters.split(separator));
}
public void setSignOutFilters(String signOutFilters) {
this.signOutFilters = signOutFilters;
}
public List<String> getAuthFilters() {
return Arrays.asList(authFilters.split(separator));
}
public void setAuthFilters(String authFilters) {
this.authFilters = authFilters;
}
public List<String> getAssertionFilters() {
return Arrays.asList(assertionFilters.split(separator));
}
public void setAssertionFilters(String assertionFilters) {
this.assertionFilters = assertionFilters;
}
public List<String> getRequestWrapperFilters() {
return Arrays.asList(requestWrapperFilters.split(separator));
}
public void setRequestWrapperFilters(String requestWrapperFilters) {
this.requestWrapperFilters = requestWrapperFilters;
}
public String getCasServerUrlPrefix() {
return casServerUrlPrefix;
}
public void setCasServerUrlPrefix(String casServerUrlPrefix) {
this.casServerUrlPrefix = casServerUrlPrefix;
}
public String getCasServerLoginUrl() {
return casServerLoginUrl;
}
public void setCasServerLoginUrl(String casServerLoginUrl) {
this.casServerLoginUrl = casServerLoginUrl;
}
public String getServerName() {
return serverName;
}
public void setServerName(String serverName) {
this.serverName = serverName;
}
public boolean isRedirectAfterValidation() {
return redirectAfterValidation;
}
public void setRedirectAfterValidation(boolean redirectAfterValidation) {
this.redirectAfterValidation = redirectAfterValidation;
}
public boolean isUseSession() {
return useSession;
}
public void setUseSession(boolean useSession) {
this.useSession = useSession;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
最后配置以下注入CAS的配置,这些配置同上面介绍的一致,只是这里是通过Java Bean方式注入,来实现客户端接入。
package net.anumbrella.sso.config;
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 org.springframework.stereotype.Component;
/**
* @author Anumbrella
*/
@Configuration
@Component
public class CasCustomConfig {
@Autowired
SpringCasAutoconfig autoconfig;
private static boolean casEnabled = true;
public CasCustomConfig() {
}
@Bean
public SpringCasAutoconfig getSpringCasAutoconfig() {
return new SpringCasAutoconfig();
}
@Bean
public ServletListenerRegistrationBean<SingleSignOutHttpSessionListener> singleSignOutHttpSessionListener() {
ServletListenerRegistrationBean<SingleSignOutHttpSessionListener> listener = new ServletListenerRegistrationBean<SingleSignOutHttpSessionListener>();
listener.setEnabled(casEnabled);
listener.setListener(new SingleSignOutHttpSessionListener());
listener.setOrder(1);
return listener;
}
/**
* 该过滤器用于实现单点登出功能,单点退出配置,一定要放在其他filter之前
*
* @return
*/
@Bean
public FilterRegistrationBean singleSignOutFilter() {
FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
filterRegistration.setFilter(new SingleSignOutFilter());
filterRegistration.setEnabled(casEnabled);
if (autoconfig.getSignOutFilters().size() > 0) {
filterRegistration.setUrlPatterns(autoconfig.getSignOutFilters());
} else {
filterRegistration.addUrlPatterns("/*");
}
filterRegistration.addInitParameter("casServerUrlPrefix", autoconfig.getCasServerUrlPrefix());
filterRegistration.setOrder(3);
return filterRegistration;
}
/**
* 该过滤器负责用户的认证工作
*
* @return
*/
@Bean
public FilterRegistrationBean authenticationFilter() {
FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
filterRegistration.setFilter(new AuthenticationFilter());
filterRegistration.setEnabled(casEnabled);
if (autoconfig.getAuthFilters().size() > 0) {
filterRegistration.setUrlPatterns(autoconfig.getAuthFilters());
} else {
filterRegistration.addUrlPatterns("/*");
}
if (autoconfig.getIgnoreFilters() != null) {
filterRegistration.addInitParameter("ignorePattern", autoconfig.getIgnoreFilters());
}
filterRegistration.addInitParameter("casServerLoginUrl", autoconfig.getCasServerLoginUrl());
filterRegistration.addInitParameter("serverName", autoconfig.getServerName());
filterRegistration.addInitParameter("useSession", autoconfig.isUseSession() ? "true" : "false");
filterRegistration.addInitParameter("redirectAfterValidation", autoconfig.isRedirectAfterValidation() ? "true" : "false");
filterRegistration.setOrder(4);
return filterRegistration;
}
/**
* 该过滤器负责对Ticket的校验工作,使用CAS 3.0协议
*
* @return
*/
@Bean
public FilterRegistrationBean cas30ProxyReceivingTicketValidationFilter() {
FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
filterRegistration.setFilter(new Cas30ProxyReceivingTicketValidationFilter());
filterRegistration.setEnabled(casEnabled);
if (autoconfig.getValidateFilters().size() > 0) {
filterRegistration.setUrlPatterns(autoconfig.getValidateFilters());
} else {
filterRegistration.addUrlPatterns("/*");
}
filterRegistration.addInitParameter("casServerUrlPrefix", autoconfig.getCasServerUrlPrefix());
filterRegistration.addInitParameter("serverName", autoconfig.getServerName());
filterRegistration.setOrder(5);
return filterRegistration;
}
@Bean
public FilterRegistrationBean httpServletRequestWrapperFilter() {
FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
filterRegistration.setFilter(new HttpServletRequestWrapperFilter());
filterRegistration.setEnabled(true);
if (autoconfig.getRequestWrapperFilters().size() > 0) {
filterRegistration.setUrlPatterns(autoconfig.getRequestWrapperFilters());
} else {
filterRegistration.addUrlPatterns("/*");
}
filterRegistration.setOrder(6);
return filterRegistration;
}
/**
* 该过滤器使得可以通过org.jasig.cas.client.util.AssertionHolder来获取用户的登录名。
* 比如AssertionHolder.getAssertion().getPrincipal().getName()。
* 这个类把Assertion信息放在ThreadLocal变量中,这样应用程序不在web层也能够获取到当前登录信息
*
* @return
*/
@Bean
public FilterRegistrationBean assertionThreadLocalFilter() {
FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
filterRegistration.setFilter(new AssertionThreadLocalFilter());
filterRegistration.setEnabled(true);
if (autoconfig.getAssertionFilters().size() > 0) {
filterRegistration.setUrlPatterns(autoconfig.getAssertionFilters());
} else {
filterRegistration.addUrlPatterns("/*");
}
filterRegistration.setOrder(7);
return filterRegistration;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
那么当我登陆成功后,如何判断呢?我们可以通过下面的方法获取CAS给我们传递回来的对象,放置在session中。
Assertion assertion = (Assertion) request.getSession().getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION);
1
如下,我们获取登陆的用户名,并打印出来。
//获取cas给我们传递回来的对象,这个东西放到了session中
//session的 key是 _const_cas_assertion_
Assertion assertion = (Assertion) request.getSession().getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION);
//获取登录用户名
String loginName = assertion.getPrincipal().getName();
System.out.printf("登录用户名:%s\r\n", loginName);
1
2
3
4
5
6
7
到此我们的Spring Boot接入CAS就完成了,输入实例地址,我们可以发现出现了我们熟悉的CAS登录界面。
除了使用官方的jar包外,我们还可以使用第三方开发的cas-client包,它将Spring Boot集合CAS做了封装,更简单。比如cas-client-autoconfig-support,通过简单配置和加上@EnableCasClient注解就实现了集成CAS客户端。
代码实例:Chapter8
参考
spring boot-整合CAS Client实现单点登陆验证
SpringBoot整合cas单点登录
https://github.com/Unicon/cas-client-autoconfig-support
https://github.com/apereo/java-cas-client
点赞 1
————————————————
版权声明:本文为CSDN博主「Anumbrella」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Anumbrella/article/details/87897230