一、环境准备
这里使用的是CAS Server 6.3版本,环境要求:
- jdk11:6.x需要jdk11, 而5.x系列使用的jdk8
- gradle:7.0以上,cas自从5.3以后就改成gradle构建了
- tomcat:9.0以上
从github下载CAS服务模板,解压该包重命名为cas-server
二、配置gradle
1、下载gradle并解压
2、在环境变量中增加GRADLE_HOME变量,并指向gradle解压目录。
3、增加GRADLE_USER_HOME环境变量,随便给一个保存jar包的地址,如:D:\gradle-repository
4、在Path环境变量中增加bin目录指向,如:%GRADLE_HOME%\bin
5、gradle支持阿里云:在gradle解压目录的init.d目录下新建一个名称为init.gradle的文件,内容如下
allprojects{
repositories {
def ALIYUN_REPOSITORY_URL = 'http://maven.aliyun.com/nexus/content/groups/public'
def ALIYUN_JCENTER_URL = 'http://maven.aliyun.com/nexus/content/repositories/jcenter'
all { ArtifactRepository repo ->
if(repo instanceof MavenArtifactRepository){
def url = repo.url.toString()
if (url.startsWith('https://repo1.maven.org/maven2')) {
project.logger.lifecycle "Repository ${repo.url} replaced by $ALIYUN_REPOSITORY_URL."
remove repo
}
if (url.startsWith('https://jcenter.bintray.com/')) {
project.logger.lifecycle "Repository ${repo.url} replaced by $ALIYUN_JCENTER_URL."
remove repo
}
}
}
maven {
url ALIYUN_REPOSITORY_URL
url ALIYUN_JCENTER_URL
}
}
}
用idea 打开项目, 设置gradle jvm使用的jvm
修改build.gradle文件, 添加aliyun依赖地址
repositories {
if (project.privateRepoUrl) {
maven {
url project.privateRepoUrl
credentials {
username = project.privateRepoUsername
password = System.env.PRIVATE_REPO_TOKEN
}
}
}
mavenLocal()
// mavenCentral()
maven { url 'https://maven.aliyun.com/nexus/content/repositories/google' }
maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' }
maven { url 'https://maven.aliyun.com/nexus/content/repositories/jcenter'}
maven { url 'https://oss.sonatype.org/content/repositories/releases' }
maven {
url 'https://oss.sonatype.org/content/repositories/snapshots'
mavenContent { snapshotsOnly() }
}
maven { url 'https://build.shibboleth.net/nexus/content/repositories/releases/' }
maven {
url "https://repo.spring.io/milestone"
mavenContent { releasesOnly() }
}
}
三、构建cas项目
仔细阅读README.md文件, 大多数操作官方都给了提示
gradlew文件是gradle构建项目的入口, 可以通过如下命令了解gradlew的用法
./gradlew help
./gradlew tasks
如果IDEA正常设置,默认会自动进行开始预加载jar包,这个过程要慢一些。我们可以手动进行,执行如下命令
./gradlew.bat clean build
执行完成后,对其进行解压war包,执行如下命令
./gradlew.bat explodeWar
在build中会生成cas-resources目录,将其复制到src/main目录下并命名为resources
CAS默认使用https服务, 需要配置https证书,我们也可以启用http,修改/src/main/resoucres/services/HTTPSandIMAPS-10000001.json文件
{
"@class": "org.apereo.cas.services.RegexRegisteredService",
"serviceId": "^(https|http|imaps)://.*",
"name": "HTTPS and IMAPS",
"id": 10000001,
"description": "This service definition authorizes all application urls that support HTTPS and IMAPS protocols.",
"evaluationOrder": 10000
}
将"serviceId": "^(https|http|imaps)://.*",中增加了http,这样就可以了。
重新编译生成新的war包
./gradlew.bat build
通过tomcat启动项目, 其中Deployment配置项选择war包。
显示该页面表示已正常启动可访问,我们可以使用src/main/resources/application.properties配置文件最底部配置的
cas.authn.accept.users=casuser::Mellon
casuser::Mellon账号进行测试。casuser是账号, Mellon是密码
四、使用数据库账号数据验证
打开build.gradle文件, 在其中进行添加jdbc包的引用和相应数据库的包引用。
// 寻找文件中该部分位置,在其内部最下方增加下面最后3行
dependencies {
...
// CAS dependencies/modules may be listed here statically...
implementation "org.apereo.cas:cas-server-core-api-configuration-model"
implementation "org.apereo.cas:cas-server-webapp-init"
// 数据库连接所需依赖
implementation 'org.apereo.cas:cas-server-support-jdbc:6.3.7.2'
implementation 'mysql:mysql-connector-java:8.0.30'
}
在yml配置文件添加数据库配置, 其中query[0]配置的是固定加密算法, encode[0]配置的是盐加密算法, 可以并存
cas:
view:
cas2:
v3ForwardCompatible: true
authn:
# accept:
# enabled: false
# users: casuser::Mellon
# name: Static Credentials
jdbc:
query[0]:
url: jdbc:mysql://localhost:3306/crm_single?characterEncoding=utf8&useSSL=false&zeroDateTimeBehavior=convertToNull&tinyInt1isBit=false&serverTimezone=Asia/Shanghai&useAffectedRows=true
user: root
password: 12345678
sql: SELECT password FROM admin_user WHERE username=?
field-password: password
driver-class: com.mysql.cj.jdbc.Driver
dialect: org.hibernate.dialect.MySQLDialect
password-encoder:
# 加密类型为NONE|DEFAULT|STANDARD|BCRYPT|SCRYPT|PBKDF2这几种, 或指定java类型 org.apereo.cas.config.MyPasswordEncoder
type: BCRYPT
# character-encoding: UTF-8
# 加密算法可从中选: org.apache.commons.codec.digest.DigestUtils, 如:MD5
# encoding-algorithm: MD5
# strength: 32
# cas推荐的加盐算法
encode[0]:
url: jdbc:mysql://localhost:3306/crm_single?characterEncoding=utf8&useSSL=false&zeroDateTimeBehavior=convertToNull&tinyInt1isBit=false&serverTimezone=Asia/Shanghai&useAffectedRows=true
user: root
password: 12345678
driver-class: com.mysql.cj.jdbc.Driver
dialect: org.hibernate.dialect.MySQLDialect
sql: SELECT password,salt FROM admin_user WHERE username=?
# 密码字段
password-field-name: password
# 哈希次数
number-of-iterations: 2
# 固定静态盐, 使用静态盐加密配置此项
static-salt: .
# 盐值字段, 使用动态盐加密才需要配置此项
salt-field-name: salt
algorithm-name: MD5
# 过期字段
expired-field-name: expired
# 禁用字段
disabled-field-name: disabled
tgc:
secure: false
cas推荐的盐加密算法, java实现如下
import cn.hutool.core.util.IdUtil;
import cn.hutool.crypto.SecureUtil;
import com.google.common.collect.Lists;
import org.apache.shiro.crypto.hash.ConfigurableHashService;
import org.apache.shiro.crypto.hash.DefaultHashService;
import org.apache.shiro.crypto.hash.HashRequest;
import org.apache.shiro.util.ByteSource;
import org.junit.Test;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import java.util.List;
public class PasswordSaltTest {
private String staticSalt = ".";
private String algorithmName = "MD5";
private String encodedPassword = "123456";
// private static String dynamicSalt = "d3aff9f360a54e0a8f7ab40c24af4e1e";
@Test
public void dynamicSalt() throws Exception {
String dynamicSalt = IdUtil.simpleUUID();
// String dynamicSalt = "0a9c11b7942148eeac743a114d757ed8";
System.out.println("dynamicSalt = " + dynamicSalt);
ConfigurableHashService hashService = new DefaultHashService();
hashService.setPrivateSalt(ByteSource.Util.bytes(this.staticSalt));
hashService.setHashAlgorithmName(this.algorithmName);
hashService.setHashIterations(2);
HashRequest request = new HashRequest.Builder()
.setSalt(dynamicSalt)
.setSource(encodedPassword)
.build();
String res = hashService.computeHash(request).toHex();
System.out.println("res = " + res);
}
}
五、 服务注册管理
简介
cas客户端接入称之为service,必须经过cas的允许才能进行登录,当然不同的客户端可以做不同的事情,其中包括:
- 自定义主题(各客户端登录页自定义)
- 自定义属性(服务属性(固定)与用户属性(动态))
- 自定义协议
- 自定义登录后跳转方式,跳转路径
- 授权策略(拒绝属性、可登录时间范围限制、等等)
- 拒绝授权模式
持久化策略
- InMemory XML(通过spring bean进行内存存储)
- JSON(通过json文件存储)
推荐
❤❤❤ - YAML(通过yml文件存储)
- Mongo(文档数据库持久化)
推荐
❤❤❤❤❤ - JPA(关系型数据库持久化)
- DynameDb
- LDAP
- Cochbase
在sso初步上线时推荐采用json文件存储,后面逐步多服务注入时推荐采用Mongo进行存储,采用cas-management进行采用UI进行管理我们的数据,目前阶段,持久化策略必须和cas进行配置一致才能生效
JSON 配置示例
需求:对所有http://localhost
开头请求的service进行允许认证
在resources/services
下新建文件Localhost-10000002.json
{
"@class": "org.apereo.cas.services.RegexRegisteredService",
"serviceId": "^(http)://localhost.*",
"name": "本地服务",
"id": 10000002,
"description": "这是一个本地允许的服务,通过localhost访问都允许通过",
"evaluationOrder": 1
}
注意:json文件名字规则为${name}-${id}.json
, id必须为json文件内容id一致
json文件解释:
- @class:必须为
org.apereo.cas.services.RegisteredService
的实现类,对其他属性进行一个json反射对象,常用的有RegexRegisteredService,匹配策略为id的正则表达式- serviceId:唯一的服务id
- name: 服务名称,会显示在默认登录页
- id:全局唯一标志
- description:服务描述,会显示在默认登录页
- evaluationOrder: 匹配争取时的执行循序
除了以上说的还有很多配置策略以及节点,具体看官方文档, 配置不同的RegisteredService也会有稍微不一样
打开build.gradle文件, 添加需要的依赖
// 寻找文件中该部分位置,在其内部最下方增加下面最后3行
dependencies {
...
// 服务注册所需依赖
implementation "org.apereo.cas:cas-server-support-json-service-registry"
implementation "org.apereo.cas:cas-server-support-jpa-service-registry"
}
在yml文件添加如下配置
cas:
# Service Registry(服务注册)
service-registry:
# 开启识别Json文件,默认false
init-from-json: true
#自动扫描服务配置,默认开启
watcher-enabled: true
schedule:
# 默认2分钟扫描一遍
repeat-interval: PT2M
# 默认延迟15秒开启
start-delay: PT15S
json:
location: classpath:/services
# jpa方式
jpa:
url: jdbc:mysql://localhost:3306/sso?characterEncoding=utf8&useSSL=false&zeroDateTimeBehavior=convertToNull&tinyInt1isBit=false&serverTimezone=Asia/Shanghai&useAffectedRows=true
user: root
password: 12345678
driver-class: com.mysql.cj.jdbc.Driver
dialect: org.hibernate.dialect.MySQL5Dialect
ddl-auto: update