CAS - Architecture (apereo.github.io)
CAS - Multifactor Authentication (apereo.github.io)
cas-projects/cas-sample-java-webapp: Sample Java web app protected by Java CAS client (github.com)
在application.properties
中添加
# 从json文件初始化
cas.serviceRegistry.initFromJson=true
cas.serviceRegistry.watcherEnabled=true
cas.serviceRegistry.schedule.repeatInterval=120000
cas.serviceRegistry.schedule.startDelay=15000
cas.serviceRegistry.managementType=DEFAULT
cas.serviceRegistry.json.location=classpath:/services
拷贝target/cas/war/WEB-INF/classes/services/HTTPSandIMAPS-10000001.json
到src/main/resources/services
下
修改src/main/resources/services/HTTPSandIMAPS-10000001.json
在serviceId
正则表达式中添加http
以支持 http 应用接入
{
"@class" : "org.apereo.cas.services.RegexRegisteredService",
"serviceId" : "^(http|https|imaps)://.*",
"name" : "HTTP,HTTPS and IMAPS",
"id" : 10000001,
"description" : "This service definition authorizes all application urls that support HTTP,HTTPS and IMAPS protocols.",
"evaluationOrder" : 10000
}
在src/main/resources/services
下创建sjzt-10000003.json
{
"@class" : "org.apereo.cas.services.RegexRegisteredService",
"serviceId" : "^(http|https)://127.0.0.1:\\d*.*",
"name" : "sjzt",
"theme" : "apereo",
"id" : 10000003,
"description" : "数据中台服务",
"evaluationOrder" : 1
}
serviceId
中正则表达式匹配应用地址,可匹配多个应用文件命名规则为
JSON fileName = serviceName + "-" + serviceNumericId + ".json"
<!-- https://mvnrepository.com/artifact/org.jasig.cas.client/cas-client-core -->
<dependency>
<groupId>org.jasig.cas.client</groupId>
<artifactId>cas-client-core</artifactId>
<version>3.6.2</version>
</dependency>
<!-- 单点退出 -->
<filter>
<filter-name>CAS Single Sign Out Filter</filter-name>
<filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class>
<init-param>
<!-- cas服务地址 -->
<param-name>casServerUrlPrefix</param-name>
<param-value>http://127.0.0.1:8085/cas</param-value>
</init-param>
</filter>
<listener>
<listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>
</listener>
<!-- ticket校验 -->
<filter>
<filter-name>CAS Validation Filter</filter-name>
<!--<filter-class>org.jasig.cas.client.validation.Saml11TicketValidationFilter</filter-class>-->
<filter-class>org.jasig.cas.client.validation.Cas30ProxyReceivingTicketValidationFilter</filter-class>
<init-param>
<!-- cas服务地址 -->
<param-name>casServerUrlPrefix</param-name>
<param-value>http://192.168.3.88:8085/cas</param-value>
</init-param>
<init-param>
<!-- 应用地址 -->
<param-name>serverName</param-name>
<param-value>http://127.0.0.1:8080</param-value>
</init-param>
<init-param>
<param-name>redirectAfterValidation</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>useSession</param-name>
<param-value>true</param-value>
</init-param>
<!--
<init-param>
<param-name>acceptAnyProxy</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>proxyReceptorUrl</param-name>
<param-value>/sample/proxyUrl</param-value>
</init-param>
<init-param>
<param-name>proxyCallbackUrl</param-name>
<param-value>https://mmoayyed.unicon.net:9443/sample/proxyUrl</param-value>
</init-param>
-->
<init-param>
<param-name>authn_method</param-name>
<param-value>mfa-duo</param-value>
</init-param>
</filter>
<!-- 登录跳转 -->
<filter>
<filter-name>CAS Authentication Filter</filter-name>
<!--<filter-class>org.jasig.cas.client.authentication.Saml11AuthenticationFilter</filter-class>-->
<filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>
<init-param>
<!-- cas登录地址 -->
<param-name>casServerLoginUrl</param-name>
<param-value>http://192.168.3.88:8085/cas/login</param-value>
</init-param>
<init-param>
<!-- 应用地址,登录成功后会跳回这个地址+跳转前的路径 -->
<param-name>serverName</param-name>
<param-value>http://127.0.0.1:8080</param-value>
</init-param>
</filter>
<!-- HttpServletRequest包裹,可通过HttpServletRequest.getRemoteUser()获取登录人账号 -->
<filter>
<filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
<filter-class>org.jasig.cas.client.util.HttpServletRequestWrapperFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CAS Single Sign Out Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CAS Validation Filter</filter-name>
<url-pattern>/casClient/login</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CAS Authentication Filter</filter-name>
<url-pattern>/casClient/login</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
为实现单向的单点登录,即可不通过单点登录进入系统,cas 拦截器仅拦截了
/casClient/login
一个路径,因此应用系统需要拦截未登录时跳转到/casClient/login
配合 cas 拦截器实现 ticket校验、单点登录跳转
SSO单点登录系列6:cas单点登录防止登出退出后刷新后退ticket失效报500错_落雨-CSDN博客
放行/casClient/**
,未登录跳转/casClient/login
,即由/casClient/login
处理单点登录、应用内登录
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- 未登录跳转 -->
<property name="loginUrl" value="/casClient/login" />
<property name="filterChainDefinitions">
<value>
/=anon
/casClient/**=anon
/**=authc
</value>
</property>
</bean>
@RequestMapping("/login")
public ModelAndView index(ModelAndView modelAndView, HttpServletRequest request) {
// 已登陆跳转
User user = getUser();
if(user != null) {
return new ModelAndView("redirect:/space");
}
// 获取单点登录用户
String phone = request.getRemoteUser();
if (StringUtil.isNotEmpty(phone)) {
// remoteUser
User casUser = userService.selectOne(new EntityWrapper<User>().eq("phone", phone));
if (null != casUser) {
// 用户存在开始登录
Subject subject = SecurityUtils.getSubject();
// SecurityUtils.getSecurityManager().logout(subject);
// 登录后存放进shiro token
UsernamePasswordToken token = new UsernamePasswordToken(casUser.getUserNo(), casUser.getPassword());
token.setHost("2");
try {
subject.login(token);
return new ModelAndView("redirect:/space");
} catch (AuthenticationException e) {
}
}
}
return new ModelAndView("redirect:/login");
}
进入任一应用地址
- shiro 拦截
应用地址
- 已登录正常进入
- 未登录跳转
/casClient/login
- shiro 放行
/casClient/login
- cas 拦截
/casClient/login
鉴权- cas 已登录,从 cas 包裹的
HttpServletRequest
取出 remote user,完成 shiro 登录逻辑,跳转首页[1]
- cas 未登录,跳转单点登录页面
- cas 登录成功,跳回
/casClient/login
- 同
[1]
- 同
- cas 登录成功,跳回
- cas 已登录,从 cas 包裹的
- cas 拦截
- shiro 放行
[1]
处可使用WebUtils.getSavedRequest(HttpServletRequest)
取出被拦截的应用地址
,在完成 shiro 登录逻辑之后跳回应用地址
SavedRequest savedRequest = WebUtils.getSavedRequest(request); if(savedRequest != null) { String savedUrl = savedRequest.getRequestUrl(); if(StringUtil.isNotEmpty(savedUrl)) { return new ModelAndView("redirect:" + savedUrl); } else { return new ModelAndView("redirect:/space"); } }
因 cas 只拦截/casClient/login
,单向认证逻辑与普通 shiro 一致,只需 shiro 放行应用内登录地址,在该地址登录即可
如/casClient/single
@RequestMapping("/single")
public ModelAndView single(ModelAndView modelAndView) {
User user = getUser();
if(user == null) {
modelAndView.setViewName("/home/login");
return modelAndView;
}else {
return new ModelAndView("redirect:/space");
}
}
在application.properties
中添加
# 允许登出后重定向
cas.logout.followServiceRedirects=true
# 重定向地址的参数名称
cas.logout.redirectParameter=service
# 重定向地址,配置该参数redirectParameter失效
#cas.logout.redirectUrl=http://www.github.com
# 登出是否提示
cas.logout.confirmLogout=false
# 移除子系统票据
cas.logout.removeDescendantTickets=true
CAS Properties (apereo.github.io)
protected ModelAndView handleRequestInternal( final HttpServletRequest request, final HttpServletResponse response) throws Exception { //取得TGT_ID final String ticketGrantingTicketId = this.ticketGrantingTicketCookieGenerator.retrieveCookieValue(request); // 取得service参数数据,这个参数是可选参数 final String service = request.getParameter("service"); //如果TGT不为空 if (ticketGrantingTicketId != null) { //那么在centralAuthenticationService中销毁 this.centralAuthenticationService .destroyTicketGrantingTicket(ticketGrantingTicketId); //ticketGrantingTicketCookieGenerator 中销毁cookie this.ticketGrantingTicketCookieGenerator.removeCookie(response); //warnCookieGenerator 中销毁 this.warnCookieGenerator.removeCookie(response); } // 如果参数:followServiceRedirects为true 同时service不会空的时候,跳转到service指定的URL if (this.followServiceRedirects && service != null) { return new ModelAndView(new RedirectView(service)); } //否则,跳转到logoutView指定的页面 return new ModelAndView(this.logoutView); }
退出时可重定向
http://127.0.0.1:8085/cas/logout?service=http://www.baidu.com
登出回到应用登录地址
http://127.0.0.1:8085/cas/logout?service=http://127.0.0.1:8080/casClient/login
单点登出默认开启
关闭单点登出,在application.properties
中添加
# 关闭单点登出
cas.slo.disabled=false
cas.slo.asynchronous=true
javax.servlet.ServletException: org.jasig.cas.client.validation.TicketValidationException: 鏈兘澶熻瘑鍒嚭鐩爣 'ST-41-2VcnVMguCDWJX5zHaaaD-cas01.example.org'绁ㄦ牴
web.xml
拦截器应用地址未配置正确
没有配置应用的 json 服务或者 json 中serviceId
地址匹配不对
json 没有配置允许 http 接入
javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative DNS name matching idcardcert.market.alicloudapi.com found.
服务端使用了自签名的 ssl 证书