单点登陆(Single Sign On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一。SSO的定义是在多个应用系统中,用户只须要登陆一次就能够访问全部相互信任的应用系统。
CAS 是一个开源的企业级单点登陆系统,目前最新版本为 5.2.x。
CAS 包含两个部分:CAS Server 和 CAS Client,它们之间独立部署。CAS 客户端拦截未认证的用户请求,并重定向至 CAS 服务端,由 CAS 服务端对用户身份进行统一认证。html
对于本地搭建 CAS 服务端,官方提供了基于 Maven 和 Gradle 的 Overlay 构建方式,本文用的是 CAS Maven WAR Overlay。java
Overlay 技术能够把多个项目 war 合并成为一个项目,若是项目存在同名文件,那么主项目中的文件将覆盖掉其余项目的同名文件。
使用 Overlay 无需对 CAS 源码进行编译,也避免了对 CAS 源码进行侵入性改造。node
下载 CAS Maven WAR Overlay,修改 pom.xml ,设置 CAS 版本为 5.2.2。建议去除掉 pom.xml 文件中的 wrapper-maven-plugin 和无用的 profile 配置。git
<properties> <cas.version>5.2.2</cas.version> </properties>
首次导入 IDEA,能够看到后台正在下载官方 cas.war。github
工程 overlays 目录下的文件是由 maven 编译后才产生的,能够在 pom.xml 中配置官方 cas.war 中的文件的那些文件能够排除,不要在 overlays 中生成:web
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>2.6</version> <configuration> <warName>cas</warName> <failOnMissingWebXml>false</failOnMissingWebXml> <recompressZippedFiles>false</recompressZippedFiles> <archive> <compress>false</compress> <manifestFile>${manifestFileToUse}</manifestFile> </archive> <overlays> <overlay> <groupId>org.apereo.cas</groupId> <artifactId>cas-server-webapp${app.server}</artifactId> <!--原有的服务再也不初始化进去--> <excludes> <exclude>WEB-INF/classes/services/*</exclude> <exclude>WEB-INF/classes/application.*</exclude> </excludes> </overlay> </overlays> </configuration> </plugin>
打开 Project Structure,能够观察到该工程具备两个 Web Root,可是 src/main/webapp 目录并不存在,须要进行手动建立。apache
拷贝 overlays 目录下的 application.properties 配置文件至 resources 目录,用于覆盖 CAS WAR 中的同名文件。最终工程目录结构以下:json
为工程配置 tomcat 8.0 并启动,注意 CAS 5.2.x 不支持低于 tomcat 8.0 的版本。
看到控制台打印 READY 代表启动成功。tomcat
访问 http://localhost:8080/cas/login 进入登陆界面。java-web
其中Non-secure Connection
提示须要配置 SSL,Static Authentication
提示须要对用户配置进行修改,能够修改成 JDBC、REST 等方式。目前用户配置写死在 application.properties 配置文件中,用户名为 casuser,密码为 Mellon。
## # CAS Authentication Credentials # cas.authn.accept.users=casuser::Mellon
客户端接入 CAS 首先须要在服务端进行注册,不然客户端访问将提示“未认证受权的服务”警告:
在 resources 文件夹下建立 services 文件夹进行服务定义,该目录中可包含多个 JSON 文件,其命名必须知足如下规则:
JSON fileName = serviceName + "-" + serviceNumericId + ".json"
建立 services/Localhost-10000003.json 文件,表示容许全部以 http://localhost 开头的认证请求:
{ "@class": "org.apereo.cas.services.RegexRegisteredService", "serviceId": "^(http)://localhost.*", "name": "本地服务", "id": 10000003, "description": "这是一个本地容许的服务,经过localhost访问都容许经过", "evaluationOrder": 1 }
对其中属性的说明以下,更多详细内容见官方文档-Service-Management。
最后,根据官方文档-service-registry,还需修改 application.properties 文件告知 CAS 服务端从本地加载服务定义文件:
#开启识别json文件,默认false cas.serviceRegistry.initFromJson=true #自动扫描服务配置,默认开启 #cas.serviceRegistry.watcherEnabled=true #120秒扫描一遍 #cas.serviceRegistry.repeatInterval=120000 #延迟15秒开启 #cas.serviceRegistry.startDelay=15000 #资源加载路径 #cas.serviceRegistry.config.location=classpath:/services
启动时打印如下日志,说明服务注册成功。
2018-03-18 23:36:08,660 INFO [org.apereo.cas.services.AbstractServicesManager] - <Loaded [0] service(s) from [InMemoryServiceRegistry].> 2018-03-18 23:36:08,876 INFO [org.apereo.cas.config.CasServiceRegistryInitializationConfiguration] - <Attempting to initialize the service registry [InMemoryServiceRegistry] from service definition resources found at [class path resource [services]]> 2018-03-18 23:36:08,877 WARN [org.apereo.cas.services.ServiceRegistryInitializer] - <Service registry [InMemoryServiceRegistry] will be auto-initialized from JSON service definitions. This behavior is only useful for testing purposes and MAY NOT be appropriate for production. Consider turning off this behavior via the setting [cas.serviceRegistry.initFromJson=false] and explicitly register definitions in the services registry.> 2018-03-18 23:36:09,283 INFO [org.apereo.cas.services.AbstractServicesManager] - <Loaded [3] service(s) from [InMemoryServiceRegistry].>
在官方文档中提供了 CAS Java 客户端样例,即 cas-sample-java-webapp。
修改 pom.xml,修改 tomcat7-maven-plugin 设置访问地址为http://localhost:8181/node1
:
<!-- tomcat7 plugin --> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> <configuration> <port>8181</port> <uriEncoding>UTF-8</uriEncoding> <server>tomcat7</server> <path>/node1</path> </configuration> </plugin>
CAS Client 经过拦截器将未认证的请求重定向到 CAS Server,这里对 cas-sample-java-webapp 的 web.xml 文件进行修改,将服务端、客户端地址替换为实际测试的地址:
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <!-- <context-param> <param-name>renew</param-name> <param-value>true</param-value> </context-param> --> <!--单点登出过滤器--> <filter> <filter-name>CAS Single Sign Out Filter</filter-name> <filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class> <init-param> <param-name>casServerUrlPrefix</param-name> <param-value>http://localhost:8080/cas</param-value> </init-param> </filter> <listener> <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class> </listener> <!--用来跳转登陆--> <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> <param-name>casServerLoginUrl</param-name> <param-value>http://localhost:8080/cas/login</param-value> </init-param> <init-param> <param-name>serverName</param-name> <!--这是客户端的部署地址,认证时会带着这个地址,认证成功后会跳转到这个地址--> <param-value>http://localhost:8181/node1</param-value> </init-param> </filter> <!--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> <param-name>casServerUrlPrefix</param-name> <param-value>http://localhost:8080/cas</param-value> </init-param> <init-param> <param-name>serverName</param-name> <param-value>http://localhost:8181/node1</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>authn_method</param-name> <param-value>mfa-duo</param-value> </init-param> </filter> <!-- 该过滤器负责实现HttpServletRequest请求的包裹,好比容许开发者经过HttpServletRequest的getRemoteUser()方法得到SSO登陆用户的登陆名,可选配置--> <filter> <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name> <filter-class>org.jasig.cas.client.util.HttpServletRequestWrapperFilter</filter-class> </filter> <!-- 该过滤器使得开发者能够经过org.jasig.cas.client.util.AssertionHolder来获取用户的登陆名。 好比AssertionHolder.getAssertion().getPrincipal().getName()--> <!--<filter> <filter-name>CASAssertion Thread LocalFilter</filter-name> <filter-class>org.jasig.cas.client.util.AssertionThreadLocalFilter</filter-class> </filter> <filter-mapping> <filter-name>CASAssertion Thread LocalFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>--> <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>/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>CAS Authentication Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <welcome-file-list> <welcome-file> index.jsp </welcome-file> </welcome-file-list> </web-app>
此时访问 http://localhost:8181/node1
会跳转至http://localhost:8080/cas/login?service=http%3A%2F%2Flocalhost%3A8181%2Fnode1%2F
输入用户信息,登陆成功,返回http://localhost:8181/node1/;jsessionid=6628138DCAAA5BA3481CD4C9238FEBFF
利用相同的方法配置第二个客户端,访问地址为http://localhost:8282/node2
,可知在 node1 登陆成功的状况下,无需再次输入用户密码便可访问 node2 后台页面。
至此,开发环境搭建完毕。因为客户端只是对 web.xml 中的过滤器进行配置,能够很方便地集成到各个业务系统中。
转载请注明出处。