文章目录
一、 CAS简介
Cas的全称是Centeral Authentication Service,是对单点登录SSO(Single Sign On)
的一种实现。其由Cas Server和Cas Client两部分组成,Cas Server是核心,而Cas Client通常就对应于我们的应用。一个Cas Server可以对应于多个Cas Client。它允许我们在一个Client进行登录以后无需再让用户输入用户名和密码进行认证即可访问其它Client应用。
Cas Server的主要作用是通过发行和验证Ticket(票)来对用户进行认证和授权访问Client应用,用于认证的凭证信息都是由Cas Server管理的。而Cas Client就对应于我们真正的应用,当然其中会使用到Cas相关的类,用于与Cas Server进行交互。
二、 CAS认证流程
下图是官网上用户认证的流程图
用户首次访问应用app1
如你所见,在第一次访问应用app1时,由于没有登录会直接跳转到Cas Server去进行登录认证,此时将附带查询参数service在Cas Server的登录地址上,表示登录成功后将要跳转的地址。
此时Cas Server检查到没有之前成功登录后生成的SSO Session信息,那么就会引导用户到登录页面进行登录。如:
https://cas.example.com:8443/cas/login?service=http%3A%2F%2Fapp.example.com%3A8080%2Flogin%2Fcas
用户输入信息提交登录请求,Cas Server认证成功后将生成对应的SSO Session,以及名为CASTGC的cookie,该cookie包含用来确定用户SSO Session的Ticket Granting Ticket(TGT)。之后会生成一个Service Ticket(ST),并将以ticket作为查询参数名,以该ST作为查询参数值跳转到登录时service对应的URL。如:
http://app.example.com:8080/login/cas?ticket=ST-7-hXaLlWKHnwc-RpxHEyVf0aQVF0wxxxxxx
之后的操作对用户来说都是透明的,即不可见的。
app1之后将以service和ticket作为查询参数请求Cas Server对service进行验证,地址举例:
https://cas.example.com:8443/cas/p3/serviceValidate?ticket=ST-15-K7SRE776Y1e4LtJqcMOzzaehcbgmaruilei&service=http%3A%2F%2Fapp.example.com%3A8080%2Flogin%2Fcas
验证通过后Cas Server将返回当前用户的用户名等信息。app1就会给当前用户生成其自身的Session,以后该用户以该Session都可以成功的访问app1,而不需要再去请求Cas Server进行认证。流程图如下
第二次访问app1
当该用户再去访问app2的时候,由于其在app2上没有对应的Session信息,将会跳转到Cas Server的登录地址,Cas Server此时发现其包含名为CASTGC的cookie,将获取其中包含的TGT来获取对应的SSO Session,然后会将用户重定向到app2对应的地址,以Service Ticket作为查询参数。之后app2会向Cas Server发送请求校验该Service Ticket,校验成功后app2将建立该用户对应的Session信息,以后该用户以该Session就可以自由的访问app2了。
综上所述,我们知道,各系统之间的单点登录是通过Cas Server生成的SSO Session来交流的,而用户与实际的应用系统进行交互的时候,各应用系统将建立单独的Session,以满足用户与该系统的交互需求。
三、 接入CAS所需配置
pom.xml引入文件配置
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-cas</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
application.yml配置
cas:
server:
prefix: https://cas.example.com:8443/cas
login: /login
logout: /logout
client:
prefix: http://127.0.0.1:8080/clientapp
login: /login/cas
logout: /logout/cas
cas相关过滤器、入口配置
@Configuration
public class CasSecurityProperties {
@Value("${cas.server.prefix}")
private String casServerPrefix;
@Value("${cas.server.login}")
private String casServerLogin;
@Value("${cas.server.logout}")
private String casServerLogout;
@Value("${cas.client.prefix}")
private String casClientPrefix;
@Value("${cas.client.login}")
private String casClientLogin;
@Value("${cas.client.logout}")
private String casClientLogout;
/**
* 设置本cas服务的属性bean
* serviceProperties.setAuthenticateAllArtifacts(true);
* 设置带有ticket参数的请求都可以触发认证,主要在代理认证时使用
* @return
*/
@Bean
public ServiceProperties serviceProperties() {
ServiceProperties serviceProperties = new ServiceProperties();
serviceProperties.setService(casClientPrefix + casClientLogin);
serviceProperties.setAuthenticateAllArtifacts(true);
return serviceProperties;
}
/**
* 票据验证,使用cas3.0 代理票据认证器
* 传递cas server地址进行初始化
*/
@Bean
public Cas30ProxyTicketValidator ticketValidator() {
Cas30ProxyTicketValidator validator = new Cas30ProxyTicketValidator(casServerPrefix);
return validator;
}
/**
* 接收cas服务器发出的注销请求
*/
@Bean
public SingleSignOutFilter singleSignOutFilter() {
SingleSignOutFilter singleSignOutFilter = new SingleSignOutFilter();
singleSignOutFilter.setCasServerUrlPrefix(casServerPrefix);
singleSignOutFilter.setIgnoreInitConfiguration(true);
return singleSignOutFilter;
}
@Bean
public LogoutSuccessHandler logoutSuccessHandler() {
return new MyLogoutSuccessHandler();
}
/**
* 注销请求转发到cas server
* 由于登出会重定向到cas server页面,发生跨域,
* 此处重写了LogoutSuccessHandler,后台只进行session销毁,然后通知前端进行登出跳转
*/
@Bean
public LogoutFilter logoutFilter(LogoutSuccessHandler logoutSuccessHandler) {
LogoutFilter logoutFilter = new LogoutFilter(logoutSuccessHandler, new SecurityContextLogoutHandler());
logoutFilter.setFilterProcessesUrl(casClientLogout);
return logoutFilter;
}
@Bean
public UserDetailsService userDetailsService() {
UserServiceImpl userService = new UserServiceImpl();
return userService;
}
@Bean
public CasAuthenticationProvider casAuthenticationProvider(ServiceProperties sp, TicketValidator ticketValidator, UserDetailsService userDetailsService) {
CasAuthenticationProvider casAuthenticationProvider = new CasAuthenticationProvider();
casAuthenticationProvider.setKey("casProvider");
casAuthenticationProvider.setServiceProperties(sp);
casAuthenticationProvider.setTicketValidator(ticketValidator