本章节的内容为JDBC认证,查找数据库进行验证,其中包括:
- 密码加密策略(无密码,简单加密,加盐处理)
- 认证策略(jdbc)
业务需求
在不同公司,可能有很多业务需求或者架构不一样导致我们实现验证的方式不一样,那么cas为我们提供了很多认证的模式(当然也可以自定义),其中常用的有:
- JDBC认证
- LDAP认证
- Basic认证
- Shiro认证
- Pac4j认证
- MongoDB认证
- Rest认证
- IP黑白名单
还有可能交给第三方认证,例如:微信、QQ、新浪等等
当然也有一些公司或者企业也非常的变态,如:
- 认证中心不能直接访问账号库,cas也提供功能,可以考虑用REST认证模块来处理这个事情
- 老系统账号唯一性不确定,例如 组织+账号 才是唯一值,这时候只能自定义认证器(后面章节会有教程)
- 业务系统要求返回用户密码(多属性返回/ClearPass)
加密方案
cas支持jdbc校验方案:
- 根据sql给予用户名进行查询,根据密码字段进行鉴定(
select * from table_users where username=?
)可判断有效等 - 通过盐等手段进行编码加密再进行匹配(推荐)
- 根据sql给予用户名以及密码进行查询(
select count(x) from tables_users where username = ? and password=?
),不可判断有效期,若数量大于0则成功 - 根据用户名密码连接数据库,原理是通过jdbc,若连接成功则成功
JDBC验证
采用mysql数据库存储用户数据。
修改pom.xml
sso-server/pom.xml:
<!--新增支持jdbc验证-->
<dependency>
<groupId>org.apereo.cas</groupId>
<artifactId>cas-server-support-jdbc</artifactId>
<version>${cas.version}</version>
</dependency>
<!-- mysql驱动 根据不同的数据库使用不同驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.16</version>
</dependency>
<!--
若不想找驱动可以直接写下面的依赖即可,其中包括
HSQLDB、Oracle、MYSQL、PostgreSQL、MariaDB、Microsoft SQL Server
-->
<!--
<dependency>
<groupId>org.apereo.cas</groupId>
<artifactId>cas-server-support-jdbc-drivers</artifactId>
<version>${cas.version}</version>
</dependency>
-->
表结构
sys_user:
create table sys_user
(
id int not null auto_increment,
username varchar(40),
password varchar(200),
expired int comment '1为过期 需要修改密码',
disable int comment '1为不可用,禁用',
email varchar(200),
primary key (id)
);
CAS Authentication Credentials
根据sql给予用户名进行查询,根据密码字段进行鉴定(
select * from sys_user where username=?
)
程序新增配置
##
# CAS Authentication Credentials
#
#cas.authn.accept.users=casuser::Mellon
#Query Database Authentication 数据库查询校验用户名开始
#查询账号密码sql,必须包含密码字段
cas.authn.jdbc.query[0].sql=select * from sys_user where username=?
#指定上面的sql查询字段名(必须)
cas.authn.jdbc.query[0].fieldPassword=password
#指定过期字段,1为过期,若过期不可用
cas.authn.jdbc.query[0].fieldExpired=expired
#为不可用字段段,1为不可用,需要修改密码
cas.authn.jdbc.query[0].fieldDisabled=disabled
#数据库方言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/sso?characterEncoding=utf8
#数据库用户名
cas.authn.jdbc.query[0].user=root
#数据库密码
cas.authn.jdbc.query[0].password=123456
#Query Database Authentication 数据库查询校验用户名结束
以上配置,如驱动,查询数据库等等需要根据不同的场景进行调整
常用单向加密算法:MD5、SHA、HMAC。
一般我们常用的加密算法就这几种。在JDBC认证中我们也可以选择配置加密算法,加密算法一般为上面的三种,MD5、SHA、HMAC,加密类型为NONE|DEFAULT|STANDARD|BCRYPT|SCRYPT|PBKDF2
这几种,我们在配置文件中选择加密类型,指定加密算法。
普通MD5加密配置
前面配置不变指定JDBC配置,后面的配置为密码加密策略,配置如下:
#默认加密策略,通过encodingAlgorithm来指定算法,默认NONE不加密
cas.authn.jdbc.query[0].passwordEncoder.type=DEFAULT
cas.authn.jdbc.query[0].passwordEncoder.characterEncoding=UTF-8
cas.authn.jdbc.query[0].passwordEncoder.encodingAlgorithm=MD5
执行流程(如输入账号密码为 admin/123):
登陆成功:
然后我们启动应用。当我们运行起CAS,输入原来的用户名和密码admin/123456
,并不能登录,因为我们更改密码验证为MD5加密模式了。将123456
使用md5加密后,填入数据库,再次登录,可以发现登录成功。由此验证我们加密成功!
对密码进行盐值处理(推荐)官方实例
上一步只是对密码进行了简单的加密,两个帐号有可能相同的值,就能判断出密码是一致的,但通过此方案,大大增加了难度,所以安全系数也高了许多,推荐使用策略。
在上面的基础上,再添加如下代码,可以共存,:
#加密迭代次数
cas.authn.jdbc.encode[0].numberOfIterations=2
#该列名的值可替代上面的值,但对密码加密时必须取该值进行处理
cas.authn.jdbc.encode[0].numberOfIterationsFieldName=
#盐值固定列
cas.authn.jdbc.encode[0].saltFieldName=username
#静态盐值
cas.authn.jdbc.encode[0].staticSalt=.
cas.authn.jdbc.encode[0].sql=SELECT * FROM sys_user WHERE username =?
#对处理盐值后的算法
cas.authn.jdbc.encode[0].algorithmName=MD5
cas.authn.jdbc.encode[0].passwordFieldName=password
cas.authn.jdbc.encode[0].expiredFieldName=expired
cas.authn.jdbc.encode[0].disabledFieldName=disabled
#数据库连接
cas.authn.jdbc.encode[0].url=jdbc:mysql://127.0.0.1:3306/sso?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false
cas.authn.jdbc.encode[0].dialect=org.hibernate.dialect.MySQL5Dialect
cas.authn.jdbc.encode[0].driverClass=com.mysql.jdbc.Driver
cas.authn.jdbc.encode[0].user=root
cas.authn.jdbc.encode[0].password=123456
加密生成类:
//固定盐生成类
public class PasswordSaltTest {
private String staticSalt = ".";
private String algorithmName = "MD5";
private String encodedPassword = "123456";
private String dynaSalt = "test";
@Test
public void test() throws Exception {
ConfigurableHashService hashService = new DefaultHashService();
hashService.setPrivateSalt(ByteSource.Util.bytes(this.staticSalt));
hashService.setHashAlgorithmName(this.algorithmName);
hashService.setHashIterations(2);
HashRequest request = new HashRequest.Builder()
.setSalt(dynaSalt)
.setSource(encodedPassword)
.build();
String res = hashService.computeHash(request).toHex();
System.out.println(res);
}
}
测试结果
使用test/123456登录成功(加盐密码可用)
说明:
除此之外如果我们要自定义加密类型,就需要实现org.springframework.security.crypto.password.PasswordEncoder
接口,并且把类名配置在passwordEncoder.type
。
这种方式我们在这里就不做尝试了,接下来我们结合SpringSecurity提供的加密方法,完成自定义认证校验策略(推荐使用
)