CAS统一登录认证(5): CAS 配置中心
配置中心
一、前言
说到配置中心,不得不说一下微服务了。微服务是一种有别于传统单体架构的一种服务架构,简单理解微服务就是将原有的服务按照一点规则进行拆分,简而言之,微服务架构风格这种开发方法,是以开发一组小型服务的方式来开发一个独立的应用系统的。其中每个小型服务都运行在自己的进程中,并经常采用HTTP资源API这样轻量的机制来相互通信。这些服务围绕业务功能进行构建,并能通过全自动的部署机制来进行独立部署。这些微服务可以使用不同的语言来编写,并且可以使用不同的数据存储技术。
微服务具备的特性:
- 每个微服务可独立运行在自己的进程里;
- 一系列独立运行的微服务共同构建起了整个系统;
- 每个服务为独立的业务开发,一个微服务一般完成某个特定的功能,比如:订单管理、用户管理等;
- 微服务之间通过一些轻量的通信机制进行通信,例如通过REST API或者RPC的方式进行调用。
微服务框架Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智能路由,微代理,控制总线,一次性令牌,全局锁定,领导选举,分布式会话,集群状态)。
配置管理模块(Spring Cloud Config)为分布式系统中的外部化配置提供服务器和客户端支持。使用Config Server,您可以在所有环境中管理应用程序的外部属性。客户端和服务器上的概念映射与Spring Environment
和 PropertySource
抽象,因此它们非常适合Spring应用程序,但可以与任何语言运行的任何应用程序一起使用。当应用程序通过部署管道从开发到测试并进入生产时,您可以管理这些环境之间的配置,并确保应用程序具有迁移时需要运行的所有内容。服务器存储后端的默认实现使用git,因此它可以轻松支持配置环境的标签版本,以及可用于管理内容的各种工具。
Spring Cloud Config Server功能:
-
用于外部配置的HTTP,基于资源的API(名称 - 值对或等效的YAML内容)
-
加密和解密属性值(对称或非对称)
-
使用可轻松嵌入Spring Boot应用程序
@EnableConfigServer
Spring Cloud Config Client功能(适用于Spring应用程序):
-
绑定到Config Server并
Environment
使用远程属性源初始化Spring -
加密和解密属性值(对称或非对称)
Spring Cloud Config 就是云端存储配置信息的,它具有中心化、版本控制、支持动态更新、平*立、语言独立等特性。原理如图所示,真正的数据存在Git等repository中,Config Server去获取相应的信息,然后开发给Client Application,相互间的通信基于HTTP、TCP、UDP等协议.
二、cas配置中心
cas的配置中心的功能就是为服务端提供统一的配置,保证多个服务端都能使用一致的配置。实现原理其实很简单,我们独立部署一个应用用于为Cas Server提供配置,这个应用作为Config Server,而Cas Server作为Config Client。当Cas Server启动时,主动去Config Server获取配置(各种认证配置、安全设置等等),启动完成之后,定期与Cas Server进行通讯,及时的获取Cas Config 中的配置发生改变。
那么第一个问题来了,Config Server的配置从哪里来呢?要怎么配置呢?
约定:
- 服务是以spring.application.name的配置属性来决定这个服务的id,也就是告诉配置中心,我是谁
- spring.profiles.active这是要拿哪个配置文件,那么这样我们就可以区分多个维度或者说环境,这里可以是多个,可以逗号分隔,如果不指定,则默认加载application.properties的配置,不会加载带有profile的配置(开发为dev,测试为test,生产为prod)
http请求读取配置的匹配规则:
/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties
如果在应用在的配置如下所示:
spring.application.name=cas-server
spring.profiles.active=dev
那么,应用则会在resources中寻找配置文件名为cas-server-dev.properties或cas-server-dev.yml的文件,然后将里面的内容打包到JSON格式的配置信息体内以供Config Client获取。
三、搭建配置中心
3.1 创建应用
使用IDEA 创建一个名为cas-server-config-center的Spring Boot应用,只需引入Web模块即可,创建出来的应用结构如下。
3.2 引入Spring Cloud依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.oyc</groupId>
<artifactId>cas-server-config-center</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>cas-server-config-center</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-cloud.version>Dalston.SR3</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3.3 配置应用
配置应用的服务端口和访问路径,配置配置读取文件。
server:
port: 8888
context-path: /config-center
spring:
application:
name: cas-config
profiles:
active: dev,native
将配置是放在与本地,在spring的默认配置在目录resources/config下新增cas-config.properties,将Cas-Server中application.properties的配置拷贝到里面。
##
# CAS Server Context Configuration
#
server.context-path=/cas
server.port=8443
server.ssl.key-store=file:/etc/cas/thekeystore
server.ssl.key-store-password=changeit
server.ssl.key-password=changeit
# server.ssl.ciphers=
# server.ssl.client-auth=
# server.ssl.enabled=
# server.ssl.key-alias=
# server.ssl.key-store-provider=
# server.ssl.key-store-type=
# server.ssl.protocol=
# server.ssl.trust-store=
# server.ssl.trust-store-password=
# server.ssl.trust-store-provider=
# server.ssl.trust-store-type=
server.compression.enabled=true
server.compression.mime-types=application/javascript,application/json,application/xml,text/html,text/xml,text/plain
server.max-http-header-size=2097152
server.use-forward-headers=true
server.connection-timeout=20000
server.error.include-stacktrace=NEVER
server.tomcat.max-http-post-size=2097152
server.tomcat.basedir=build/tomcat
server.tomcat.accesslog.enabled=true
server.tomcat.accesslog.pattern=%t %a "%r" %s (%D ms)
server.tomcat.accesslog.suffix=.log
server.tomcat.max-threads=10
server.tomcat.port-header=X-Forwarded-Port
server.tomcat.protocol-header=X-Forwarded-Proto
server.tomcat.protocol-header-https-value=https
server.tomcat.remote-ip-header=X-FORWARDED-FOR
server.tomcat.uri-encoding=UTF-8
spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true
spring.http.encoding.force=true
##
# CAS Cloud Bus Configuration
#
spring.cloud.bus.enabled=false
# spring.cloud.bus.refresh.enabled=true
# spring.cloud.bus.env.enabled=true
# spring.cloud.bus.destination=CasCloudBus
# spring.cloud.bus.ack.enabled=true
endpoints.enabled=false
endpoints.sensitive=true
endpoints.restart.enabled=false
endpoints.shutdown.enabled=false
management.security.enabled=true
management.security.roles=ACTUATOR,ADMIN
management.security.sessions=if_required
management.context-path=/status
management.add-application-context-header=false
security.basic.authorize-mode=role
security.basic.enabled=false
security.basic.path=/cas/status/**
##
# CAS Web Application Session Configuration
#
server.session.timeout=300
server.session.cookie.http-only=true
server.session.tracking-modes=COOKIE
##
# CAS Thymeleaf View Configuration
#
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.cache=true
spring.thymeleaf.mode=HTML
##
# CAS Log4j Configuration
#
# logging.config=file:/etc/cas/log4j2.xml
server.context-parameters.isLog4jAutoInitializationDisabled=true
# 开启识别Json文件,默认false
cas.serviceRegistry.initFromJson=true
# 保存tgc
cas.tgc.secure=false
tgc.name=TGC
tgc.path=/
tgc.maxAge=-1
warn.cookie.maxAge=-1
# The encryption secret key. By default, must be a octet string of size 256.
# tgc.encryption.key=
# The signing secret key. By default, must be a octet string of size 512.
# tgc.signing.key=
# Decides whether SSO cookie should be created only under secure connections.
# tgc.secure=true
# The expiration value of the SSO cookie
# tgc.maxAge=-1
# The name of the SSO cookie
# tgc.name=TGC
# The path to which the SSO cookie will be scoped
# tgc.path=/cas
# Decides whether SSO Warning cookie should be created only under secure connections.
# warn.cookie.secure=true
# The expiration value of the SSO Warning cookie
# warn.cookie.maxAge=-1
##
# CAS AspectJ Configuration
#
spring.aop.auto=true
spring.aop.proxy-target-class=true
##
# CAS Authentication Credentials
#
#cas.authn.accept.users=casuser::Mellon
##
# CAS Delegated Authentication
#
cas.authn.pac4j.bitbucket.clientName=Bitbucket
cas.authn.pac4j.dropbox.clientName=Dropbox
cas.authn.pac4j.facebook.clientName=Facebook
cas.authn.pac4j.foursquare.clientName=Foursquare
cas.authn.pac4j.github.clientName=Github
cas.authn.pac4j.google.clientName=Google
cas.authn.pac4j.linkedIn.clientName=LinkedIn
cas.authn.pac4j.paypal.clientName=PayPal
cas.authn.pac4j.twitter.clientName=Twitter
cas.authn.pac4j.yahoo.clientName=Yahoo
cas.authn.pac4j.windowsLive.clientName=Windows Live
cas.authn.pac4j.wordpress.clientName=WordPress
## 关闭静态认证
#server.ssl.enabled=false
#staticAuthentication=false
#jdbc验证配置
#Query Database Authentication 数据库查询校验用户名开始
#查询账号密码sql,必须包含密码字段
cas.authn.jdbc.query[0].sql=select * from cas_user where user_name=?
#指定上面的sql查询字段名(必须)
cas.authn.jdbc.query[0].fieldPassword=user_password
#指定过期字段,1为过期,若过期需要修改密码
cas.authn.jdbc.query[0].fieldExpired=expired_flag
#不可用字段段,1为不可用,
cas.authn.jdbc.query[0].fieldDisabled=disabled_flag
#数据库方言hibernate的知识
cas.authn.jdbc.query[0].dialect=org.hibernate.dialect.MySQLDialect
#数据库驱动
cas.authn.jdbc.query[0].driverClass=com.mysql.jdbc.Driver
#数据库连接
cas.authn.jdbc.query[0].url=jdbc:mysql://localhost:3306/cas?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
#数据库用户名
cas.authn.jdbc.query[0].user=root
#数据库密码
cas.authn.jdbc.query[0].password=123456
#默认加密策略,通过encodingAlgorithm来指定算法,默认NONE不加密
cas.authn.jdbc.query[0].passwordEncoder.type=NONE
# 盐值固定列
#cas.authn.jdbc.encode[0].saltFieldName=username
##静态盐值
##cas.authn.jdbc.encode[0].staticSalt=.
#cas.authn.jdbc.query[0].passwordEncoder.characterEncoding=UTF-8
#cas.authn.jdbc.query[0].passwordEncoder.encodingAlgorithm=MD5
3.4 验证配置中心
启动项目,在浏览器中访问http://127.0.0.1:8888/config-center/cas-config/dev,结果如下图,说明配置中心搭建成功。
四、Cas Server 连接配置中心
以上,我们已经搭建好了配置中心并且将Cas Server所需要的配置放置在了配置中心,配置中心也提供了接口以供我们获取配置的内容,所以我们的 application.properties里面的配置就可以清掉了,然后新增bootstrap.properties用于配置我们的配置中心,内容如下所示:
#指定日志文件
logging.file=logs/cas.log
info.name=cas单点登录系统
#定义application.name的id
spring.application.name=casconfig
#寻找配置中心
spring.profiles.active=online
#指定配置中心地址
spring.cloud.config.uri=http://localhost:8888/config-center
#开启配置中心
spring.cloud.config.enabled=true
#支持自动任务去配置中心刷新配置
spring.cloud.config.watch.enabled=true
#30秒刷新一次
spring.cloud.config.watch.initialDelay=30000
#请求配置中心超时时间
spring.cloud.config.watch.delay=1000
#开启健康检查
health.config.enabled=true
在resources中新增log4j2.xml,配置如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!-- Specify the refresh internal in seconds. -->
<Configuration monitorInterval="5" packages="org.apereo.cas.logging">
<Properties>
<Property name="baseDir">${sys:catalina.home}${sys:file.separator}logs${sys:file.separator}</Property>
</Properties>
<Appenders>
<Console name="console" target="SYSTEM_OUT">
<PatternLayout pattern="%d %p [%c] - <%m>%n"/>
</Console>
<RollingFile name="file" fileName="${baseDir}/CasServer.log" append="true"
filePattern="${baseDir}/CasServer-%d{yyyy-MM-dd-HH}-%i.log">
<PatternLayout pattern="%highlight{%d %p [%c] - <%m>}%n"/>
<Policies>
<OnStartupTriggeringPolicy />
<SizeBasedTriggeringPolicy size="1024 MB"/>
<TimeBasedTriggeringPolicy />
</Policies>
<DefaultRolloverStrategy max="5" compressionLevel="9">
<Delete basePath="${baseDir}" maxDepth="2">
<IfFileName glob="*/*.log.gz" />
<IfLastModified age="7d" />
</Delete>
</DefaultRolloverStrategy>
</RollingFile>
<RollingFile name="auditlogfile" fileName="${baseDir}/CasServer_audit.log" append="true"
filePattern="${baseDir}/CasServer_audit-%d{yyyy-MM-dd-HH}-%i.log">
<PatternLayout pattern="%d %p [%c] - %m%n"/>
<Policies>
<OnStartupTriggeringPolicy />
<SizeBasedTriggeringPolicy size="1024 MB"/>
<TimeBasedTriggeringPolicy />
</Policies>
<DefaultRolloverStrategy max="5" compressionLevel="9">
<Delete basePath="${baseDir}" maxDepth="2">
<IfFileName glob="*/*.log.gz" />
<IfLastModified age="7d" />
</Delete>
</DefaultRolloverStrategy>
</RollingFile>
<RollingFile name="perfFileAppender" fileName="${baseDir}/CasServer_stats.log" append="true"
filePattern="${baseDir}/CasServer_stats-%d{yyyy-MM-dd-HH}-%i.log">
<PatternLayout pattern="%m%n"/>
<Policies>
<OnStartupTriggeringPolicy />
<SizeBasedTriggeringPolicy size="1024 MB"/>
<TimeBasedTriggeringPolicy />
</Policies>
<DefaultRolloverStrategy max="5" compressionLevel="9">
<Delete basePath="${baseDir}" maxDepth="2">
<IfFileName glob="*/*.log.gz" />
<IfLastModified age="7d" />
</Delete>
</DefaultRolloverStrategy>
</RollingFile>
<CasAppender name="casAudit">
<AppenderRef ref="auditlogfile" />
</CasAppender>
<CasAppender name="casFile">
<AppenderRef ref="file" />
</CasAppender>
<CasAppender name="casConsole">
<AppenderRef ref="console" />
</CasAppender>
<CasAppender name="casPerf">
<AppenderRef ref="perfFileAppender" />
</CasAppender>
</Appenders>
<Loggers>
<AsyncLogger name="com.couchbase" level="warn" additivity="false" includeLocation="true">
<AppenderRef ref="casConsole"/>
<AppenderRef ref="casFile"/>
</AsyncLogger>
<AsyncLogger name="org.apereo.cas.web.CasWebApplication" level="info" additivity="false" includeLocation="true">
<AppenderRef ref="casConsole"/>
<AppenderRef ref="casFile"/>
</AsyncLogger>
<AsyncLogger name="org.springframework.security" level="warn" additivity="false">
<AppenderRef ref="casConsole"/>
<AppenderRef ref="casFile"/>
</AsyncLogger>
<AsyncLogger name="org.springframework.boot.autoconfigure.security" level="info" additivity="false">
<AppenderRef ref="casConsole"/>
<AppenderRef ref="casFile"/>
</AsyncLogger>
<AsyncLogger name="org.jasig.cas.client" level="info" additivity="false" includeLocation="true">
<AppenderRef ref="casConsole"/>
<AppenderRef ref="casFile"/>
</AsyncLogger>
<AsyncLogger name="org.apereo" level="info" additivity="false" includeLocation="true">
<AppenderRef ref="casConsole"/>
<AppenderRef ref="casFile"/>
</AsyncLogger>
<AsyncLogger name="org.apereo.services.persondir" level="warn" additivity="false" includeLocation="true">
<AppenderRef ref="casConsole"/>
<AppenderRef ref="casFile"/>
</AsyncLogger>
<AsyncLogger name="org.apache" level="error" additivity="false">
<AppenderRef ref="casConsole"/>
<AppenderRef ref="casFile"/>
</AsyncLogger>
<AsyncLogger name="org.springframework.cloud" level="info" additivity="false">
<AppenderRef ref="casConsole"/>
<AppenderRef ref="casFile"/>
</AsyncLogger>
<AsyncLogger name="org.springframework.cloud.context" level="warn" additivity="false">
<AppenderRef ref="casConsole"/>
<AppenderRef ref="casFile"/>
</AsyncLogger>
<AsyncLogger name="org.springframework.boot" level="warn" additivity="false">
<AppenderRef ref="casConsole"/>
<AppenderRef ref="casFile"/>
</AsyncLogger>
<AsyncLogger name="org.springframework" level="warn" additivity="false">
<AppenderRef ref="casConsole"/>
<AppenderRef ref="casFile"/>
</AsyncLogger>
<AsyncLogger name="org.springframework.aop" level="warn" additivity="false">
<AppenderRef ref="casConsole"/>
<AppenderRef ref="casFile"/>
</AsyncLogger>
<AsyncLogger name="org.springframework.boot.actuate.autoconfigure" level="warn" additivity="false">
<AppenderRef ref="casConsole"/>
</AsyncLogger>
<AsyncLogger name="org.springframework.webflow" level="warn" additivity="false">
<AppenderRef ref="casConsole"/>
<AppenderRef ref="casFile"/>
</AsyncLogger>
<AsyncLogger name="org.springframework.session" level="warn" additivity="false">
<AppenderRef ref="casConsole"/>
<AppenderRef ref="casFile"/>
</AsyncLogger>
<AsyncLogger name="org.springframework.amqp" level="off" additivity="false">
<AppenderRef ref="casConsole"/>
<AppenderRef ref="casFile"/>
</AsyncLogger>
<AsyncLogger name="org.springframework.integration" level="warn" additivity="false">
<AppenderRef ref="casConsole"/>
<AppenderRef ref="casFile"/>
</AsyncLogger>
<AsyncLogger name="org.springframework.messaging" level="warn" additivity="false">
<AppenderRef ref="casConsole"/>
<AppenderRef ref="casFile"/>
</AsyncLogger>
<AsyncLogger name="org.springframework.web" level="warn" additivity="false">
<AppenderRef ref="casConsole"/>
<AppenderRef ref="casFile"/>
</AsyncLogger>
<AsyncLogger name="org.springframework.orm.jpa" level="warn" additivity="false">
<AppenderRef ref="casConsole"/>
<AppenderRef ref="casFile"/>
</AsyncLogger>
<AsyncLogger name="org.springframework.scheduling" level="warn" additivity="false">
<AppenderRef ref="casConsole"/>
<AppenderRef ref="casFile"/>
</AsyncLogger>
<AsyncLogger name="org.thymeleaf" level="warn" additivity="false">
<AppenderRef ref="casConsole"/>
<AppenderRef ref="casFile"/>
</AsyncLogger>
<AsyncLogger name="org.pac4j" level="warn" additivity="false">
<AppenderRef ref="casConsole"/>
<AppenderRef ref="casFile"/>
</AsyncLogger>
<AsyncLogger name="org.opensaml" level="warn" additivity="false">
<AppenderRef ref="casConsole"/>
<AppenderRef ref="casFile"/>
</AsyncLogger>
<AsyncLogger name="net.sf.ehcache" level="warn" additivity="false">
<AppenderRef ref="casConsole"/>
<AppenderRef ref="casFile"/>
</AsyncLogger>
<AsyncLogger name="com.ryantenney.metrics" level="warn" additivity="false">
<AppenderRef ref="console"/>
<AppenderRef ref="file"/>
</AsyncLogger>
<AsyncLogger name="net.jradius" level="warn" additivity="false">
<AppenderRef ref="casConsole"/>
<AppenderRef ref="casFile"/>
</AsyncLogger>
<AsyncLogger name="org.openid4java" level="warn" additivity="false">
<AppenderRef ref="casConsole"/>
<AppenderRef ref="casFile"/>
</AsyncLogger>
<AsyncLogger name="org.ldaptive" level="warn" additivity="false">
<AppenderRef ref="casConsole"/>
<AppenderRef ref="casFile"/>
</AsyncLogger>
<AsyncLogger name="com.hazelcast" level="warn" additivity="false">
<AppenderRef ref="casConsole"/>
<AppenderRef ref="casFile"/>
</AsyncLogger>
<AsyncLogger name="org.springframework.context.annotation" level="off" additivity="false" />
<AsyncLogger name="org.springframework.boot.devtools" level="off" additivity="false" />
<AsyncLogger name="org.jasig.spring" level="warn" additivity="false">
<AppenderRef ref="casConsole"/>
<AppenderRef ref="casFile"/>
</AsyncLogger>
<AsyncLogger name="org.springframework.web.socket" level="warn" additivity="false">
<AppenderRef ref="casConsole"/>
<AppenderRef ref="casFile"/>
</AsyncLogger>
<AsyncLogger name="org.apache.cxf" level="warn" additivity="false">
<AppenderRef ref="casConsole"/>
<AppenderRef ref="casFile"/>
</AsyncLogger>
<AsyncLogger name="org.apache.http" level="warn" additivity="false">
<AppenderRef ref="casConsole"/>
<AppenderRef ref="casFile"/>
</AsyncLogger>
<AsyncLogger name="perfStatsLogger" level="info" additivity="false" includeLocation="true">
<AppenderRef ref="casPerf"/>
</AsyncLogger>
<AsyncLogger name="org.apereo.cas.web.flow" level="info" additivity="true" includeLocation="true">
<AppenderRef ref="casFile"/>
</AsyncLogger>
<AsyncLogger name="org.apereo.inspektr.audit.support" level="info" includeLocation="true">
<AppenderRef ref="casAudit"/>
<AppenderRef ref="casFile"/>
</AsyncLogger>
<AsyncRoot level="error">
<AppenderRef ref="casConsole"/>
</AsyncRoot>
</Loggers>
</Configuration>
重启项目即可看到:
从上图可以看出,Cas Server去配置中心获取配置了。等待启动完成,即可测试是否成功,如果没有意外,效果应该跟之前是一样的。
上一篇: jq时间倒计时处理
推荐阅读
-
基于CAS实现单点登录(SSO):配置CAS服务端的数据库查询认证机
-
基于CAS实现单点登录(SSO):配置CAS服务端的数据库查询认证机
-
Laravel5中集成Jasig cas统一认证系统
-
Laravel5中集成Jasig cas统一认证系统 - mrhyher
-
CAS统一登录认证(11): 提供oauth2.0认证服务器
-
springmvc--sso单点登录cas统一身份认证器
-
CAS统一登录认证(12): 通过oauth2.0单点登录Afterlogic webmail
-
CAS统一登录认证(13): ldap 批量导入用户
-
NC集成CAS统一认证+单点登录原理
-
CAS单点登录(一):启动CAS认证中心服务