客户服务管理程序:CAS单点登录(五)——Service配置及管理

在上一节我们讲述了CAS中关于自定义认证登录策略,对CAS中关于自定义登录配置的方案,校验策略有了一定的了解,如果忘记了可以去复习一下——————CAS单点登录(四)——自定义认证登录策略。这节本来该介绍自定义表单信息的知识,但是考虑到使用自定义表单知识涉及到Service配置方面的知识,所以这一节介绍一下在CAS中Service配置及管理。

首先我们要明白CAS中的Service的概念是什么,我们在第一节就讲解了在CAS系统中,主要分为三部分,User、Web应用、SSO认证中心。User就是我们普通用户,Web应用就是需要接入SSO认证中心的应用也就是这里的Service,而SSO认证中心就是CAS服务端。

简单来说就是CAS分为服务端和客户端,而Service就是指具体的多个客户端(CAS Clients)。

而这里的服务管理(Service Management)就是CAS服务管理工具允许CAS服务器管理员声明和配置哪些服务(Service,CAS客户端)可以在哪些方面使用CAS。服务管理工具的核心组件是服务注册表,它存储一个或多个注册服务。

接下来先介绍我们的第一个知识点——Service配置!

一、Service配置

我们刚刚提及到在CAS中,服务管理工具中的服务注册表当中存储着一个或多个注册服务,而这些Service中包含着各个行为的元数据,通过配置这些数据我们可以控制这些Service的行为。

主要行为包括一些几点:

  • 授权服务 - 控制哪些服务可以参与CAS SSO会话。
  • 强制身份验证 - 为强制身份验证提供管理控制。
  • 属性发布 - 为服务提供用户详细信息以进行授权和个性化。
  • 代理控制 - 通过授予/拒绝代理身份验证功能进一步限制授权服务。
  • 主题控制 - 定义用于特定服务的备用CAS主题。

在Service中配置属性主要包括以下这些信息:

属性

上图介绍了一些在Service中常用的配置项,对于各个配置属性的含义可以参考具体文档,服务配置。

在这些配置中,比较常使用的主要是:

服务访问策略——(accessStrategy),具体可以查看:服务策略配置。

服务属性配置——(properties),具体查看:服务属性配置。

服务到期政策——(expirationPolicy),具体查看:服务到期配置。

对CAS元数据的配置信息有了大致的了解后,我们需要配置其存储方式,就像我们前面介绍的多种认证方式一样,用户信息提供了多种方式,这里的Service存储方式也是提供了多种的解决方案。

储存方案

推荐使用JSON、YAML、MongoDb、Redis、JPA这几种方式来存储使用,这里也将使用这几种方式来介绍。

1、JSON

这种方式也是CAS默认初始化使用的,注册表在应用程序上下文初始化时从JSON配置文件中读取服务定义,期望在配置的目录位置内找到JSON文件。

首先添加依赖包:

<dependency> <groupId>org.apereo.cas</groupId> <artifactId>cas-server-support-json-service-registry</artifactId> <version>${cas.version}</version></dependency>

在resources/services文件夹下面新建web-10000001.json,具体内容如下:

{ "@class" : "org.apereo.cas.services.RegexRegisteredService", "serviceId" : "^(https|imaps|http)://.*", "name" : "web", "id" : 10000001, "evaluationOrder" : 10}

注意:Json文件名字规则为${name}-${id}.json,id必须为Json文件内容Json一致。

Json文件解释:

  • @class:必须为org.apereo.cas.services.RegisteredService的实现类,对其他属性进行一个json反射对象,常用的有RegexRegisteredService,匹配策略为id的正则表达式
  • serviceId:唯一的服务id
  • name: 服务名称,会显示在默认登录页
  • id:全局唯一标志
  • description:服务描述,会显示在默认登录页
  • evaluationOrder: 匹配争取时的执行循序,最好是比1大的数字

因为在CAS服务中,默认是提供了默认的Service配置项,所以如果添加的Json配置没起作用,可以尝试注释掉默认启动Json,在pom.xml文件里面进行配置,如下:

排除配置

然后在配置文件application.properties下添加配置:

### Service Registry(服务注册)## 开启识别Json文件,默认falsecas.serviceRegistry.initFromJson=true#自动扫描服务配置,默认开启#cas.serviceRegistry.watcherEnabled=true#120秒扫描一遍cas.serviceRegistry.schedule.repeatInterval=120000#延迟15秒开启# cas.serviceRegistry.schedule.startDelay=15000### Json配置cas.serviceRegistry.json.location=classpath:/services

启动服务,我们可以发现注释掉war包下Json初始化后,默认只启动了一个Json,在控制台中我们可以发现启动日志:

启动日志

如果没有注释话,默认会是加载了2个服务配置。

输入具体地址https://sso.anumbrella.net:8443/cas/login?service=http://localhost:9080/sample,进行登录,输入用户名、密码登录成功!然后跳转到一个http://localhost:9080/sample?ticket=xxxxxxxxxxxxxx的地址。

link

后面接入的就是指具体的服务地址。说明先前的配置是其作用的,因为配置了http通过服务认证。

但是我们要求的是模拟客户端登录,这里暂时没有准备客户端接入,后面文章会准备讲解,所以我们这里直接使用官方提供的Demo,更改接入地址即可。

这里官方准备的客户端Demo是Java版本的,地址为:cas-sample-java-webapp

这里我重新给客户端设置绑定一个client.anumbrella.net域名,同样在hosts文件下进行映射。

hosts

证书

然后我重新导入了一个client.anumbrella.net的证书,如果忘记了操作,可以看一下CAS单点登录(二)——搭建基础服务。将证书导入JDK中,可以通过命令查看导入了哪些证书:

查看cacerts中的证书列表:

Windows:

keytool -list -keystore "%JAVA_HOME%/jre/lib/security/cacerts" -storepass changeit

Unix:

keytool -list -keystore $JAVA_HOME/jre/lib/security/cacerts -storepass changeit

删除cacerts中指定名称的证书:

Windows:

keytool -delete -alias taobao -keystore "%JAVA_HOME%/jre/lib/security/cacerts" -storepass changeit

Unix:

keytool -delete -alias taobao -keystore $JAVA_HOME/jre/lib/security/cacerts -storepass changeit

进入客户端,配置webapp下面WEB-INF下面的web.xml文件。如下图所示:

webapp

https://sso.anumbrella.net:8443/cas/login为CAS服务端的地址,而https://client.anumbrella.net:9443为我们客户端待会要启动的地址,也就是我们所说的登录地址。这里我客户端是使用了https,如果不使用https,这里配置为http://localhost:9080也是可以的。

然后配置我们的证书,如果客户端没使用https,直接配置为http://localhost:9080,后面的操作可以不用配置了,前面的证书也不用导入。

证书配置

配置好证书的地址和密匙即可。接着进入Demo项目的根目录,执行mvn clean package jetty:run-forked命令,

webapp run

在浏览器中输入https://client.anumbrella.net:9443/sample,或者输入http://localhost:9080/sample跳转到CAS登录地址,输入用户名和密码,登录成功!

登录

client demo

我们继续使用先前提到的服务访问策略进行更改,更改Json配置如下:

{ "@class" : "org.apereo.cas.services.RegexRegisteredService", "serviceId" : "^(https|imaps|http)://.*", "name" : "web", "id" : 10000001, "evaluationOrder" : 10, "accessStrategy" : { "@class" : "org.apereo.cas.services.DefaultRegisteredServiceAccessStrategy", "enabled" : false, "ssoEnabled" : false }}

禁止服务使用,这里就是我们前面提到的服务策略配置,再启动CAS服务端测试一下。

直接提示我们未认证授权的服务!!!,说明访问策略起作用了。

服务禁止

2、YAML

yaml配置与json配置基本一致,添加依赖:

<dependency> <groupId>org.apereo.cas</groupId> <artifactId>cas-server-support-yaml-service-registry</artifactId> <version>${cas.version}</version></dependency>

在resources/services文件夹下面新建web-10000001.yml,具体内容如下:

--- !<org.apereo.cas.services.RegexRegisteredService>serviceId: "^(https|imaps|http)://.*"name: "web"id: 10000001description: "description"evaluationOrder: 10attributeReleasePolicy: !<org.apereo.cas.services.ReturnAllAttributeReleasePolicy> {}accessStrategy: !<org.apereo.cas.services.DefaultRegisteredServiceAccessStrategy> enabled: true ssoEnabled: true

注意:

yaml文件名字规则为${name}-${id}.yml,id必须为yaml文件内容id一致。其他与Json配置一样的。

在application.properties更改配置如下:

cas.serviceRegistry.initFromJson=falsecas.serviceRegistry.yaml.location=classpath:/services

启动服务,登录成功!!

3、MongoDb

同样的先添加依赖:

<dependency> <groupId>org.apereo.cas</groupId> <artifactId>cas-server-support-mongo-service-registry</artifactId> <version>${cas.version}</version></dependency>

在application.properties更改配置如下:

### MongoDB配置##ip地址cas.serviceRegistry.mongo.host=localhost#cas.serviceRegistry.mongo.clientUri=cas.serviceRegistry.mongo.idleTimeout=30000#数据库端口cas.serviceRegistry.mongo.port=27017cas.serviceRegistry.mongo.dropCollection=falsecas.serviceRegistry.mongo.socketKeepAlive=false##密码cas.serviceRegistry.mongo.password=admin cas.serviceRegistry.mongo.collection=cas-service-registrycas.serviceRegistry.mongo.databaseName=admincas.serviceRegistry.mongo.timeout=5000#用户名cas.serviceRegistry.mongo.userId=admincas.serviceRegistry.mongo.writeConcern=NORMALcas.serviceRegistry.mongo.replicaSet=cas.serviceRegistry.mongo.sslEnabled=falsecas.serviceRegistry.mongo.conns.lifetime=60000cas.serviceRegistry.mongo.conns.perHost=10

这里要注意一下:MongoDB默认需要开启认证才行,如果不开起认证,启动CAS会报错。

我使用的是docker启动的MongoDB,直接使用的是admin数据库,用户名和密码配置也是添加环境变量启动的。如果自己搭建的MongoDB数据库,可以参考一下下面的配置:

MongoDB用户配置(版本3.4以上)

#启动>mongod.exe#登录>mongo#切换数据库>use admin#新增管理员>db.createUser({user: "admin",pwd: "admin",roles:[{role:"userAdminAnyDatabase", db: "admin" } ]})#切换数据库>use cas-mongo-database# 新增用户>db.createUser({user: "cas",pwd: "123456",roles: [ { role: "readWrite", db: "cas-mongo-database" }]})#重启并开启认证>mongod.exe --auth

启动服务后,发现服务提示这样的信息?如下情况:

服务未授权

这是为啥?因为我们没有定义可以认证的服务,这就需要我们使用服务管理(Service Management)添加服务数据,或者手动添加数据信息到数据库中,像Json、Yaml中文件配置的信息一样。所以暂时可以忽略不用管,后面我会讲解如何使用Service Management来添加。

4、Redis

Redis的使用与MongoDB的用法大致相同,这里就不详细分析了。同样添加依赖:

<dependency> <groupId>org.apereo.cas</groupId> <artifactId>cas-server-support-redis-service-registry</artifactId> <version>${cas.version}</version></dependency>

添加配置信息:

### Redis配置#cas.serviceRegistry.redis.host=localhostcas.serviceRegistry.redis.database=0cas.serviceRegistry.redis.port=6380cas.serviceRegistry.redis.password=cas.serviceRegistry.redis.timeout=2000cas.serviceRegistry.redis.useSsl=falsecas.serviceRegistry.redis.usePool=truecas.serviceRegistry.redis.pool.max-active=20cas.serviceRegistry.redis.pool.maxIdle=8cas.serviceRegistry.redis.pool.minIdle=0cas.serviceRegistry.redis.pool.maxActive=8cas.serviceRegistry.redis.pool.maxWait=-1cas.serviceRegistry.redis.pool.numTestsPerEvictionRun=0cas.serviceRegistry.redis.pool.softMinEvictableIdleTimeMillis=0cas.serviceRegistry.redis.pool.minEvictableIdleTimeMillis=0cas.serviceRegistry.redis.pool.lifo=truecas.serviceRegistry.redis.pool.fairness=false

5、Jpa

最后介绍一下Jpa这种方式,通过Java持久层API来实现数据保存到数据库,可以是Mysql、Oracle、SQL Service这种方式可能使用的比较多一些。

同样的先添加依赖:

<dependency> <groupId>org.apereo.cas</groupId> <artifactId>cas-server-support-jpa-service-registry</artifactId> <version>${cas.version}</version></dependency>

添加配置信息:

### Jpa配置#cas.serviceRegistry.jpa.user=rootcas.serviceRegistry.jpa.password=123cas.serviceRegistry.jpa.driverClass=com.mysql.jdbc.Drivercas.serviceRegistry.jpa.url=jdbc:mysql://127.0.0.1:3306/cas?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=falsecas.serviceRegistry.jpa.dialect=org.hibernate.dialect.MySQL5Dialectcas.serviceRegistry.jpa.failFastTimeout=1cas.serviceRegistry.jpa.healthQuery=cas.serviceRegistry.jpa.isolateInternalQueries=falsecas.serviceRegistry.jpa.leakThreshold=10cas.serviceRegistry.jpa.batchSize=1#设置配置的服务,一直都有,不会给清除掉 , 第一次使用,需要配置为 create-drop#create-drop 重启cas服务的时候,就会给干掉#create 没有表就创建,有就不创建#none 一直都有#update 更新cas.serviceRegistry.jpa.ddlAuto=updatecas.serviceRegistry.jpa.autocommit=truecas.serviceRegistry.jpa.idleTimeout=5000cas.serviceRegistry.jpa.pool.suspension=falsecas.serviceRegistry.jpa.pool.minSize=6cas.serviceRegistry.jpa.pool.maxSize=18cas.serviceRegistry.jpa.pool.maxWait=2000cas.serviceRegistry.jpa.pool.timeoutMillis=1000

更详细的驱动信息可以参考文档:

https://apereo.github.io/cas/5.3.x/installation/JDBC-Drivers.html

启动服务后,还是和前面一样,提示CAS的服务记录是空的,没有定义服务。 希望通过CAS进行认证的应用程序必须在服务记录中明确定义。。。暂时可以忽略掉,马上我们就来配置服务管理。

然后我们去数据库发现,新增了一些表,这就是CAS将服务信息保存到数据库使用的表。

jpa表

二、Service管理

在前面的服务配置中,我们知道了CAS需要先把服务的信息确定下来,无论你是写在Json、MongoDB、Redis还是数据库MySQL、Oracle等等中,如果在没有信息前,你是无法登陆认证的。

而Service管理相当于我们可以动态管理我们自己的服务信息,随时增加,随时删除。

1、服务管理Web应用程序

官方为我们提供了一个管理Web服务的简单Demo,我们可以直接使用,这个应用服务管理webapp不是CAS服务器的一部分,是单独的一个服务。

地址:cas-management-overlay

我们然后新建src/main/resources文件夹,在下面新建文件application.properties,添加覆盖配置如下:

### CAS Thymeleaf Views#spring.thymeleaf.cache=falsespring.thymeleaf.mode=HTMLspring.thymeleaf.order=1### Embedded CAS Tomcat Container#server.context-path=/cas-managementserver.port=8081server.ssl.key-store=calsspath:thekeystoreserver.ssl.key-store-password=changeitserver.ssl.key-password=changeit### Log4J Configuration#server.context-parameters.isLog4jAutoInitializationDisabled=true# logging.config=file:/etc/cas/log4j2.xml### CAS Server#cas.server.name=https://sso.anumbrella.net:8443cas.server.prefix=${cas.server.name}/cas### CAS Authentication Attributes#cas.authn.attributeRepository.stub.attributes.uid=uidcas.authn.attributeRepository.stub.attributes.givenName=givenNamecas.authn.attributeRepository.stub.attributes.eppn=eppn# cas-management服务地址mgmt.serverName=https://client.anumbrella.net:8081### CAS Web Application Config#server.session.timeout=1800server.session.cookie.http-only=trueserver.session.tracking-modes=COOKIE### CAS Cloud Bus Configuration# Please leave spring.cloud.bus.enabled set to false#spring.cloud.bus.enabled=false#Indicates that systemPropertiesOverride can be used.# Set to false to prevent users from changing the default accidentally. Default true.spring.cloud.config.allow-override=true# External properties should override system properties.spring.cloud.config.override-system-properties=false# When allowOverride is true, external properties should take lowest priority, and not override any# existing property sources (including local config files).spring.cloud.config.override-none=false### Actuator Endpoint Security Defaults#endpoints.sensitive=trueendpoints.enabled=falseendpoints.actuator.enabled=

这里也是通过覆盖配置,使用Service Management,这里的cas-management其实也相当于一个客户端使用,只是它可以用来进行Service的管理配置。

同时新建user-details.properties文件,添加用户配置,如下:

casuser=notused,ROLE_ADMINanumbrella=notused,ROLE_ADMIN

这里我添加了anumbrella用户,默认只有casuser用户。

cas-management配置

注意:cas-management启动需要https,所以我又在Tomcat中的server.xml中添加了一个端口,来启动cas-management,并使用client.anumbrella.net域名绑定。

端口

还有一个细节注意,如果在同一电脑下启动CAS服务端,又启动cas-management,注意端口占用问题。如果出现该问题,启动一个应用时,把另一个端口注释掉,当启动时再打开。

启动cas-management应用后,发现并不能登录,又出现刚刚先前那个没有未认证授权的服务页面。这是因为cas-management也是一个客户端(client),所以没有在数据库中添加相应信息是不能登录的。(现在使用的是Jpa模式存储)

tip

打开数据库的RegexRegisteredService表,按照我们先前Json一样的数据进行添加,把要求的必填信息填写进去就可以了,如下:

数据

这里我就填写了了id、evaluation_order、name、serviceId这四个字段,分别为1,10,cas-management,^(https|imaps|http)://.*。等大约2分钟后,配置扫描更改成功后,我们再刷新登录页面,输入密码登录成功!

cas-management

这就是我们需要的cas-management,它提供了完善的service配置信息添加,点击旁边的添加按钮我们可以发现详细的service添加信息。

service信息

无论是基本的service信息,退出路由配置,服务策略配置等等都有。。

我们还可以以不同形式查看,比如以Json查看,这里的信息就是我们先前Json配置的一样,复制过去就可以使用。这里提供了更为详细的模板。

Json

目前cas-management只是把信息存储在内存中,我们需要它指定为我们的数据库存储——Jpa方式,同前面的操作一样。添加依赖,添加配置,

<!-- 数据库jdbc驱动 --> <dependency> <groupId>org.apereo.cas</groupId> <artifactId>cas-server-support-jdbc-drivers</artifactId> <version>${cas.version}</version> </dependency> <!-- Jpa Service Registry --> <dependency> <groupId>org.apereo.cas</groupId> <artifactId>cas-server-support-jpa-service-registry</artifactId> <version>${cas.version}</version> </dependency>

添加配置:

### Jpa配置#cas.serviceRegistry.jpa.user=rootcas.serviceRegistry.jpa.password=123cas.serviceRegistry.jpa.driverClass=com.mysql.jdbc.Drivercas.serviceRegistry.jpa.url=jdbc:mysql://127.0.0.1:3306/cas?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=falsecas.serviceRegistry.jpa.dialect=org.hibernate.dialect.MySQL5Dialectcas.serviceRegistry.jpa.failFastTimeout=1cas.serviceRegistry.jpa.healthQuery=cas.serviceRegistry.jpa.isolateInternalQueries=falsecas.serviceRegistry.jpa.leakThreshold=10cas.serviceRegistry.jpa.batchSize=1#设置配置的服务,一直都有,不会给清除掉 , 第一次使用,需要配置为 create-drop#create-drop 重启cas服务的时候,就会给干掉#create 没有表就创建,有就不创建#none 一直都有#update 更新cas.serviceRegistry.jpa.ddlAuto=updatecas.serviceRegistry.jpa.autocommit=truecas.serviceRegistry.jpa.idleTimeout=5000cas.serviceRegistry.jpa.pool.suspension=falsecas.serviceRegistry.jpa.pool.minSize=6cas.serviceRegistry.jpa.pool.maxSize=18cas.serviceRegistry.jpa.pool.maxWait=2000cas.serviceRegistry.jpa.pool.timeoutMillis=1000

然后我们重启服务,登录成功后,会发现先前我们在数据库中添加的服务信息。然后我们新增一个名为client-demo的服务,接着去数据库的RegexRegisteredService表查看,会发现完成新信息的添加!这里就实现了cas-management同步管理我们CAS系统Service的增删改查。

信息

2、自定义接口管理服务

我们发现管理CAS每次都要启动一个cas-management,实在是麻烦,难道没简单方法吗?其实我们可以自己去实现数据的操作。

这里有两个思路,1、直接操作数据库,或者我们需要存储的文件,直接把需要的信息写进去。2、利用CAS提供的接口来实现操作。

这里讲解一下利用CAS提供的接口来实现操作,我们知道cas-management其实就是封装了一下界面,然后调用提供的接口,直接操作增删改查。

我们也可以在CAS服务添加一个Restful类似的接口,通过CAS中的ServicesManager来操作services。我们可以查看一下ServicesManager源码:

ServicesManager

ServicesManager是一个接口,封装了我们增删改查的各种方法,简直nice!需要的就是它。

使用自定义接口需要依赖库:

<!-- Services Management --> <dependency> <groupId>org.apereo.cas</groupId> <artifactId>cas-server-core-services-api</artifactId> <version>${cas.version}</version> </dependency> <!-- Authentication Attributes --> <dependency> <groupId>org.apereo.cas</groupId> <artifactId>cas-server-core-authentication-attributes</artifactId> <version>${cas.version}</version> </dependency>

接着我们自定义controller层如下:

package net.anumbrella.sso.controller;import org.apereo.cas.services.RegexRegisteredService;import org.apereo.cas.services.RegisteredService;import org.apereo.cas.services.ReturnAllAttributeReleasePolicy;import org.apereo.cas.services.ServicesManager;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.RestController;import java.io.IOException;import java.net.URL;/** * @author anumbrella */@RestController@RequestMapping("/services")public class ServicesManagerController { @Autowired @Qualifier("servicesManager") private ServicesManager servicesManager; /** * 添加cas客户端 * 增加了单点退出功能,cas退出默认使用隐式退出 * protocol 代表的是协议,比如: http或者https的协议 */ @RequestMapping(value = "/addClient/{protocol}/{serviceId}/{id}", method = RequestMethod.GET) public String addService(@PathVariable("serviceId") String serviceId, @PathVariable("protocol") String protocol , @PathVariable("id") int id) throws IOException { String url = protocol + "://" + serviceId; RegisteredService svc = servicesManager.findServiceBy(url); if (svc != null) { return "0"; } //serviceId,可以配置为正则匹配 String a = "^" + url + ".*"; RegexRegisteredService service = new RegexRegisteredService(); ReturnAllAttributeReleasePolicy re = new ReturnAllAttributeReleasePolicy(); service.setServiceId(a); service.setId(id); service.setAttributeReleasePolicy(re); //将name统一设置为servicesId service.setName(serviceId); //单点登出 service.setLogoutUrl(new URL(url)); servicesManager.save(service, true); servicesManager.load(); return "1"; } /** * 删除服务 * * @param serviceId * @return */ @RequestMapping(value = "/delete/{serviceId}", method = RequestMethod.GET) public String delService(@PathVariable("serviceId") String serviceId) { String res = ""; RegisteredService svc = servicesManager.findServiceBy(serviceId); if (svc != null) { try { servicesManager.delete(svc); } catch (Exception e) { if (null == servicesManager.findServiceBy(serviceId)) { res = "success"; servicesManager.load(); } else { res = "failed"; } } } return res; }}

最后在resources下的META-INF文件下的spring.factories注入spring boot的配置,

配置

启动服务访问我们设置的路由,传入数据,如下:

add

最后我们去数据库可以发现我们添加的服务信息。

result

因此我们可以在Restful中进行服务的管理。

代码实例:Chapter4

参考

  • CAS单点登录-Service配置(七)
  • CAS5.2x单点登录(六)-----动态添加services
  • CAS单点登录-Cas-Management(八)
  • https://apereo.github.io/cas/5.3.x/installation/Service-Management.html

相关推荐

相关文章