CAS创建服务端:SSO单点登录(一)创建CAS服务端_我是混IT圈的
由于CAS需要用到域名,所以需要把本地ip映射成域名,参考文档:Win10 如何把本地ip映射成域名_我是混IT圈的
cas客户端示例下载:https://github.com/cas-projects/cas-sample-java-webapp
这个CAS客户端是一个maven项目,可以参考,但是如果想要和springboot整合,可以看下面的配置。
SpringBoot创建CAS客户端如下:
SpringBoot的版本:
<version>2.4.10</version>
pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--基于注解的配置 CAS,这个包是必须要的-->
<dependency>
<groupId>net.unicon.cas</groupId>
<artifactId>cas-client-autoconfig-support</artifactId>
<version>2.0.0-GA</version>
</dependency>
配置文件:application.properties
#端口号
server.port=8888
#CAS服务端的地址
cas.server-url-prefix=http://sso.cas.com:8080/cas
#客户端如果要登录,会跳转到统一的服务认证中的登录界面,也就是CAS服务端的登录地址
cas.server-login-url=http://sso.cas.com:8080/cas/login
#客户端在CAS服务端登录成功后,自动从CAS服务端跳转回客户端的地址
cas.client-host-url=http://a.cas.com:8888
#校验后的CAS服务端颁发给客户端的用户信息是否需要放入session中。session获取用户信息,key:_const_cas_assertion_ value:assertion
cas.use-session=true
#Ticket校验器使用 Cas30ProxyReceivingTicketValidationFilter
cas.validation-type=CAS3
#不需要拦截的请求,可以用*做模糊匹配,多个用【|】竖线隔开
udf.ignorePattern=/home.html|/*.js|/*.css
#自定义拦截的路径
udf.ignoreUrlPatternType=com.example.casclienta.config.SimpleUrlPatternMatcherStrategy
配置文件中的一些url,根据实际情况来配置,因为为了让单点登录产生效果,所以必须要用域名才可以。
配置类:CasConfig
package com.example.casclientb.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.HttpServletRequestWrapperFilter;
import org.jasig.cas.client.validation.Cas30ProxyReceivingTicketValidationFilter;
import org.springframework.beans.factory.annotation.Value;
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.EventListener;
import java.util.HashMap;
import java.util.Map;
/**
* Cas客户端的配置
*/
@Configuration
public class CasConfig {
@Value("${cas.server-url-prefix}")
private String serverUrlPrefix;
@Value("${cas.server-login-url}")
private String serverLoginUrl;
@Value("${cas.client-host-url}")
private String clientHostUrl;
@Value("${udf.ignorePattern}")
private String ignorePattern;
@Value("${udf.ignoreUrlPatternType}")
private String ignoreUrlPatternType;
@Value("${cas.use-session}")
private String useSession;
/**
* 登出过滤器(过滤器顺序有要求,先登出-》认证/授权过滤器-》校验过滤器-》request wraper 过滤器)
* @return
*/
@Bean
public FilterRegistrationBean filterSingleRegistration() {
final FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new SingleSignOutFilter());
// 设定匹配的路径
registration.addUrlPatterns("/*");
Map<String,String> initParameters = new HashMap<>();
//cas服务端的地址,意思是如果退出了,默认会跳转到这个服务里面去。
initParameters.put("casServerUrlPrefix", serverUrlPrefix);
registration.setInitParameters(initParameters);
// 设定加载的顺序
registration.setOrder(1);
return registration;
}
/**
* 添加监听器
* 它是个登出监听器
* @return
*/
@Bean
public ServletListenerRegistrationBean<EventListener> singleSignOutListenerRegistration() {
ServletListenerRegistrationBean<EventListener> registrationBean = new ServletListenerRegistrationBean<>();
registrationBean.setListener(new SingleSignOutHttpSessionListener());
// 设定加载的顺序
registrationBean.setOrder(1);
return registrationBean;
}
/**
* 配置认证/授权,也就是登录过滤器
* @return
*/
@Bean
public FilterRegistrationBean filterAuthenticationRegistration() {
final FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new AuthenticationFilter());
// 设定匹配的路径
registration.addUrlPatterns("/*");
Map<String,String> initParameters = new HashMap<>();
//客户端如果要登录,会跳转到统一的服务认证中的登录界面,也就是CAS服务端的登录地址
initParameters.put("casServerLoginUrl", serverLoginUrl);
//客户端在CAS服务端登录成功后,自动从CAS服务端跳转回客户端的地址
initParameters.put("serverName", clientHostUrl);
//不需要拦截的请求,必须是正则表达式
if(ignorePattern != null && !"".equals(ignorePattern)){
initParameters.put("ignorePattern", ignorePattern);
}
//自定义鉴权
if(ignoreUrlPatternType != null && !"".equals(ignoreUrlPatternType)){
initParameters.put("ignoreUrlPatternType", ignoreUrlPatternType);
}
registration.setInitParameters(initParameters);
// 设定加载的顺序
registration.setOrder(2);
return registration;
}
/**
* 校验/验证过滤器 这里用的是 Cas30ProxyReceivingTicketValidationFilter
* @return
*/
@Bean
public FilterRegistrationBean filterValidationRegistration() {
final FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new Cas30ProxyReceivingTicketValidationFilter());
// 设定匹配的路径
registration.addUrlPatterns("/*");
Map<String,String> initParameters = new HashMap<String, String>();
//CAS服务端的地址
initParameters.put("casServerUrlPrefix", serverUrlPrefix);
//校验成功后,自动从CAS服务端跳转回客户端的地址,也是登录成功后跳转的地址
initParameters.put("serverName", clientHostUrl);
//校验后的CAS服务端颁发给客户端的用户信息是否需要放入session中。session获取用户信息,key:_const_cas_assertion_ value:assertion
initParameters.put("useSession", useSession);
registration.setInitParameters(initParameters);
// 设定加载的顺序
registration.setOrder(3);
return registration;
}
/**
* request wraper 过滤器
* 重新包装 request 的过滤器
* @return
*/
@Bean
public FilterRegistrationBean filterWrapperRegistration() {
final FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new HttpServletRequestWrapperFilter());
// 设定匹配的路径
registration.addUrlPatterns("/*");
// 设定加载的顺序
registration.setOrder(4);
return registration;
}
}
自定义鉴权类:SimpleUrlPatternMatcherStrategy
import org.jasig.cas.client.authentication.UrlPatternMatcherStrategy;
import java.util.regex.Pattern;
/**
* 自定义鉴权,简单的来说就是自定义需要或者不需要拦截的请求,并且对这些请求可以进行处理
*/
public class SimpleUrlPatternMatcherStrategy implements UrlPatternMatcherStrategy {
//配置文件配置的不需要拦截的请求
private Pattern pattern;
@Override
public boolean matches(String url) {
//在此可以做额外的扩展,比如判断是insert也放行 可以通过查询数据库来进行动态判断
if(url.contains("/insert")){
return true;
}
//默认是根据 cas.ignore-pattern 来判断是否否满足过滤
return this.pattern.matcher(url).find();
}
/**
* 这个pattern就是 在 application.properties 中配置的 udf.ignorePattern
* @param pattern
*/
@Override
public void setPattern(String pattern) {
this.pattern = Pattern.compile(pattern);
}
}
HomeController:
package com.example.casclientb.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import javax.servlet.http.HttpServletRequest;
@Controller
public class HomeController {
//首页,不需要登录就可以进来
@GetMapping(value = "/home.html")
public String index(HttpServletRequest request){
return "home";
}
//详情页面,需要登录
@GetMapping(value = "/details.html")
public String list(HttpServletRequest request){
//cas校验后保存到session中的用户信息,要在session中获取到这个信息,必须在配置中配置。
Object assertion = request.getSession().getAttribute("_const_cas_assertion_");
System.out.println(assertion);
return "details";
}
}
首页:home.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="/static/jquery.min.js"></script>
</head>
<body>
<p>这里是淘宝首页,不需要登录</p>
<a href="/details.html">详情</a>
</body>
</html>
详情页:details.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="static/jquery.min.js"></script>
</head>
<body>
<span>这是淘宝详情页面,需要登录</span><br/>
<a href="http://sso.cas.com:8080/cas/logout?service=http://a.cas.com:8888/home.html">退出</a><br/>
<a href="/home.html">首页</a><br/>
</body>
</html>
启动类,添加注解:
//启动CAS @EnableCasClient
@EnableCasClient
由于CAS是默认使用https的,那么为了能测试,需要把CAS服务端配置成能使用http协议。
上面的配置都可以实现单点登录和单点退出的功能。
下一篇文章请期待:shiro和cas的整合。