Cas整合UcCenter数据库
CAS:为Web应用系统提供了单点登录服务。它的特性包括:一个开放和具有很好文档支持的协议;一个Java开源服务器组件;提供多种类型的客户端包括Java、.Net、PHP、Perl、Apache、uPortal等;能够与uPortal、BlueSocket、TikiWiki、Mule、 Liferay、Moodle集成使用。
Cas原理和协议:
从结构上来看cas包含两部分:cas server 和cas Client .casServer需要独立部署,主要负责对用户的认证工作,cas client负责对客户端受保护资源的访问请求。需要登录时,重定向到cas服务器
CAS Client 与受保护的客户端应用部署在一起,以 Filter 方式保护受保护的资源。对于访问受保护资源的每个 Web 请求,CAS Client 会分析该请求的 Http 请求中是否包含 Service Ticket,如果没有,则说明当前用户尚未登录,于是将请求重定向到指定好的 CAS Server 登录地址,并传递 Service (也就是要访问的目的资源地址),以便登录成功过后转回该地址。用户输入认证信息,如果登录成功,CAS Server 随机产生一个相当长度、唯一、不可伪造的 Service Ticket,并缓存以待将来验证,之后系统自动重定向到 Service 所在地址,并为客户端浏览器设置一个 Ticket Granted Cookie(TGC),CAS Client 在拿到 Service 和新产生的 Ticket 过后,与 CAS Server 进行身份核实,以确保 Service Ticket 的合法性。
在该协议中,所有与 CAS 的交互均采用 SSL 协议,确保,ST 和 TGC 的安全性。协议工作过程中会有 2 次重定向的过程,但是 CAS Client 与 CAS Server 之间进行 Ticket 验证的过程对于用户是透明的。
配置cas server:
1. 下载cas
http://www.jasig.org/cas_server_3_4_2_1
下载最新的cas_server_3_4_2_1.zip 服务器端,解压后找到cas-server-3.4.2.1-release\cas-server-3.4.2.1\modules\cas-server-webapp.war,将这个war进行解压到tomcat的webapps目录下.
将刚才解压出的目录cas-server-webapp进行重命名为cas
启动tomcat 输入http://localhost:8080/cas/login 查看是否成功部署
默认的认证方式是用户名和密码相同则认证成功。这个肯定是不能满足项目的需求的,所以需要和用户数据库配置起来
2.配置数据库
首先的将一些包导入进来,这里使用的是dbcp来配置数据库
将上述包拷贝到cas\WEB-INF\lib下
在 cas-server-support-jdbc-3.4.2.1.jar包中,提供了 3 个基于 JDBC 的 AuthenticationHandler,分别为BindModeSearchDatabaseAuthenticationHandler, QueryDatabaseAuthenticationHandler, SearchModeSearchDatabaseAuthenticationHandler
我们这里使用的是QueryDatabaseAuthenticationHandler,它是通过配置一个 SQL 语句查出密码
打开文件cas/WEB-INF/deployerConfigContext.xml,找到
<bean class="org.jasig.cas.authentication.handler.support.SimpleTestUsernamePasswordAuthenticationHandler" />
|
将它替换为
<bean class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler"> <property name="sql" value="select password from uc_members where lower(username)=lower(?)"/> <property name="dataSource"> <ref local="dataSource"/> </property> </bean>
|
并在最后加入
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/ucenter"/> <property name="username" value="root" /> <property name="password" value="123" /> <property name="maxActive" value="20"/> <property name="maxIdle" value="20"/> <property name="maxWait" value="20"/> </bean>
|
重启后输入用户名和密码登陆。这里整合的是discuz,discuz的密码进行了加密,我们还得附加加密功能。
package org.jasig.cas.authentication.handler.support;
import java.util.List;
import java.util.Map;
import org.jasig.cas.adaptors.jdbc.AbstractJdbcUsernamePasswordAuthenticationHandler;
import org.jasig.cas.authentication.handler.AuthenticationException;
import org.jasig.cas.authentication.handler.BadPasswordAuthenticationException;
import org.jasig.cas.authentication.handler.UnknownUsernameAuthenticationException;
import org.jasig.cas.authentication.principal.UsernamePasswordCredentials;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.rowset.SqlRowSet;
/**
* @className:JdbcUsernamePasswordAuthHandlerImpl.java
* @classDescription:
* @author:xiayingjie
* @createTime:2010-8-11
*/
public class JdbcUsernamePasswordAuthHandlerImpl extends AbstractJdbcUsernamePasswordAuthenticationHandler {
// it's better to move below properties to external configure file, for example 'maxFailureTimes'
private static final String QUERY_USER_SQL = "select salt,password from uc_members where username = ?";
/**
* authenticate username password internal
*
* @param credentials
* credentials
* @throws AuthenticationException
* AuthenticationException
* @return true if user login success
* @see org.jasig.cas.authentication.handler.support.AbstractUsernamePasswordAuthenticationHandler
* #authenticateUsernamePasswordInternal(rg.jasig.cas.authentication.principal.UsernamePasswordCredentials)
*/
@Override
protected boolean authenticateUsernamePasswordInternal(final UsernamePasswordCredentials credentials)
throws AuthenticationException {
final String username = credentials.getUsername();
final String password = credentials.getPassword();
JdbcTemplate template = new JdbcTemplate(getDataSource());
try {
Map mp= template.queryForMap(QUERY_USER_SQL, new String[]{username});
String salt=(String) mp.get("salt");
String userPassword=(String) mp.get("password");
String cPassword=MD5.getInstance().createMD5(MD5.getInstance().createMD5(password)+salt);
if(cPassword.equals(userPassword)){
return true;
} else{
return false;
}
} catch (final IncorrectResultSizeDataAccessException e) {
// this means the username was not found.
throw new UnknownUsernameAuthenticationException();
}
}
}
package org.jasig.cas.authentication.handler.support;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class MD5 {
private MessageDigest md;
private static MD5 md5;
private MD5()
{
try {
md=MessageDigest.getInstance("md5");
} catch (NoSuchAlgorithmException e) {
System.out.println("没有这种算法");
}
}
//产生一个MD5实例
public static MD5 getInstance()
{
if(null!=md5)
return md5;
else
{
makeInstance();
return md5;
}
}
//保证同一时间只有一个线程在使用MD5加密
private static synchronized void makeInstance()
{
if(null==md5)
md5=new MD5();
}
public String createMD5(String pass)
{
md.update(pass.getBytes());
byte[] b=md.digest();
return byteToHexString(b);
}
private String byteToHexString(byte[] b)
{
StringBuffer sb=new StringBuffer();
String temp="";
for(int i=0;i<b.length;i++)
{
temp=Integer.toHexString(b[i]&0Xff);
if(temp.length()==1)
temp="0"+temp;
sb.append(temp);
}
return sb.toString();
}
public static void main(String[] args) {
System.out.println(MD5.getInstance().createMD5((MD5.getInstance().createMD5("asdf")+"e61bb7")));
}
}
<property name="authenticationHandlers">
<list>
<bean class="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler"
p:httpClient-ref="httpClient" />
<bean class="org.jasig.cas.authentication.handler.support.JdbcUsernamePasswordAuthHandlerImpl">
<property name="dataSource" ref="dataSource" />
</bean>
</list>
</property>
如果要实现CAS的单点登入,这是必须的
1. 生产密钥
2. 点击’开始’ ’运行’,输入cmd
CD X:\jdk根目录\Java\jdk1.6.0_14\bin
CD X:
打开系统的hosts文件,我们定义2个解析
127.0.0.1 casserver
127.0.0.1 server1
下文中导入过程密码统一使用password
keytool -genkey -alias cas-server -keyalg RSA -keypass password -storepass password -keystore casserver.keystore
输入密码后,在第一个提示输入姓名(CN)的时候,输入你的CAS服务端域名或者hosts文件里定义的映射,如casserver
国家(C)输入CN
keytool -export -alias cas-server -storepass password -file casserver.cer -keystore casserver.keystore
keytool -import -trustcacerts -alias server -file casserver.cer -keystore “jre根目录/lib/security/cacerts “ -storepass changeit
(keytool -import -trustcacerts -alias server -file casserver.cer -keystore cacerts -storepass changeit )
注意,这个 changeit 是cacerts 文件(密钥库)的密码,不是刚才设置的密钥密码, changeit 为密钥库的默认密码
如果操作成功,会在jdk bin目录下看到casserver.cer casserver.keystore 这两个文件
如果项目中已经存在证书 则删除证书
keytool -delete -alias server -keystore "%JAVA_HOME%/jre/lib/security/cacerts" -storepass changeit
keytool -delete -alias server -keystore cacerts -storepass changeit
打开tomcat目录下conf/ server.xml 文件,找到
<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true" maxThreads="150" scheme="https" secure="true" clientAuth="false" sslProtocol="TLS" />
|
将注释去掉,修改为
<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true" maxHttpHeaderSize="8192" maxThreads="150" minSpareThreads="25" maxSpareThreads="75" enableLookups="false" disableUploadTimeout="true" acceptCount="100" scheme="https" secure="false" clientAuth="false" sslProtocol="TLS" keystoreFile=" casserver.keystore所在的目录\casserver.keystore" keystorePass="password" truststoreFile="jre目录/lib/security/cacerts" truststorePass="changeit" />
|
默认的密钥有效期是3个月,可以根据自己需求修改
访问https://casserver:8443/cas/login 测试是否部署成功
keytool 用法:
-certreq [-v] [-protected]
[-alias <别名>] [-sigalg <sigalg>]
[-file <csr_file>] [-keypass <密钥库口令>]
[-keystore <密钥库>] [-storepass <存储库口令>]
[-storetype <存储类型>] [-providername <名称>]
[-providerclass <提供方类名称> [-providerarg <参数>]]
[-providerpath <路径列表>]
-changealias [-v] [-protected] -alias <别名> -destalias <目标别名>
[-keypass <密钥库口令>]
[-keystore <密钥库>] [-storepass <存储库口令>]
[-storetype <存储类型>] [-providername <名称>]
[-providerclass <提供方类名称> [-providerarg <参数>]]
[-providerpath <路径列表>]
-delete [-v] [-protected] -alias <别名>
[-keystore <密钥库>] [-storepass <存储库口令>]
[-storetype <存储类型>] [-providername <名称>]
[-providerclass <提供方类名称> [-providerarg <参数>]]
[-providerpath <路径列表>]
-exportcert [-v] [-rfc] [-protected]
[-alias <别名>] [-file <认证文件>]
[-keystore <密钥库>] [-storepass <存储库口令>]
[-storetype <存储类型>] [-providername <名称>]
[-providerclass <提供方类名称> [-providerarg <参数>]]
[-providerpath <路径列表>]