原文地址,转载请注明出处: https://blog.csdn.net/qq_34021712/article/details/81318649 ©王赛超
之前在服务端整合了数据库,也整合了shiro,我们一直是在服务端玩,登录跳转到登录成功页面,没啥意思,今天我们来将服务端和 客户端整合,使不同的客户端使用cas登录。cas服务端还是基于之前的整合shiro版本。
ip | 域名 | 对应服务 |
---|---|---|
127.0.0.1 | server.cas.com | CAS服务器 |
127.0.0.1 | app1.cas.com | CAS客户端1 |
127.0.0.1 | app2.cas.com | CAS客户端2 |
在/etc/hosts
中增加如下配置:
127.0.0.1 server.cas.com
127.0.0.1 app1.cas.com
127.0.0.1 app2.cas.com
客户端接入 CAS 首先需要在服务端进行注册,否则客户端访问将提示“未认证授权的服务”警告:
需求:对所有https和http请求的service进行允许认证,在resources/services下新建文件HTTPSandIMAPS-10000001.json
,这个文件是我从cas源代码同路径下拷贝过来的。
{
"@class" : "org.apereo.cas.services.RegexRegisteredService",
"serviceId" : "^(https|imaps|http)://.*",
"name" : "测试客户端",
"id" : 10000001,
"description" : "这是一个测试客户端的服务,所有的https或者http访问都允许通过",
"evaluationOrder" : 10000
}
注意:services目录中可包含多个 JSON 文件,其命名必须满足以下规则:${name}-${id}.json
,id必须为json文件中内容id一致。
对其中属性的说明如下,更多详细内容见官方文档-Service-Management。
● @class:必须为org.apereo.cas.services.RegisteredService的实现类
● serviceId:对服务进行描述的表达式,可用于匹配一个或多个 URL 地址
● name: 服务名称
● id:全局唯一标志
● description:服务描述,会显示在默认登录页
● evaluationOrder:定义多个服务的执行顺序
配置好service之后,根据官方文档-service-registry,还需修改 application.properties 文件告知 CAS 服务端从本地加载服务定义文件
#注册客户端
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
cas.logout.followServiceRedirects=true
2018-07-31 18:49:38,611 WARN [org.apereo.cas.services.ServiceRegistryInitializer] -
2018-07-31 18:49:38,611 WARN [org.apereo.cas.services.ServiceRegistryInitializer] -
网上说必须保证客户端证书和服务端证书是同一个证书,不然就会报错,我因为是在同一台机器,所以就没有进行这一步操作。
sudo keytool -import -file /Users/wangsaichao/Desktop/tomcat.cer -alias tomcat -keystore /Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/jre/lib/security/cacerts -storepass changeit
在官方文档中提供了 CAS Java 客户端样例,即cas-sample-java-webapp。下载项目导入idea
<plugin>
<groupId>org.apache.tomcat.mavengroupId>
<artifactId>tomcat7-maven-pluginartifactId>
<version>2.2version>
<configuration>
<port>8081port>
<uriEncoding>UTF-8uriEncoding>
<server>tomcat7server>
<path>/path>
configuration>
plugin>
这里给的例子是client1的,如果是client2只需要将 app1.cas.com:8081改为 app2.cas.com:8082
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:jsp="http://java.sun.com/xml/ns/javaee/jsp"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<display-name>cas-appdisplay-name>
<listener>
<listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListenerlistener-class>
listener>
<filter>
<filter-name>CAS Single Sign Out Filterfilter-name>
<filter-class>org.jasig.cas.client.session.SingleSignOutFilterfilter-class>
<init-param>
<param-name>casServerUrlPrefixparam-name>
<param-value>https://server.cas.com:8443/casparam-value>
init-param>
filter>
<filter-mapping>
<filter-name>CAS Single Sign Out Filterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
<filter>
<filter-name>CAS Filterfilter-name>
<filter-class>org.jasig.cas.client.authentication.AuthenticationFilterfilter-class>
<init-param>
<param-name>casServerLoginUrlparam-name>
<param-value>https://server.cas.com:8443/cas/loginparam-value>
init-param>
<init-param>
<param-name>serverNameparam-name>
<param-value>http://app1.cas.com:8081param-value>
init-param>
filter>
<filter-mapping>
<filter-name>CAS Filterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
<filter>
<filter-name>CAS Validation Filterfilter-name>
<filter-class>org.jasig.cas.client.validation.Cas30ProxyReceivingTicketValidationFilterfilter-class>
<init-param>
<param-name>casServerUrlPrefixparam-name>
<param-value>https://server.cas.com:8443/casparam-value>
init-param>
<init-param>
<param-name>serverNameparam-name>
<param-value>http://app1.cas.com:8081param-value>
init-param>
filter>
<filter-mapping>
<filter-name>CAS Validation Filterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
<filter>
<filter-name>CAS HttpServletRequest Wrapper Filterfilter-name>
<filter-class>org.jasig.cas.client.util.HttpServletRequestWrapperFilterfilter-class>
filter>
<filter-mapping>
<filter-name>CAS HttpServletRequest Wrapper Filterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
<filter>
<filter-name>CAS Assertion Thread Local Filterfilter-name>
<filter-class>org.jasig.cas.client.util.AssertionThreadLocalFilterfilter-class>
filter>
<filter-mapping>
<filter-name>CAS Assertion Thread Local Filterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
web-app>
为了看出效果,在两个客户端的index.jsp添加一些标签,和两个客户端互相跳转的路径,下面只给出客户端1的完整实例
<%@page contentType="text/html" %>
<%@page pageEncoding="UTF-8" %>
<%@ page import="java.util.Map" %>
<%@ page import="java.util.Iterator" %>
<%@ page import="java.util.List" %>
<%@ page import="org.jasig.cas.client.authentication.AttributePrincipal" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>CAS Example Java Web Apptitle>
head>
<body>
<h1>当前为客户端1h1>
<h2><a href="http://app1.cas.com:8081/">客户端1a>h2>
<h2><a href="http://app2.cas.com:8082/">客户端2a>h2>
<p>A sample web application that exercises the CAS protocol features via the Java CAS Client.p>
<hr>
<p><b>Authenticated User Id:b> <a href="logout.jsp" title="Click here to log out"><%= request.getRemoteUser() %>
a>p>
<%
if (request.getUserPrincipal() != null) {
AttributePrincipal principal = (AttributePrincipal) request.getUserPrincipal();
final Map attributes = principal.getAttributes();
if (attributes != null) {
Iterator attributeNames = attributes.keySet().iterator();
out.println("Attributes:");
if (attributeNames.hasNext()) {
out.println("
");
out.println("Attributes ");
out.println("Key Value ");
for (; attributeNames.hasNext(); ) {
out.println("");
String attributeName = (String) attributeNames.next();
out.println(attributeName);
out.println(" ");
final Object attributeValue = attributes.get(attributeName);
if (attributeValue instanceof List) {
final List values = (List) attributeValue;
out.println("Multi-valued attribute: " + values.size() + "");
out.println(""
);
for (Object value : values) {
out.println("" + value + "");
}
out.println("");
} else {
out.println(attributeValue);
}
out.println(" ");
}
out.println("
");
} else {
out.print("No attributes are supplied by the CAS server.");
}
} else {
out.println("The attribute map is empty. Review your CAS filter configurations.
");
}
} else {
out.println("The user principal is empty from the request object. Review the wrapper filter configuration.
");
}
%>
body>
html>
启动服务端和客户端,此时访问
http://app1.cas.com:8081/
会跳转至
https://server.cas.com:8443/login?service=https%3A%2F%2Fapp1.cas.com%3A8081%2F
输入用户信息,登录成功,返回
http://app1.cas.com:8081/;jsessionid=3EFAC76F253826DB83F73C8EC7432D10
这里首先把出现的错误罗列一下,个跟客户端整合的过程中,也是出现了很多的问题。
1.javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No name matching cas.com found
原因:之前生成证书的时候,用的域名是server.cas.com 在开始使用cas 服务端的时候一直配置的域名是cas.com 其实应该带上server 所以,修改客户端中的cas.com 为 server.cas.com。记得修改/etc/hosts中的域名映射。
2.org.jasig.cas.client.validation.TicketValidationException: No principal was found in the response from the CAS server.
原因:通过debug,发现后台
Cas20ServiceTicketValidator.parseResponseFromServer(Cas20ServiceTicketValidator.java:98)也就是这行代码,解析的时候报错了.具体原因是解析html页面报错,返回的是登录页面,在server验证成功后,应该是templates/protocol/3.0/casServiceValidationSuccess.html页面负责生成与客户端交互的xml信息。但是返回的却是登录页面,问题出在哪里呢?网上很多都是说的中文乱码什么的,我这边不是这个原因。还记得我们上一篇 服务端整合shiro,没错 是shiro权限拦截了,因为我们设置了ShiroFilterFactoryBean 并且配置了/**为 user 就是被拦截了。这个其实是不应该配置 ShiroFilterFactoryBean的.
具体参考 :cas5.3.2单点登录-服务端集成shiro权限认证(五)