单点登录CAS-04:数据库存储登录帐号

单点登录CAS-04:数据库鉴权详解

1、前言

        cas.war运行之后,默认的登录帐号是配置在application.properties文件中的。这样做只是为了方便上手使用,但实际应用场景中几乎没有人会把用户帐号密码全部都写到配置文件当中。
        本文将介绍如何开启数据库验证帐号登录的功能。主要包括以下内容:
        1)禁用配置文件中的登录帐号。
        2)对接数据库实现登录验证。
                数据库验证又包括query,search和encode方式。

2、禁用配置文件帐号

        此步骤比较简单,把application.properties文件从cas.war中复制出来放到本地工程目录cas-overlay-template\src\main\resources下。打开文件查找配置项"cas.authn.accept.users="并将其注释掉:

##
# CAS Authentication Credentials
#
#cas.authn.accept.users=casuser::Mellon

        回到工程目录下执行mvn clean package然后部署运行。浏览器打开http://localhost:8080/cas 。输入帐号/密码:casuser/Mellon点登录,此时会提示“认证信息无效”。
在这里插入图片描述

3、用数据库存储登录帐号信息

3.1 准备数据库

        在本机mysql用root帐号创建cas数据库,引擎为InnoDB。然后在cas库中创建数据表cas_auth_user,插入三个用户:

用户名密码(明文)姓名是否过期是否禁用
Test01pwd123User01
Test02pwd123User02
Test03pwd123User03

        说明:CAS除了可以验证密码,还可以校验“帐号是否已过期”,“帐号是否已禁用”,因此这里开的数据表会包含这两上字段。

DROP TABLE IF EXISTS `cas_auth_user`;
CREATE TABLE `cas_auth_user` (
  `user_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
  `user_account` varchar(500) NOT NULL COMMENT '用户帐号',
  `user_pwd` varchar(500) NOT NULL COMMENT '用户密码*',
  `user_name` varchar(50) DEFAULT NULL COMMENT '用户姓名*',
  `user_email` varchar(500) DEFAULT NULL COMMENT '电子邮箱*',
  `user_tel` varchar(500) DEFAULT NULL COMMENT '联系电话*',
  `user_expired` tinyint(4) DEFAULT NULL COMMENT '用户过期状态(0:正常,1:过期)',
  `user_disabled` tinyint(4) DEFAULT NULL COMMENT '用户状态(0:启用,1:禁用)',
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户表';

insert into cas_auth_user(user_account,user_pwd,user_name,user_email,user_tel,user_expired,user_disabled) 
values ('Test01','pwd123','User01','','',0,0)
,('Test02','pwd123','User02','','',1,0)
,('Test03','pwd123','User03','','',0,1);

3.2 Database Authentication数据库鉴权

        在官方文档https://apereo.github.io/cas/5.3.x/installation/Configuration-Properties.html在右侧导航菜单找到Database Authentication如下图:
在这里插入图片描述
        如图所示Database Authentication有几个小节,简单介绍如下:
Database Authentication(数据库鉴权)
        Query Database Authentication(Query数据库鉴权)
                以【用户名】做条件查数据库,然后在内存中比较【密码】字段。允许对密码字段进行加密。
        Search Database Authentication(Search数据库鉴权)
                以【用户名+密码】做条件查数据库。允许对密码字段进行加密,如果指定加密算法,加密算法会在SQL语句之前执行,SQL语句的参数值是算出的密文。
        Bind Database Authentication(绑定数据库鉴权)
                用数据库帐号密码创建数据库链接来进行鉴权。如果输入的帐号密码能连接上数据库,则验证通过。
        Encode Database Authentication (加密数据库鉴权)
                 以【用户名】做条件查数据库,然后在内存中比较【密码】字段。允许对密码字段进行加密。
        注:此方式是在Query方式的基础上进行扩展,不同的地方在于Encode方式可以设置静态盐值和动态盐值。静态盐值对每个帐号相同,动态盐值每个帐号可以不同。

        点击上图中的"please review this guide"链接进入页面如下图:在这里插入图片描述
这一页文档有四个有用信息:
1)maven配置:
需要在cas-overlay-template工程的pom.xml中增加cas-server-support-jdbc的引用。

                <!--这个是cas的数据库驱动支持包,只是提供了一个类用来解析配置参数-->
				<dependency>
                    <groupId>org.apereo.cas</groupId>
                    <artifactId>cas-server-support-jdbc</artifactId>
                    <version>5.3.9</version>
                    <type>jar</type>
                    <scope>runtime</scope>
                </dependency> 
				<!--这个是mysql的驱动包,文档没有提及此配置,需要根据自己使用的数据库额外加上。-->
				<dependency>
					<groupId>mysql</groupId>
					<artifactId>mysql-connector-java</artifactId>
					<version>5.1.30</version>
				</dependency>

2)查看CAS支持的数据库驱动。点进去找到自己合适的配置。本文使用的库版本是MySQL5.7.18,引擎为InnoDB。
因此配置为:
        驱动:com.mysql.jdbc.Driver
        方言:org.hibernate.dialect.MySQL57InnoDBDialect
3)点中间的链接查找相关的CAS配置项。(后面会讲,这里先略过 )
4)最下方的链接是有关密码策略强制LDAP的介绍。(超纲,本文不作介绍)

3.3 Query Database Authentication(Query数据库鉴权)

        回到文档https://apereo.github.io/cas/5.3.x/installation/Configuration-Properties.html进入Query Database Authentication如下图:

Query Database Authentication文档内容
在这里插入图片描述

这一段有4项有用信息,前面3个是引用链接,最后一个是配置项:
(1)Database Settings 数据库配置
(2)Authentication Principal Transformation 身份验证主体转换
(3)Password Encoding 密码加密(数据库不存明文密码)
(4)query方式相关的配置项

3.3.1 Database Settings 数据库配置

        点开链接,得到配置项清单如下,将“${configurationKey}.”直接替换成“cas.authn.jdbc.query[0].”:

#query方式数据库连接配置
cas.authn.jdbc.query[0].user=root
cas.authn.jdbc.query[0].password=test
cas.authn.jdbc.query[0].driverClass=com.mysql.jdbc.Driver
cas.authn.jdbc.query[0].url=jdbc:mysql://localhost:3306/cas?useUnicode=true&useAffectedRows=true&characterEncoding=utf-8&autoReconnect=true&failOverReadOnly=false&zeroDateTimeBehavior=convertToNull
cas.authn.jdbc.query[0].dialect=org.hibernate.dialect.MySQL57InnoDBDialect

cas.authn.jdbc.query[0].failFastTimeout=1000
cas.authn.jdbc.query[0].isolationLevelName=ISOLATION_READ_COMMITTED
cas.authn.jdbc.query[0].healthQuery=
cas.authn.jdbc.query[0].isolateInternalQueries=false
cas.authn.jdbc.query[0].leakThreshold=10
cas.authn.jdbc.query[0].propagationBehaviorName=PROPAGATION_REQUIRED
cas.authn.jdbc.query[0].batchSize=1
cas.authn.jdbc.query[0].defaultCatalog=
cas.authn.jdbc.query[0].defaultSchema=
cas.authn.jdbc.query[0].ddlAuto=create-drop

cas.authn.jdbc.query[0].autocommit=false
cas.authn.jdbc.query[0].idleTimeout=5000

cas.authn.jdbc.query[0].dataSourceName=
cas.authn.jdbc.query[0].dataSourceProxy=false

# Hibernate-specific properties (i.e. `hibernate.globally_quoted_identifiers`)
#cas.authn.jdbc.query[0].properties.propertyName=propertyValue

cas.authn.jdbc.query[0].pool.suspension=false
cas.authn.jdbc.query[0].pool.minSize=6
cas.authn.jdbc.query[0].pool.maxSize=18
cas.authn.jdbc.query[0].pool.maxWait=2000
cas.authn.jdbc.query[0].pool.timeoutMillis=1000

3.3.2 Authentication Principal Transformation 身份验证主体转换

        点开链接,得到配置项清单如下,将“${configurationKey}.”直接替换成“cas.authn.jdbc.query[0].”:

# cas.authn.jdbc.query[0].principalTransformation.pattern=(.+)@example.org
# cas.authn.jdbc.query[0].principalTransformation.groovy.location=file:///etc/cas/config/principal.groovy
# cas.authn.jdbc.query[0].principalTransformation.suffix=
#大小写转换NONE|UPPERCASE|LOWERCASE。
#NONE:大小写敏感,其他选项忽略大小写。
cas.authn.jdbc.query[0].principalTransformation.caseConversion=NONE
# cas.authn.jdbc.query[0].principalTransformation.prefix=

3.3.3 Password Encoding 密码加密

点开链接,得到配置项清单如下,将“${configurationKey}.”直接替换成“cas.authn.jdbc.query[0].”:

# cas.authn.jdbc.query[0].passwordEncoder.type=NONE|DEFAULT|STANDARD|BCRYPT|SCRYPT|PBKDF2
# cas.authn.jdbc.query[0].passwordEncoder.characterEncoding=
# cas.authn.jdbc.query[0].passwordEncoder.encodingAlgorithm=
# cas.authn.jdbc.query[0].passwordEncoder.secret=
# cas.authn.jdbc.query[0].passwordEncoder.strength=16

此处的type是个枚举值,以下是支持的选项:

Type类型说明
NONENo password encoding (i.e. plain-text) takes place.
不对密码进行编码。纯文本。
DEFAULTUse the DefaultPasswordEncoder of CAS.
使用CAS中的默认密码编码器DefaultPasswordEncoder。
For message-digest algorithms via characterEncoding and encodingAlgorithm.
消息摘要算法通过字符编码和编码算法。
BCRYPTUse the BCryptPasswordEncoder based on the strength provided and an optional secret.
根据提供的强度和可选机密使用BCryptPasswordEncoder
SCRYPTUse the SCryptPasswordEncoder.
PBKDF2Use the Pbkdf2PasswordEncoder based on the strength provided and an optional secret.
根据提供的强度和可选机密使用Pbkdf2PasswordEncoder
STANDARDUse the StandardPasswordEncoder based on the secret provided.
使用标准密码编码器
GLIBC_CRYPTUse the GlibcCryptPasswordEncoder based on the encodingAlgorithm, strength provided and an optional secret.
根据编码算法,提供的强度和可选机密使用GlibcCryptPasswordEncoder
org.example.MyEncoder An implementation of PasswordEncoder of your own choosing.
您自己选择的密码编码器的实现。
file:///path/to/script.groovyPath to a Groovy script charged with handling password encoding operations.
负责处理密码编码操作的Groovy脚本的路径

3.3.4 Query查询配置项

#凭据条件
cas.authn.jdbc.query[0].credentialCriteria=
#查询名称
cas.authn.jdbc.query[0].name=
#顺序
cas.authn.jdbc.query[0].order=0

#登录查询语句。根据前面的说明query只传入用户名做条件
cas.authn.jdbc.query[0].sql=SELECT * FROM cas_auth_user where user_account=?
#密码字段名
cas.authn.jdbc.query[0].fieldPassword=user_pwd
#过期标识字段名(0:未过期,1:已过期)
cas.authn.jdbc.query[0].fieldExpired=user_expired
#禁用标识字段名(0:启用,1:禁用)
cas.authn.jdbc.query[0].fieldDisabled=user_disabled
#主体属性列表,作用不详,可能与LDAP有关。
cas.authn.jdbc.query[0].principalAttributeList=sn,cn:commonName,givenName

3.3.5 Query示例1:存储密码明文,帐号忽略大小写

3.3.5.1 修改配置

编辑application.properties属性文件,相关配置项如下

#第一段:
#数据库连接配置《开始》
#-------------------------------------------------------------------------------------------
cas.authn.jdbc.query[0].user=root
cas.authn.jdbc.query[0].password=test
cas.authn.jdbc.query[0].driverClass=com.mysql.jdbc.Driver
cas.authn.jdbc.query[0].url=jdbc:mysql://localhost:3306/cas?useUnicode=true&useAffectedRows=true&characterEncoding=utf-8&autoReconnect=true&failOverReadOnly=false&zeroDateTimeBehavior=convertToNull
cas.authn.jdbc.query[0].dialect=org.hibernate.dialect.MySQL57InnoDBDialect

cas.authn.jdbc.query[0].failFastTimeout=1000
#数据库事务级别(经验证,开启此项会启动失败。)
#cas.authn.jdbc.query[0].isolationLevelName=ISOLATION_READ_COMMITTED
#定期检测数据库连接测试语句
cas.authn.jdbc.query[0].healthQuery=select 1
#是否在其自己的事务中隔离内部池查询,
cas.authn.jdbc.query[0].isolateInternalQueries=false
#泄漏阈值
cas.authn.jdbc.query[0].leakThreshold=10
#spring数据库事务传递标识(经验证,开启此项会启动失败。)
#cas.authn.jdbc.query[0].propagationBehaviorName=PROPAGATION_REQUIRED
cas.authn.jdbc.query[0].batchSize=1
cas.authn.jdbc.query[0].defaultCatalog=
cas.authn.jdbc.query[0].defaultSchema=
#对象与表结构更新策略
#create 启动时删表重建,退出时不删除数据表 
#create-drop 启动时删表重建,退出时删除数据表,表不存在则报错 
#update 如果启动时表格式不一致则更新表,不删数据
#validate 启动时执行校验,字段数或字段类型不匹配则报错
cas.authn.jdbc.query[0].ddlAuto=update
#是否自动提交
cas.authn.jdbc.query[0].autocommit=true
#空闲超时时长(毫秒)
cas.authn.jdbc.query[0].idleTimeout=5000
#数据源名称。
cas.authn.jdbc.query[0].dataSourceName=
#是否使用数据源代理
cas.authn.jdbc.query[0].dataSourceProxy=false

# Hibernate-specific properties (i.e. `hibernate.globally_quoted_identifiers`)
#cas.authn.jdbc.query[0].properties.propertyName=propertyValue
#以下是链接池配置
cas.authn.jdbc.query[0].pool.suspension=false
cas.authn.jdbc.query[0].pool.minSize=6
cas.authn.jdbc.query[0].pool.maxSize=18
cas.authn.jdbc.query[0].pool.maxWait=2000
cas.authn.jdbc.query[0].pool.timeoutMillis=1000
#数据库连接配置《结束》
#-------------------------------------------------------------------------------------------

#第二段:
# 主体转换策略《开始》
# cas.authn.jdbc.query[0].principalTransformation.pattern=(.+)@example.org
# cas.authn.jdbc.query[0].principalTransformation.groovy.location=file:///etc/cas/config/principal.groovy
# cas.authn.jdbc.query[0].principalTransformation.suffix=
#大小写转换NONE|UPPERCASE|LOWERCASE。
#NONE:大小写敏感,其他选项忽略大小写。
#注:经验证设为UPPERCASE,则输入的帐号会被转换成大写,数据库里的帐号也会转换成大写。若为LOWERCASE则全部转换成小写。
cas.authn.jdbc.query[0].principalTransformation.caseConversion=UPPERCASE
# cas.authn.jdbc.query[0].principalTransformation.prefix=
# 主体转换策略《结束》

#第三段:
# 查询语句相关配置《开始》
#------------------------------------------------------------------------------------------------------------
#凭据条件
cas.authn.jdbc.query[0].credentialCriteria=
#查询名称
cas.authn.jdbc.query[0].name=
#顺序
cas.authn.jdbc.query[0].order=0

#登录查询语句。根据前面的说明query只传入用户名做条件
cas.authn.jdbc.query[0].sql=SELECT * FROM cas_auth_user where user_account=?
#密码字段名
cas.authn.jdbc.query[0].fieldPassword=user_pwd
#过期标识字段名(0:未过期,1:已过期)
cas.authn.jdbc.query[0].fieldExpired=user_expired
#禁用标识字段名(0:启用,1:禁用)
cas.authn.jdbc.query[0].fieldDisabled=user_disabled
#主体属性列表,作用不详,可能与LDAP有关。
cas.authn.jdbc.query[0].principalAttributeList=sn,cn:commonName,givenName
# 查询语句相关配置《结束》
#------------------------------------------------------------------------------------------------------------

#第四段:
# 密码加密策略《开始》
# 加密类型NONE|DEFAULT|STANDARD|BCRYPT|SCRYPT|PBKDF2
# 这里写NONE,用明文密码
 cas.authn.jdbc.query[0].passwordEncoder.type=NONE
# cas.authn.jdbc.query[0].passwordEncoder.characterEncoding=
# cas.authn.jdbc.query[0].passwordEncoder.encodingAlgorithm=
# cas.authn.jdbc.query[0].passwordEncoder.secret=
# cas.authn.jdbc.query[0].passwordEncoder.strength=16
# 密码加密策略《结束》
3.3.5.2 测试验证

        执行mvn clean package构建部署后,打开浏览器,进入http://127.0.0.1:8080/cas
输入帐号登录,结果如下:

帐号/密码期望结果实际结果
test01/pwd123登录成功在这里插入图片描述
test02/pwd123密码已过期在这里插入图片描述
test03/pwd123帐号已禁用在这里插入图片描述
TesT01/pwd123忽略大小写登录成功在这里插入图片描述

3.3.6 Query示例2:存储密码MD5,帐号忽略大小写

3.3.6.1 修改配置

        配置信息在Query示例1的基础上进行修改第四段的配置。编辑application.properties属性文件,相关配置项如下

#第四段:
# 密码加密策略《开始》
# 加密类型NONE|DEFAULT|STANDARD|BCRYPT|SCRYPT|PBKDF2
# 这里写DEFAULT,用默认编码器进行编码
 cas.authn.jdbc.query[0].passwordEncoder.type=DEFAULT
#指定字符编码为UTF-8
 cas.authn.jdbc.query[0].passwordEncoder.characterEncoding=UTF-8
 #指定算法为MD5
 cas.authn.jdbc.query[0].passwordEncoder.encodingAlgorithm=MD5
# cas.authn.jdbc.query[0].passwordEncoder.secret=
 cas.authn.jdbc.query[0].passwordEncoder.strength=32
# 密码加密策略《结束》
3.3.6.2 开启http协议

         此处不详细展开,请看tomcat官网文档。本文用的是tomcat8.5.41
https://tomcat.apache.org/tomcat-8.5-doc/ssl-howto.html#Configuration

3.3.6.3 准备测试数据

打开MD5在线加密网站:https://www.cmd5.com/
本文用的密码是"pwd123"。得到密文是45cb41b32dcfb917ccd8614f1536d6da
生成初始化语句并插入到cas_auth_user表中
insert into cas_auth_user(user_account,user_pwd,user_name,user_email,user_tel,user_expired,user_disabled)
values (‘Test05’,‘45cb41b32dcfb917ccd8614f1536d6da’,‘User05’,’’,’’,0,0);

3.3.6.4 测试验证

        打开浏览器输入网址:https://localhost:8443/cas/

用户/密码期望结果实际结果
test05/pwd123登录成功在这里插入图片描述
test01/pwd123登录失败在这里插入图片描述

3.3.7 Query示例3:存储密码密文(自定义加密算法),帐号忽略大小写

3.3.7.1 修改配置

        配置信息在Query示例1的基础上进行修改第四段的配置。编辑application.properties属性文件。
此处直接在type参数中指定全类名,其他配置不要。相关配置项如下:

#第四段:
# 密码加密策略《开始》
# 加密类型NONE|DEFAULT|STANDARD|BCRYPT|SCRYPT|PBKDF2
# 这里写DEFAULT,用默认编码器进行编码
 cas.authn.jdbc.query[0].passwordEncoder.type=org.example.cas.MySaltEncoder
#指定字符编码为UTF-8
 #cas.authn.jdbc.query[0].passwordEncoder.characterEncoding=
 #指定算法为MD5
 #cas.authn.jdbc.query[0].passwordEncoder.encodingAlgorithm=
# cas.authn.jdbc.query[0].passwordEncoder.secret=
 #cas.authn.jdbc.query[0].passwordEncoder.strength=32
# 密码加密策略《结束》
3.3.7.2 编写自定义的加密类

        此处特别说明一下,根据官网文档的介绍(见本文3.4.3 Password Encoding)若要实现自定义的加密算法,官方推荐继承org.springframework.security.crypto.password.AbstractPasswordEncoder类。这个抽像类在jar包spring-security-core-4.2.10.RELEASE.jar中。我打下的cas-overlay-template工程是5.3.x版本,执行mvn clean package后在cas.war中会有四个版本不同的jar包分别是:

spring-security-core-4.2.10.RELEASE.jar加密核心包
spring-security-crypto-4.2.10.RELEASE.jar加密包
spring-security-core-4.2.8.RELEASE.jar加密核心包
spring-security-crypto-4.2.3.RELEASE.jar加密包

        出现这种情况,估计是cas-overlay-template工程有多个包引用了core和crypto的不同版本导致。陈述这些情况只是为了说明实现的抽象类在哪里,以及实施过程中可能会遇到哪些问题。回到正题继续往下。
用jd_gui反编译AbstractPasswordEncoder这个类就能看到里面的一些代码逻辑,此步骤在本文档不会截图说明,有兴趣的请自行研究。这是官方文档给出的继承方式的代码模板:

package org.example.cas;
import org.springframework.security.crypto.codec.*;
import org.springframework.security.crypto.password.*;
public class MyEncoder extends AbstractPasswordEncoder {
    @Override
    protected byte[] encode(CharSequence rawPassword, byte[] salt) {
        return ...
    }
}

        另一种方式是实现org.springframework.security.crypto.password.PasswordEncoder接口,这也是AbstractPasswordEncoder这个抽象实现了的接口。笔者的实施办法如下:
在eclipse中创建一个java项目,把spring-security-core-4.2.10.RELEASE.jar这个包引入到工程中,jdk使用jdk1.8.0_181版本。然后创建一个MySaltEncoder类代码全部贴出如下:

package org.example.cas;

import java.util.Base64;
import java.util.Random;

import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * @author    : hangui_zhang
 * @create by : 2020-05-22 14:20:11
 */
public class MySaltEncoder implements PasswordEncoder {

    /**
     * 盐值长度,默认为8
     */
    private int saltSize = 8;
    /**
     * 此方法用于生成一个随机盐值:<pre/>
     * 1.长度为8位
     * 2.内容为ASCII 33-126 之间的字符。(查ASCII码表可知这个区间的字符是可见字符。
     */
    private byte[] randomSalt(int saltSize) {
        byte[] salt = new byte[saltSize];
        int maxCharNum = 126;
        int minCharNum = 33; //排除不可见字符
        int maxIndex = maxCharNum - minCharNum+1;
        Random random = new Random();
        for(int i = 0;i < salt.length;i++) {
            //确保值落在33-126之间。
            int randomChar = random.nextInt(maxIndex) + minCharNum;
            salt[i] = (byte)randomChar;
        }
        return salt;
    }

    /**
     * 接口方法——加密:传入原始密码,返回密文
     */
    @Override
    public String encode(CharSequence rawPassword) {
        String rawPasswordStr = rawPassword.toString();
        //1.加8位盐值——随机字符串
        byte[] randomSalt = randomSalt(saltSize);
        //2.把原始密码和盐值混在一起加密
        String encode = encode(rawPasswordStr,randomSalt);
        return encode;
    }

    /**
     * 接口方法——密码比较:传入用户输入的密码和数据库查到的密码。逻辑如下:
     * 1.从数据库查到的密文中解出盐值
     * 2.拿盐值和输入的密码进行加密。
     * 3.比较算出的密文跟数据库查到的密文。
     * 4.返回比较结果。true为密码正确。
     */
    @Override
    public boolean matches(CharSequence rawPassword, String encodedPassword) {
        //1.先Base64解密
        byte[] decode = Base64.getDecoder().decode(encodedPassword.getBytes());
        String pwdStr = new String(decode);
        if(saltSize >= pwdStr.length()) {
            System.err.println("密码长度错误。无效的密码!");
            return false;
        }
        //2.得到盐值字符串
        String saltStr = pwdStr.substring(0,saltSize);
        //3.加密输入密码并对比
        String encodePwd = encode(rawPassword,saltStr.getBytes());
        return encodedPassword.contains(encodePwd);
    }

    /**
     * 自已定义的加密方法,增加盐值参数。参考org.springframework.security.crypto.password.AbstractPasswordEncoder的思路
     * 程序逻辑极其简单,请看注释。
     * @author    : hangui_zhang
     * @create by : 2020-05-22 19:27:19
     * @param rawPassword
     * @param salt
     * @return
     */
    protected String encode(CharSequence rawPassword, byte[] salt) {
        //1.字符串反转
        String string = rawPassword.toString();
        StringBuffer sb = new StringBuffer(string);
        String reversedPwd = sb.reverse().toString();
        //2.拼接盐值字符串
        String saltStr = new String(salt);
        String pwdStr = saltStr+ reversedPwd;
        //3.Base64加密
        byte[] encode = java.util.Base64.getEncoder().encode(pwdStr.getBytes());
        String encodeStr = new String(encode);
        System.out.println("密码明文:" + string + ",盐值:" + saltStr + ",密码密文:" + encodeStr);
        return encodeStr;
    }
    //测试方法密码:pwd123得到密文:bl1KQ3A1JS4zMjFkd3A=
    public static void main(String[] args) {
        MySaltEncoder my = new MySaltEncoder();
        System.out.println(my.encode("pwd123"));
    }
}
3.3.7.3 准备测试数据

        拿到以上main方法输出的密文,插入一个测试用户数据:
insert into cas_auth_user(user_account,user_pwd,user_name,user_email,user_tel,user_expired,user_disabled)
values (‘Test06’,‘bl1KQ3A1JS4zMjFkd3A=’,‘User06’,’’,’’,0,0);

3.3.7.4 测试验证

        1)cmd窗口进入cas-overlay-template工程所在路径,执行mvn clean package。
        2)用压缩工具打开cas.war,进入cas.war\WEB-INF\classes\目录。
        3)打开文件夹资源窗口,进入到MySaltEncoder 所在工程的编译路径,将org\example\cas\MySaltEncoder.class拖到压缩工具窗口,复制后路径为:cas.war\WEB-INF\classes\org\example\cas\MySaltEncoder.class
        4)运行tomcat启动cas.war
        5)打开浏览器输入网址:https://localhost:8443/cas/

用户/密码期望结果实际结果
test06/pwd123登录成功在这里插入图片描述
test05/pwd123登录失败在这里插入图片描述

3.4 Search Database Authentication(Search数据库鉴权)

        回到文档https://apereo.github.io/cas/5.3.x/installation/Configuration-Properties.html进入Search Database Authentication如下图:
说明:Search与Query文档内容几乎完全一样,不同的是配置项的前缀。以及第4小节的配置项

Search Database Authentication文档内容
在这里插入图片描述

这一段有4项有用信息,前面3个是引用链接,最后一个是配置项:
(1)Database Settings 数据库配置
(2)Authentication Principal Transformation 身份验证主体转换
(3)Password Encoding 密码加密(数据库不存明文密码)
(4)search方式相关的配置项

3.4.1 Database Settings 数据库配置

        点开链接,得到配置项清单如下,将“${configurationKey}.”直接替换成“cas.authn.jdbc.search[0].”

与Query方式相同,只是得到配置项后,将 “${configurationKey}.”直接替换成“cas.authn.jdbc.search[0].

3.4.2 Authentication Principal Transformation 身份验证主体转换

        点开链接,得到配置项清单如下,将“${configurationKey}.”直接替换成“cas.authn.jdbc.search[0].”

与Query方式相同,只是得到配置项后,将 “${configurationKey}.”直接替换成“cas.authn.jdbc.search[0].

3.4.3 Password Encoding 密码加密

        点开链接,得到配置项清单如下,将“${configurationKey}.”直接替换成“cas.authn.jdbc.search[0].”

与Query方式相同,只是得到配置项后,将 “${configurationKey}.”直接替换成“cas.authn.jdbc.search[0].

3.4.4 Search查询配置项

# 凭据条件
 cas.authn.jdbc.search[0].credentialCriteria=
# 查询名称
 cas.authn.jdbc.search[0].name=
# 顺序 
 cas.authn.jdbc.search[0].order=0
 
#帐号字段名
 cas.authn.jdbc.search[0].fieldUser=user_account
#用户表名
 cas.authn.jdbc.search[0].tableUsers=cas_auth_user
#密码字段名
 cas.authn.jdbc.search[0].fieldPassword=user_pwd

3.4.5 search示例1:存储密码明文,帐号忽略大小写

3.4.5.1 修改配置

        编辑application.properties属性文件,相关配置项如下:
说明:第一段与前面的章节基本一样,只是把query[0]改成search[0],此处贴出完整配置,以免跳转影响阅读。

#第一段:
#数据库连接配置《开始》
#-------------------------------------------------------------------------------------------
cas.authn.jdbc.search[0].user=root
cas.authn.jdbc.search[0].password=test
cas.authn.jdbc.search[0].driverClass=com.mysql.jdbc.Driver
cas.authn.jdbc.search[0].url=jdbc:mysql://localhost:3306/cas?useUnicode=true&useAffectedRows=true&characterEncoding=utf-8&autoReconnect=true&failOverReadOnly=false&zeroDateTimeBehavior=convertToNull
cas.authn.jdbc.search[0].dialect=org.hibernate.dialect.MySQL57InnoDBDialect

cas.authn.jdbc.search[0].failFastTimeout=1000
#数据库事务级别(经验证,开启此项会启动失败。)
#cas.authn.jdbc.search[0].isolationLevelName=ISOLATION_READ_COMMITTED
#定期检测数据库连接测试语句
cas.authn.jdbc.search[0].healthQuery=select 1
#是否在其自己的事务中隔离内部池查询,
cas.authn.jdbc.search[0].isolateInternalQueries=false
#泄漏阈值
cas.authn.jdbc.search[0].leakThreshold=10
#spring数据库事务传递标识(经验证,开启此项会启动失败。)
#cas.authn.jdbc.search[0].propagationBehaviorName=PROPAGATION_REQUIRED
cas.authn.jdbc.search[0].batchSize=1
cas.authn.jdbc.search[0].defaultCatalog=
cas.authn.jdbc.search[0].defaultSchema=
#对象与表结构更新策略
#create 启动时删表重建,退出时不删除数据表 
#create-drop 启动时删表重建,退出时删除数据表,表不存在则报错 
#update 如果启动时表格式不一致则更新表,不删数据
#validate 启动时执行校验,字段数或字段类型不匹配则报错
cas.authn.jdbc.search[0].ddlAuto=update
#是否自动提交
cas.authn.jdbc.search[0].autocommit=true
#空闲超时时长(毫秒)
cas.authn.jdbc.search[0].idleTimeout=5000
#数据源名称。
cas.authn.jdbc.search[0].dataSourceName=
#是否使用数据源代理
cas.authn.jdbc.search[0].dataSourceProxy=false

# Hibernate-specific properties (i.e. `hibernate.globally_quoted_identifiers`)
#cas.authn.jdbc.search[0].properties.propertyName=propertyValue
#以下是链接池配置
cas.authn.jdbc.search[0].pool.suspension=false
cas.authn.jdbc.search[0].pool.minSize=6
cas.authn.jdbc.search[0].pool.maxSize=18
cas.authn.jdbc.search[0].pool.maxWait=2000
cas.authn.jdbc.search[0].pool.timeoutMillis=1000
#数据库连接配置《结束》
#-------------------------------------------------------------------------------------------

#第二段:
# 主体转换策略《开始》
# cas.authn.jdbc.search[0].principalTransformation.pattern=(.+)@example.org
# cas.authn.jdbc.search[0].principalTransformation.groovy.location=file:///etc/cas/config/principal.groovy
# cas.authn.jdbc.search[0].principalTransformation.suffix=
#大小写转换NONE|UPPERCASE|LOWERCASE。
#NONE:大小写敏感,其他选项忽略大小写。
#注:经验证设为UPPERCASE,则输入的帐号会被转换成大写,数据库里的帐号也会转换成大写。若为LOWERCASE则全部转换成小写。
cas.authn.jdbc.search[0].principalTransformation.caseConversion=UPPERCASE
# cas.authn.jdbc.search[0].principalTransformation.prefix=
# 主体转换策略《结束》

#第三段:
# 查询语句相关配置《开始》
#------------------------------------------------------------------------------------------------------------
#凭据条件
cas.authn.jdbc.search[0].credentialCriteria=
#查询名称
cas.authn.jdbc.search[0].name=
#顺序
cas.authn.jdbc.search[0].order=0

#帐号字段名
 cas.authn.jdbc.search[0].fieldUser=user_account
#用户表名
 cas.authn.jdbc.search[0].tableUsers=cas_auth_user
#密码字段名
 cas.authn.jdbc.search[0].fieldPassword=user_pwd
# 查询语句相关配置《结束》
#------------------------------------------------------------------------------------------------------------

#第四段:
# 密码加密策略《开始》
# 加密类型NONE|DEFAULT|STANDARD|BCRYPT|SCRYPT|PBKDF2
# 这里写NONE,用明文密码
 cas.authn.jdbc.search[0].passwordEncoder.type=NONE
# cas.authn.jdbc.search[0].passwordEncoder.characterEncoding=
# cas.authn.jdbc.search[0].passwordEncoder.encodingAlgorithm=
# cas.authn.jdbc.search[0].passwordEncoder.secret=
# cas.authn.jdbc.search[0].passwordEncoder.strength=16
# 密码加密策略《结束》
3.4.5.2 测试验证

        执行mvn clean package构建部署后,打开浏览器,进入http://127.0.0.1:8080/cas
输入帐号登录,结果如下:

帐号/密码期望结果实际结果
test01/pwd123登录成功在这里插入图片描述

3.4.6 Search示例2:存储密码MD5,帐号忽略大小写

3.4.6.1 修改配置

        配置信息在Search示例1的基础上进行修改第四段的配置。编辑application.properties属性文件,相关配置项如下:

#第四段:
# 密码加密策略《开始》
# 加密类型NONE|DEFAULT|STANDARD|BCRYPT|SCRYPT|PBKDF2
# 这里写DEFAULT,用默认编码器进行编码
 cas.authn.jdbc.search[0].passwordEncoder.type=DEFAULT
#指定字符编码为UTF-8
 cas.authn.jdbc.search[0].passwordEncoder.characterEncoding=UTF-8
 #指定算法为MD5
 cas.authn.jdbc.search[0].passwordEncoder.encodingAlgorithm=MD5
# cas.authn.jdbc.search[0].passwordEncoder.secret=
 cas.authn.jdbc.search[0].passwordEncoder.strength=32
# 密码加密策略《结束》
3.4.6.2 开启https协议

        开启tomcat的https协议访问
        此处不详细展开,请看tomcat官网文档。本文用的是tomcat8.5.41
https://tomcat.apache.org/tomcat-8.5-doc/ssl-howto.html#Configuration

3.4.6.3 准备测试数据

打开MD5在线加密网站:https://www.cmd5.com/
本文用的密码是"pwd123"。得到密文是45cb41b32dcfb917ccd8614f1536d6da
生成初始化语句并插入到cas_auth_user表中
insert into cas_auth_user(user_account,user_pwd,user_name,user_email,user_tel,user_expired,user_disabled)
values (‘Test07’,‘45cb41b32dcfb917ccd8614f1536d6da’,‘User07’,’’,’’,0,0);

3.4.6.4 测试验证

打开浏览器输入网址:https://localhost:8443/cas/
输入帐号登录,结果如下:

帐号/密码期望结果实际结果
test07/pwd123登录成功在这里插入图片描述

3.4.7 Search示例3:存储密码密文(自定义加密算法),帐号忽略大小写

3.4.7.1 修改application.properties配置文件

与3.3.7.1的相同,此处不赘述。

3.4.7.2 编写自定义的加密类

此处先特别再强调一下,Query方式与Search方式加密策略的不同:
Query:
        1、查库得到密文:执行SQL语句select * from {用户名} where username={用户名}
        2、解析密文得到盐值,用盐值和用户输入的密码进行加密。
        3、比较加密的字符串与数据库密文是否一致。

Search:
        1、得到密文:调用加密算法将用户输入的密码进行加密。
        2、查询数据库:执行SQL语句select * from {用户名} where username={用户名} and password={密文}

        掌握这个规则以后,实现就比较容易了。只要确保每次调用加密类的encode()方法时,传入参数相同则返回的结果必然相同就可以了。
因此本示例在3.3.7 Query示例3的基础上进行修改。只需要修改randomSalt方法返回一个8位长度的固定字符串的盐值。这样做可以用最少的篇幅进行说明,同时修改的代码也最少:

    /**
     * 此方法用于生成一个随机盐值:<pre/>
     * 1.长度为8位
     * 2.内容为ASCII 33-126 之间的字符。(查ASCII码表可知这个区间的字符是可见字符。
     */
    private byte[] randomSalt(int saltSize) {
        String saltStr = "password";
        byte[] salt = saltStr.getBytes();
        return salt;
    }
3.4.7.3 准备测试数据

执行测试类的encode方法传入pwd123,得到密文cGFzc3dvcmQzMjFkd3A=
插入一个测试用户数据:
insert into cas_auth_user(user_account,user_pwd,user_name,user_email,user_tel,user_expired,user_disabled)
values (‘Test08’,‘cGFzc3dvcmQzMjFkd3A=’,‘User08’,’’,’’,0,0);

3.3.7.4 登录验证

1)构建并运行cas.war
2)打开浏览器输入网址:https://localhost:8443/cas/

用户/密码期望结果实际结果
test08/pwd123登录成功在这里插入图片描述
test05/pwd123登录失败在这里插入图片描述

3.5 Bind Database Authentication(Bind数据库鉴权)

Bind方式的鉴权原理是用root用户创建多个数据库用户,并赋予登录权限。
启动cas.war后在登录页面输入数据库用户和密码提交登录请求。
cas后台拿到输入的帐号/密码信息后用于创建数据库连接的connection如果创建成功,则认为登录有效。
经验证,Bind方式默认是无法验证通过的,因为底层调用的是DataSource.getConnection(user,password)来创建链接,而默认的类库这个方法都是没有实现的。

cas.war默认是用com.zaxxer.hikari.HikariDataSource作为数据源(这是由spring决定的),其.getConnection(user,password)反编译如下:
在这里插入图片描述
如果在tomcat的context中添加数据源,则会用org.apache.tomcat.dbcp.dbcp2.BasicDataSource作为数据源。其.getConnection(user,password)如下:
在这里插入图片描述
经过研究后,本文决定不再深入研究Bind方式的鉴权。理由也很简单:
        1、这种方式其实意义不大。
                如果一定要说有什么意义的话,可能就是这种方式不需要建库,不需要建表,不需要维护数据表。
        2、实际业务场景不太可能采用这种鉴权方式。

如果一定实现Bind方式其实也不难,只需要改写相关jar包使getConnection(user,password)能够得到链接就可以了。

3.6 Encode Database Authentication(Encode数据库鉴权)

回到文档https://apereo.github.io/cas/5.3.x/installation/Configuration-Properties.html进入Encode Database Authentication如下图:

Encode Database Authentication文档内容
A JDBC querying handler that will pull back the password and the private salt value for a user and validate the encoded password using the public salt value.
JDBC查询处理器将返回一个用户的密码和私有盐值,并且使用公有盐值验证密文。
Assumes everything is inside the same database table.
假设所有信息都在同一个数据库表中。 Supports settings for number of iterations as well as private salt.
支持设置私有盐值的迭代次数
This password encoding method combines the private Salt and the public salt which it prepends to the password before hashing.
密码加密算法会先按格式:私有盐值+公有盐值+原始密码 拼字符串,然后对拼成的字串做哈希运算得到密文。 If multiple iterations are used, the bytecode hash of the first iteration is rehashed without the salt values.
如果使用多次迭代,则第一次迭代的字节码会在不加盐值的情况下重新哈希(散列) The final hash is converted to hex before comparing it to the database value.
最后一次哈希运算的结果会先转换成16进制字符串再与数据库中的密码字段值进行对比。
在这里插入图片描述

这一段有4项有用信息,前面3个是引用链接,最后一个是配置项:
(1)Database Settings 数据库配置
(2)Authentication Principal Transformation 身份验证主体转换
(3)Password Encoding 密码加密(数据库不存明文密码)
(4)encode加密配置项

3.6.1 Database Settings 数据库配置

        点开链接,得到配置项清单如下,将“${configurationKey}.”直接替换成“cas.authn.jdbc.encode[0].”

与Query方式相同,只是得到配置项后,将 “${configurationKey}.”直接替换成“cas.authn.jdbc.encode[0].

3.6.2 Authentication Principal Transformation 身份验证主体转换

        点开链接,得到配置项清单如下,将“${configurationKey}.”直接替换成“cas.authn.jdbc.encode[0].”

与Query方式相同,只是得到配置项后,将 “${configurationKey}.”直接替换成“cas.authn.jdbc.encode[0].

3.6.3 Password Encoding 密码加密

        点开链接,得到配置项清单如下,将“${configurationKey}.”直接替换成“cas.authn.jdbc.encode[0].”

与Query方式相同,只是得到配置项后,将 “${configurationKey}.”直接替换成“cas.authn.jdbc.encode[0].

3.6.4 encode加密配置项

#Hash加密算法见cas-server-support-jdbc-authentication-5.3.9.jar中org.apereo.cas.adaptors.jdbc.QueryAndEncodeDatabaseAuthenticationHandler类
#Hash运算迭代次数(静态-每个帐号都相同),若同时设置了numberOfIterationsFieldName参数,则以数据库中的值为准。
# cas.authn.jdbc.encode[0].numberOfIterations=0
#Hash运算迭代次数(动态-每个帐号可以不同),若设置此参数则从数据库中查找
# cas.authn.jdbc.encode[0].numberOfIterationsFieldName=numIterations
#静态盐值(私有盐-每个帐号都相同),可以为空。
# cas.authn.jdbc.encode[0].staticSalt=
#动态盐值(每个帐号可以不同)
# cas.authn.jdbc.encode[0].saltFieldName=salt

#SQL查询语句,以用户帐号作为条件
# cas.authn.jdbc.encode[0].sql=
#加密算法名称.如MD5
# cas.authn.jdbc.encode[0].algorithmName=
#密码字段
# cas.authn.jdbc.encode[0].passwordFieldName=password
#密码过期标识字段。1为过期
# cas.authn.jdbc.encode[0].expiredFieldName=
#帐号禁用标识字段。1为禁用
# cas.authn.jdbc.encode[0].disabledFieldName=

# cas.authn.jdbc.encode[0].credentialCriteria=
# cas.authn.jdbc.encode[0].name=
# cas.authn.jdbc.encode[0].order=0

3.6.5 Encode示例1:存储密码密文(MD5&Hash2次),帐号忽略大小写

3.6.5.1 修改配置

编辑application.properties属性文件,相关配置项如下

#第一段:encode方式数据库连接配置
cas.authn.jdbc.encode[0].user=root
cas.authn.jdbc.encode[0].password=test
cas.authn.jdbc.encode[0].driverClass=com.mysql.jdbc.Driver
cas.authn.jdbc.encode[0].url=jdbc:mysql://localhost:3306/cas?useUnicode=true&useAffectedRows=true&characterEncoding=utf-8&autoReconnect=true&failOverReadOnly=false&zeroDateTimeBehavior=convertToNull
cas.authn.jdbc.encode[0].dialect=org.hibernate.dialect.MySQL57InnoDBDialect

cas.authn.jdbc.encode[0].failFastTimeout=1000
cas.authn.jdbc.encode[0].isolationLevelName=ISOLATION_READ_COMMITTED
cas.authn.jdbc.encode[0].healthQuery=
cas.authn.jdbc.encode[0].isolateInternalQueries=false
cas.authn.jdbc.encode[0].leakThreshold=10
cas.authn.jdbc.encode[0].propagationBehaviorName=PROPAGATION_REQUIRED
cas.authn.jdbc.encode[0].batchSize=1
cas.authn.jdbc.encode[0].defaultCatalog=
cas.authn.jdbc.encode[0].defaultSchema=
cas.authn.jdbc.encode[0].ddlAuto=create-drop

cas.authn.jdbc.encode[0].autocommit=false
cas.authn.jdbc.encode[0].idleTimeout=5000

cas.authn.jdbc.encode[0].dataSourceName=
cas.authn.jdbc.encode[0].dataSourceProxy=false

# Hibernate-specific properties (i.e. `hibernate.globally_quoted_identifiers`)
#cas.authn.jdbc.encode[0].properties.propertyName=propertyValue

cas.authn.jdbc.encode[0].pool.suspension=false
cas.authn.jdbc.encode[0].pool.minSize=6
cas.authn.jdbc.encode[0].pool.maxSize=18
cas.authn.jdbc.encode[0].pool.maxWait=2000
cas.authn.jdbc.encode[0].pool.timeoutMillis=1000

#第二段:主体转换
# cas.authn.jdbc.encode[0].principalTransformation.pattern=(.+)@example.org
# cas.authn.jdbc.encode[0].principalTransformation.groovy.location=file:///etc/cas/config/principal.groovy
# cas.authn.jdbc.encode[0].principalTransformation.suffix=
#大小写转换NONE|UPPERCASE|LOWERCASE。
#NONE:大小写敏感,其他选项忽略大小写。
cas.authn.jdbc.encode[0].principalTransformation.caseConversion=UPPERCASE
# cas.authn.jdbc.encode[0].principalTransformation.prefix=

#第三段:查询与迭代哈希
#Hash加密算法见cas-server-support-jdbc-authentication-5.3.9.jar中org.apereo.cas.adaptors.jdbc.QueryAndEncodeDatabaseAuthenticationHandler类
#Hash运算迭代次数(静态-每个帐号都相同),若同时设置了numberOfIterationsFieldName参数,则以数据库中的值为准。
 cas.authn.jdbc.encode[0].numberOfIterations=2
#Hash运算迭代次数(动态-每个帐号可以不同),若设置此参数则从数据库中查找
 cas.authn.jdbc.encode[0].numberOfIterationsFieldName=user_hashtimes
#静态盐值(私有盐-每个帐号都相同),可以为空。
 cas.authn.jdbc.encode[0].staticSalt=pub-salt
#动态盐值(每个帐号可以不同)
 cas.authn.jdbc.encode[0].saltFieldName=user_salt

#SQL查询语句,以用户帐号作为条件
 cas.authn.jdbc.encode[0].sql=select * from cas_encode_user where user_account = ?
#加密算法名称.如MD5
 cas.authn.jdbc.encode[0].algorithmName=MD5
#密码字段
 cas.authn.jdbc.encode[0].passwordFieldName=user_pwd
#密码过期标识字段。1为过期
 cas.authn.jdbc.encode[0].expiredFieldName=user_expired
#帐号禁用标识字段。1为禁用
 cas.authn.jdbc.encode[0].disabledFieldName=user_disabled

# cas.authn.jdbc.encode[0].credentialCriteria=
# cas.authn.jdbc.encode[0].name=
# cas.authn.jdbc.encode[0].order=0
#第四段:加密
 cas.authn.jdbc.encode[0].passwordEncoder.type=NONE|DEFAULT|STANDARD|BCRYPT|SCRYPT|PBKDF2
 cas.authn.jdbc.encode[0].passwordEncoder.characterEncoding=
 cas.authn.jdbc.encode[0].passwordEncoder.encodingAlgorithm=
 cas.authn.jdbc.encode[0].passwordEncoder.secret=
 cas.authn.jdbc.encode[0].passwordEncoder.strength=32
3.6.5.2 准备测试数据

建一张数据表(带盐值和Hash次数)

DROP TABLE IF EXISTS `cas_encode_user`;
CREATE TABLE `cas_encode_user` (
  `user_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
  `user_account` varchar(500) NOT NULL COMMENT '用户帐号',
  `user_pwd` varchar(500) NOT NULL COMMENT '用户密码*',
  `user_name` varchar(50) DEFAULT NULL COMMENT '用户姓名*',
  `user_email` varchar(500) DEFAULT NULL COMMENT '电子邮箱*',
  `user_tel` varchar(500) DEFAULT NULL COMMENT '联系电话*',
  `user_expired` tinyint(4) DEFAULT NULL COMMENT '用户过期状态(0:正常,1:过期)',
  `user_disabled` tinyint(4) DEFAULT NULL COMMENT '用户状态(0:启用,1:禁用)',
  `user_hashtimes` tinyint(4) DEFAULT 1 COMMENT '散列运算次数',
  `user_salt` varchar(50) DEFAULT 'cas-salt' COMMENT '散列运算盐值',
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='盐值加密用户表';

        写一个工具类:SaltMd5调用org.apache.shiro.crypto.hash.ConfigurableHashService对密码进行加密,参考cas-server-support-jdbc-authentication-5.3.9.jar中org.apereo.cas.adaptors.jdbc.QueryAndEncodeDatabaseAuthenticationHandler.digestEncodedPassword(String encodedPassword, Map<String, Object> values)方法

package com.test;

import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author    : hangui_zhang
 * @create by : 2020-05-25 16:56:09
 */
public class SaltMd5 {
    private static final Logger LOGGER = LoggerFactory.getLogger(SaltMd5.class);
    protected String algorithmName;
    protected String sql;
    protected String passwordFieldName = "password";
    protected String saltFieldName = "user_salt";
    protected String expiredFieldName;
    protected String disabledFieldName;
    protected String numberOfIterationsFieldName;
    protected long numberOfIterations;
    protected String staticSalt = "pub-salt";
    /**
     * @author    : hangui_zhang
     * @create by : 2020-05-25 16:56:09
     * @param args
     */
    public static void main(String[] args) {
        SaltMd5 salt = new SaltMd5();
        String encodedPassword = "pwd123";
        
        Map<String,Object> values = new HashMap<String,Object>();
        values.put(salt.saltFieldName,"cas-salt");
        values.put(salt.numberOfIterationsFieldName,1);
        salt.numberOfIterations = 1;
        salt.algorithmName = "MD5";
        
        String res = salt.digestEncodedPassword(encodedPassword,values);
        System.out.println(res);
    }
    
    protected String digestEncodedPassword(String encodedPassword, Map<String, Object> values)
    {
      ConfigurableHashService hashService = new DefaultHashService();
      System.out.println("privateSalt:"+this.staticSalt);
      if (StringUtils.isNotBlank(this.staticSalt)) {
        hashService.setPrivateSalt(ByteSource.Util.bytes(this.staticSalt));
      }
      System.out.println("algorithmName:"+algorithmName);
      hashService.setHashAlgorithmName(this.algorithmName);
      
      Long numOfIterations = Long.valueOf(this.numberOfIterations);
      if (values.containsKey(this.numberOfIterationsFieldName))
      {
        String longAsStr = values.get(this.numberOfIterationsFieldName).toString();
        numOfIterations = Long.valueOf(longAsStr);
        System.out.println("numOfIterations:"+numberOfIterations);
      }
      hashService.setHashIterations(numOfIterations.intValue());
      if (!values.containsKey(this.saltFieldName)) {
        throw new IllegalArgumentException("Specified field name for salt does not exist in the results");
      }
      String dynaSalt = values.get(this.saltFieldName).toString();
      System.out.println("dynaSalt:"+dynaSalt);
      HashRequest request = new HashRequest.Builder().setSalt(dynaSalt).setSource(encodedPassword).build();
      return hashService.computeHash(request).toHex();
    }

}

执行以上的main方法对"pwd123"进行加密,静态盐值"pub-salt"动态盐值"cas-salt",Hash次数为2.
得到密文:c2b83f766426d6bde82f8c55bd7d1f99
创建语句并执行:

insert into cas_encode_user(user_account,user_pwd,user_name,user_email,user_tel,user_expired,user_disabled,user_hashtimes,user_salt) 
values ('Test01','c2b83f766426d6bde82f8c55bd7d1f99','User01','','',0,0,2,'cas-salt');
3.6.5.3 登录验证

1)运行tomcat启动cas.war
2)打开浏览器输入网址:https://localhost:8443/cas/

用户/密码期望结果实际结果
Test01/pwd123登录成功在这里插入图片描述
Test01/pwd12登录失败在这里插入图片描述
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值