与别的公司对接开发单点系统的客户端,领导讲最好我们也开发一套单点登录系统,于是自己研究了几天,也使用CAS开发了一套单点当了系统,记录步骤如下,以备以后参考:
一、服务端
1、下载cas,并配置成完整项目
下载地址:https://www.apereo.org/cas/download
在Download CAS Server栏目下点击对应版本的zip即可下载,我下载的是cas-server-3.4.8
解压文件将modules下的cas-server-webapp-3.4.8.war为cas.war,拷贝到tomcat6下运行后,得到cas项目
把cas-server-webapp\src\main\java的类文件下拷贝到cas项目src里,然后把cas项目下的class文件里除了编译过的类以外全部拷贝到src里,这样就得到了cas整个项目。
2、验证用户及密码
把cas-server-support-jdbc-3.4.8.jar和ojdbc14.jar拷贝到lib下
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.jasig.cas.authentication.principal.UsernamePasswordCredentials;
public final class CasDaoAuthenticationHandler extends AbstractUsernamePasswordAuthenticationHandler
{
public CasDaoAuthenticationHandler()
{
}
public boolean authenticateUsernamePasswordInternal(UsernamePasswordCredentials credentials)
{
boolean bool = false;
String username = credentials.getUsername();
String password = credentials.getPassword();
System.out.println("开始CAS认证......");
System.out.println("userName:"+username);
System.out.println("password:"+password);
String driver = "oracle.jdbc.driver.OracleDriver";
String conn_str_1 = "jdbc:oracle:thin:@127.0.0.1:1521:orcl";
Connection conn =null;
try {
Class.forName(driver);
conn = DriverManager.getConnection(conn_str_1,"uums","pass");
String sql = "SELECT password FROM SYS_USER WHERE LOGINNAME='"+username+"' ";//AND password='"+password+"'
ResultSet res = conn.prepareStatement(sql).executeQuery();
while(res.next()){
String psword = res.getObject(1).toString();
if(psword != null && (!"".equals(psword))){
if(password.equals(DecodeUtil.Decrypt(psword))){
System.out.println("认证成功!");
bool = true;
break;
}
}
}
}
catch (ClassNotFoundException e) {
e.printStackTrace();
}
catch (SQLException e) {
e.printStackTrace();
}
catch (Exception e) {
e.printStackTrace();
}
finally{
try{
if(conn!=null)
conn.close();
}catch(Exception e){
e.printStackTrace();
}
}
if(!bool){
System.out.println("认证不成功!");
}
return bool;
}
protected void afterPropertiesSetInternal()
throws Exception
{
// super.afterPropertiesSetInternal();
}
}
3、其他配置
a.在\WEB-INF\deployerConfigContext.xml 找到 <bean class="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler"
p:httpClient-ref="httpClient" p:requireSecure="false"/>
参数p:requireSecure="false",是否需要安全验证,即HTTPS,false为不采用。
b.在/spring-configuration/ticketGrantingTicketCookieGenerator.xml和warnCookieGenerator.xml 找到
<bean id="ticketGrantingTicketCookieGenerator" class="org.jasig.cas.web.support.CookieRetrievingCookieGenerator"
p:cookieSecure="false"
p:cookieMaxAge="-1"
p:cookieName="CASTGC"
p:cookiePath="/cas" />
p:cookieSecure 为true 为采用https,false 为http,p:cookieSecure="true" 表示只能https请求才能读到cookie中的值,而http访问时不能从cookie中取的值.
p:cookieMaxAge 为-1 设置浏览器cookie保留时间 -1为浏览器关闭后失效,简单说是COOKIE的最大生命周期,-1为无生命周期,即只在当前打开的IE窗口有效,IE关闭或重新打开其它窗口,仍会要求验证。
4、为了信息传输将更加安全可以给tomcat配置https,SSL认证分双向认证和单向认证,如果为双向认证则客户端也需要安装已生成好的文件
1).双向认证配置步骤
第一次配置成功后,第二次在配置,始终不成功,中间没有出现错误,但是项目不能访问,还不知道什么原因,怀疑是证书冲突造成的,待以后查原因。
a.、生成服务器端证书
keytool -genkey -keyalg RSA -dname "cn=localhost,ou=sango,o=none,l=china,st=beijing,c=cn" -alias server -keypass password -keystore d:\h
ttps\server.jks -storepass password -validity 3650
说明:
keytool :是个密钥和证书管理工具,
-genkey:命令向某个尚不存在的密钥仓库添加数据时,就创建了一个密钥仓库
-keyalg :指定了用于生成密钥对的算法
RSA:公钥加密算法
-dname:指定证书拥有者信息,cn为要访问的域名或ip地址
“d:\https\server.jks”:含义是将证书文件的保存路径,证书文件名称是server.jks
“-validity 3650”:含义是证书有效期,3650表示10年,默认值是90天 “
-keypass:设置密码为password
b、生产客户端证书
keytool -genkey -keyalg RSA -dname "cn=sango,ou=sango,o=none,l=china,st=beijing,c=cn" -alias custom -storetype PKCS12 -keypass password -keystore d:\https\custom.p12 -storepass password -validity 3650
说明:
客户端的CN可以是任意值
-alias:产生别名,每个keystore都关联这一个独一无二的alias,这个alias通常不区分大小写
-storepass 指定密钥库的密码(获取keystore信息所需的密码)
c、由于是双向SSL认证,服务器必须要信任客户端证书,因此,必须把客户端证书添加为服务器的信任认证。由于不能直接将PKCS12格式的证书 库导入,我们必须先把客户端证书导出为一个单独的CER文件,使用如下命令,先把客户端证书导出为一个单独的cer文件:
keytool -export -alias custom -file d:\https\custom.cer -keystore d:\https\custom.p12 -storepass password -storetype PKCS12 -rfc
然后,添加客户端证书到服务器中(将已签名数字证书导入密钥库)
keytool -import -v -alias custom -file d:\https\custom.cer -keystore d:\https\server.jks -storepass password
d、查看证书
keytool -list -v -keystore d:\https\server.jks -storepass password
e、配置tomcat service.xml文件
<Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
clientAuth="true" sslProtocol="TLS"
keystoreFile="D:\https\tomcat.keystore" keystorePass="password"
truststoreFile="D:\https\tomcat.keystore" truststorePass="password" />
说明:
protocol="org.apache.coyote.http11.Http11NioProtocol" :这样配置提供tomcat的响应速度
2)tomcat6配置单向认证 (未验证)
a、keytool -genkey -keyalg RSA -dname "cn=localhost,ou=sango,o=none,l=china,st=beijing,c=cn"
-alias server -keypass password -keystore d:\server.jks -storepass password -validity 3650
2、由于是单向认证,没有必要生成客户端的证书,直接进入配置tomcat service.xml文件,Xml代码
<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS"
keystoreFile="D:/server.jks" keystorePass="password"/>
clientAuth="false"表示单向认证,同时去掉truststoreFile="D:/server.jks" truststorePass="password"这2个属性
二、客户端
1、在http://downloads.jasig.org/cas-clients/,下载客户端,加压后把cas-client-core-3.2.1.jar放到项目的lib下
2、编写过滤器类
a.ClientFilter
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.jasig.cas.client.authentication.AttributePrincipal;
public class ClientFilter implements Filter {
String serviceUrl;
String appName;
String setSessionClass;
public void destroy() {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
AttributePrincipal principal = (AttributePrincipal) ((HttpServletRequest) request).getUserPrincipal();
HttpSession session = ((HttpServletRequest) request).getSession();
String userAccount = null;
if (session.getAttribute("cas_username") == null && principal != null && principal.getName() != null ) {
//具体项目有不同的情形:
//1、所有的应用系统账号一致
//2、所有的应用系统账号不一致
/*1、若ezIDS的用户账号与第三方应用系统账号一致*/
userAccount = principal.getName();
/*若ezIDS的用户账号与第三方应用系统账号一致*/
if(userAccount == null) {
session.invalidate();
}else{
if(setSessionClass != null){
try {
SetLoginSession sls = (SetLoginSession) Class.forName(setSessionClass).newInstance();
sls.setSession(userAccount, (HttpServletRequest) request);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
session.setAttribute("cas_username", principal.getName());
}
}
chain.doFilter(request, response);
}
public void init(FilterConfig arg0) throws ServletException {
setSessionClass = arg0.getInitParameter("setSessionClass");
}
}
b.SetLoginSession
import javax.servlet.http.HttpServletRequest;
public interface SetLoginSession {
public void setSession(String username, HttpServletRequest request);
public boolean isLogined(HttpServletRequest request);
}
c.YcyySetLoginSession
import java.util.Locale;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import modules.sys.Sys_user;
import modules.sys.Sys_userCtl;
public class YcyySetLoginSession implements SetLoginSession {
public void setSession(String username, HttpServletRequest request) {
request.getSession().setAttribute("org.apache.struts.action.LOCALE", Locale.CHINA);
HttpSession session = request.getSession();
Sys_user user = new Sys_userCtl().logon(username,request.getRemoteAddr());
session.setAttribute("userSession", user);
}
public boolean isLogined(HttpServletRequest request) {
return request.getSession().getAttribute("userSession") != null;
}
}
3、配置web.xml
<!--单点登录相关配置开始-->
<!-- 用户的身份认证 -->
<filter>
<filter-name>CASFilter</filter-name>
<filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>
<init-param>
<param-name>casServerLoginUrl</param-name> <!-- 59.203.198.2:7080 192.168.0.149:8080-->
<param-value>http://192.168.0.149:8080/cas/login</param-value>
</init-param>
<init-param>
<param-name>renew</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>gateway</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>serverName</param-name>
<param-value>http://192.168.0.149:8080</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CASFilter</filter-name>
<url-pattern>/private/index.xp</url-pattern>
</filter-mapping>
<!-- 用户的身份认证 -->
<!-- 负责Ticket的校验 -->
<filter>
<filter-name>CAS Validation Filter</filter-name>
<filter-class>org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter</filter-class>
<init-param>
<param-name>casServerUrlPrefix</param-name>
<param-value>http://192.168.0.149:8080/cas</param-value>
</init-param>
<init-param>
<param-name>serverName</param-name>
<param-value>http://192.168.0.149:8080</param-value>
</init-param>
<init-param>
<param-name>useSession</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>redirectAfterValidation</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CAS Validation Filter</filter-name>
<url-pattern>/private/index.xp</url-pattern>
</filter-mapping>
<!-- 负责Ticket的校验 -->
<!--该过滤器负责实现HttpServletRequest请求的包裹,比如允许开发者通过HttpServletRequest的getRemoteUser()方法获得SSO登录用户的登录名-->
<filter>
<filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
<filter-class>org.jasig.cas.client.util.HttpServletRequestWrapperFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
<url-pattern>/private/index.xp</url-pattern>
</filter-mapping>
<!--该过滤器负责实现HttpServletRequest请求的包裹,比如允许开发者通过HttpServletRequest的getRemoteUser()方法获得SSO登录用户的登录名-->
<!--该过滤器使得开发者可以通过org.jasig.cas.client.util.AssertionHolder来获取用户的登录名。比如AssertionHolder.getAssertion().getPrincipal().getName()-->
<filter>
<filter-name>CAS Assertion Thread Local Filter</filter-name>
<filter-class>org.jasig.cas.client.util.AssertionThreadLocalFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CAS Assertion Thread Local Filter</filter-name>
<url-pattern>/private/index.xp</url-pattern>
</filter-mapping>
<!--该过滤器使得开发者可以通过org.jasig.cas.client.util.AssertionHolder来获取用户的登录名。比如AssertionHolder.getAssertion().getPrincipal().getName()-->
<!--YCYY设置session使用的过滤器-->
<filter>
<filter-name>setnameFilter</filter-name>
<filter-class>modules.com.casFilter.ClientFilter</filter-class>
<init-param>
<param-name>setSessionClass</param-name>
<param-value>modules.com.casFilter.YcyySetLoginSession</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>setnameFilter</filter-name>
<url-pattern>/private/index.xp</url-pattern>
</filter-mapping>
<!--YCYY设置session使用的过滤器-->
<!--单点登录相关配置结束-->
</web-app>
三、遇到的问题
1、在请求服务器端时,会很慢,那是因为在top.jsp加了引用到https://ajax.googleapis.com网站的jquery,但是该网站不能进行访问了,所以会很慢,去掉后就可以了。