本教程使用的SSO服务器是Yelu大学研发的CAS(Central Authentication Server), 官网:http://www.jasig.org/cas 下载地址:http://downloads.jasig.org/ 文档地址:http://jasig.github.io/cas/4.0.x/index.html 本文修改的cas-server项目代码我已经放到了github上 https://github.com/pkaq/cas-server-3.5.2
环境配置 JDK:1.7.45 CAS版本:4.0.0
目录
环境准备 ___A. 证书生成 ______1. 导出证书 ______2. 导入服务端信任证书 ______3. 颁发给客户机 ___B. WEB服务器配置 ______1. tomcat配置 ______2. jboss配置 ______3. webloigc配置 ______4. websphere配置
服务端搭建
客户端搭建 ___a. JAVA客户端应用搭建
进阶应用 ___a. 配置自定义校验规则 ___b. 配置自定义认证返回参数 ___c. 配置自定义界面
用JDK自带的keytool工具生成证书:
1 keytool -genkey -alias wsria -keyalg RSA -keystore /home/mykey
参数说明如图:
p.s: 1.也可以申请免费的startssl证书,具体申请过程请移步https://www.startssl.com/?app=11&action=regform 当然国内也有一家wosign可以申请,申请速度比startssl要快一些,而且在线客服态度很好可以提供很好的帮助www.wosign.com
2.具体的输入项图片中都有说明,有一点我要解释一下;在输入完密码后提示输入域名是我输入的是sso.wsria.com,其实这个域名是不存在的,但是我为了演示所以虚拟了这个域名,技巧在于修改
C:\Windows\System32\drivers\etc\hosts 添加内容如下:
127.0.0.1 sso.wsria.com 这样在访问sso.wsria.com的时候其实是访问的127.0.0.1也就是本机
1 keytool -export -file /home/my.crt -alias wsria -keystore /home/mykey
1 keytool -import -trustcacerts -alias [keyEntry_name] -file mycert.crt -keystore [keystore_name]
1 keytool -import -file /opt/name.cer -keystore $JAVA_HOME/jre7/lib/security/cacerts -alias server
这里默认密码是changeit
首先要启用Web服务器(Tomcat)的SSL,也就是HTTPS加密协议; 打开tomcat目录的conf/server.xml文件,找到如下行并设置keystoreFile、keystorePass修改结果如下:
1 2 3 <Connector port ="8443" protocol ="org.apache.coyote.http11.Http11Protocol" SSLEnabled ="true" maxThreads ="150" scheme ="https" secure ="true" clientAuth ="false" sslProtocol ="TLS" keystoreFile ="/home/mykey" keystorePass ="xxxx" />
参数说明: keystoreFile:在第一步创建的key存放位置 keystorePass:创建证书时的密码
p.s 按同样的方法来配置Tomcat 7却启动不起来,报如下错误:
严重: Failed to initialize end point associated with ProtocolHandler [“http-apr-8443”] java.lang.Exception: Connector attribute SSLCertificateFile must be defined when using SSL with APR
仔细看上面的异常信息发现这是APR报的错误。Tomcat 6也有APR包但我从来都没用过。为此查看了Tomcat的ssl-how,在“Edit the Tomcat Configuration File”一节中说到: Tomcat提供了两个SSL实现,一个是JSSE实现,另一个是APR实现。 Tomcat将自动选择使用哪个实现,即如果安装了APR则自动选择APR,否则选择JSSE。 如果不希望让Tomcat自动选择,而是我们自己指定一个实现则可通过protocol定义,如下:
我又查看了6.0的相同说明,里面与7.0的说明一模一样。因此问题只可能是:是否安装了APR包。
以前只听说过APR但没弄过。APR是什么文件?后来才发现APR文件名为tcnative-1.dll。进一步检查6.0和7.0的安装目录,结果发现6.0里没这个dll文件,而7.0里有。换句话说,6.0默认使用JSSE实现,而7.0默认使用APR实现。
弄明白缘由就好办了。由于习惯使用6.0的配置方式(即JSEE实现),因此只要把上面conf\server.xml里的protocol修改一下就行了:
<Connector port="8443" protocol="org.apache.coyote.http11.Http11Protocol" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS"
keystoreFile="${user.home}/.keystore"
keystorePass="changeit" />
重新启动,一切正常。
1.SSL配置 找到standalone->configuration->standalone.xml 修改 节点为如下内容
1 2 3 4 5 6 <connector name ="https" protocol ="HTTP/1.1" scheme ="https" socket-binding ="https" secure ="true" enabled ="true" > <ssl name ="https" key-alias ="1" password ="Masterfrank099" certificate-key-file ="D:/key/domainname.jks" protocol ="all" verify-client ="false" /> </connector >
key-alias:证书别名 password:证书密码 certificate-key-file:证书放位置
p.s:JBOSS 4.2以上版本服务启动如果不加任何参数的话,只监听127.0.0.1,就是说只能用127.0.0.1或者localhost访问,用本机的对外地址是访问不了,同一网络内别的机子无法访问。
解决方案:打开jboss7/standalone/configuration/standalone.xml,找到如下代码
1 2 3 <interface name ="public" > <inet-address value ="${jboss.bind.address:127.0.0.1}" /> </interface >
将127.0.0.1改为0.0.0.0即可解决jboss无法使用IP地址访问的问题。
2.cas部署 1、将war包放到jboss7/standalone/ deployments目录下,启动jboss,如可正常访问,说明部署成功。 2、 若直接将car.war部署到jboss中,在启动时就会报错,引起错误的原因:
由于jboss自带有log4j日志,这就会引起jboss的log4j与cas的log4j发生冲突,将cas中引入log4j.xml的代码删除即可。
未指定hibernate的方言,打开cas.war\WEB-INF\classes\META-INF\ persistence.xml,将如下代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 <properties > <property name ="hibernate.dialect" value ="org.hibernate.dialect.HSQLDialect" /> </properties > //放到 <persistence-unit name ="CasPersistence" transaction-type ="RESOURCE_LOCAL" > 下。``` ##### <a name ="weblogic" > c.WebLogic</a > Weblogic默认端口为7001 请移步:[\[weblogic 12c安装\] - ssl配置/cas部署](\2015\01\26\installweblogic\index.html) p.s: 1.官方提供的zip压缩版经测试无法正常访问ssl,可能少东西 2.打成war包部署会报log4j.xml找不到错误,直接以目录形式部署没问题,出现该问题的原因应该是加载冲突所致,可考虑用serverlet方式加载spring的log4j listener #####<a name ="websphere" > d.WebSphere</a > Weblogic默认端口为9080 请移步:[\[websphere 8.5.5安装\] - ssl配置/cas部署](\2015\01\27\websphereinstall\index.html) ## <a name ="server" > 二、CAS服务端搭建</a > 1.CAS服务端下载:http://www.jasig.org/cas/download 2.解压cas-server-xxx/modules/cas-server-webapp-xxx.war,改名为cas,然后复制cas目录到你的tomcat/webapp目录下 3.访问http://localhost:8080/cas即可看到项目首页,输入casuser/Mellon点击登录(这是4.0.x之后版本的用户名密码,3.5.x版本CAS默认的验证规则只要用户名和密码相同就通过) 所以如果你看到下面的这张图片你就成功了 ![登陆成功](/images/2015/01/cas-login-success.gif) ## <a name ="client" > 三、CAS客户端</a > ### <a name ="withjava" > A.Java客户端集成</a > 1.在客户端添加 "org.jasig.cas.client:cas-client-core:+",依赖 2.配置客户端web.xml文件 ```xml <listener > <listener-class > org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class > </listener > <filter > <filter-name > CAS Single Sign Out Filter</filter-name > <filter-class > org.jasig.cas.client.session.SingleSignOutFilter</filter-class > </filter > <filter-mapping > <filter-name > CAS Single Sign Out Filter</filter-name > <url-pattern > /*</url-pattern > </filter-mapping > <filter > <filter-name > CASFilter</filter-name > <filter-class > org.jasig.cas.client.authentication.AuthenticationFilter</filter-class > <init-param > <param-name > casServerLoginUrl</param-name > <param-value > https://www.sifenggu.com:8443/cas/login</param-value > </init-param > <init-param > <param-name > serverName</param-name > <param-value > http://localhost:9090</param-value > </init-param > </filter > <filter-mapping > <filter-name > CASFilter</filter-name > <url-pattern > /*</url-pattern > </filter-mapping > <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 > https://www.sifenggu.com:8443/cas</param-value > </init-param > <init-param > <param-name > serverName</param-name > <param-value > http://localhost:9090</param-value > </init-param > </filter > <filter-mapping > <filter-name > CAS Validation Filter</filter-name > <url-pattern > /*</url-pattern > </filter-mapping > <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 > /*</url-pattern > </filter-mapping > <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 > /*</url-pattern > </filter-mapping > <filter > <display-name > AutoSetUserAdapterFilter</display-name > <filter-name > AutoSetUserAdapterFilter</filter-name > <filter-class > demo.frank.wu.sso.cas.AutoSetUserAdapterFilter</filter-class > </filter > <filter-mapping > <filter-name > AutoSetUserAdapterFilter</filter-name > <url-pattern > /*</url-pattern > </filter-mapping > ``` ## <a name ="adv" > 四、进阶应用</a > ### <a name ="adv_1" > A.配置自定义校验规则</a > 打开cas-server-webapp项目,找到src/main/webapp/WEB-INF/deployerConfigContext.xml,将primaryAuthenticationHandler这个bean注释掉,用如下bean替代 ```xml <bean id ="primaryAuthenticationHandler" class ="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler" > <property name = "dataSource" ref ="dataSource" /> <property name = "sql" value ="select upper(password) from itsm_user where loginname= ?" /> <property name = "passwordEncoder" ref ="MD5PasswordEncoder" /> </bean > ``` 在其它位置配置数据源以及自定义加密的bean ```xml <bean id ="dataSource" class ="com.alibaba.druid.pool.DruidDataSource" > <property name ="username" value ="xxx" /> <property name ="password" value ="xxx" /> <property name ="url" value ="jdbc:oracle:thin:@xxx.xxx.xxx.xx:1521:xxx" /> <property name ="initialSize" value ="10" /> <property name ="minIdle" value ="10" /> <property name ="maxActive" value ="120" /> </bean > <bean id ="MD5PasswordEncoder" class ="org.jasig.cas.util.MD5Encoder" />
关于自定义密码加密的MD5Encoder类,只要自己写一个类实现org.jasig.cas.authentication.handler.PasswordEncoder接口中的 public String encode(String password) 方法即可,该方法的返回值是加密处理后的密码。 当然如果你用MD5加密可以采用内置的MD5PasswordEncoder;
修改完成后重新打包cas-server-webapp项目 P.S 1:连接数据库需要增加对cas-server-support-jdbc项目和数据源以及数据库驱动的依赖,关于如何向pom添加依赖,请自补;当然最好给根POM配置一下国内的maven库(例如开源中国的),否则不仅会很慢,很多依赖也无法下载。
2:由于Oracle的原因 maven库中并未提供oracle驱动,而maven又不像gradle那样可以简易友好的加载本地依赖,所以需要你找到oracle驱动并且 注 册 到 本 地 仓 库 mvn install:install-file -Dfile={Path/to/your/ojdbc.jar} -DgroupId=com.oracle -DartifactId=ojdbc6 -Dversion=11.2.0 -Dpackaging=jar
部署到tomcat之后便可以通过数据库中的正式用户进行登陆了.如果页面提示”您提供的凭证有误”多半为数据源配置或密码不匹配所致。建议检查数据源是否配置正确以及encoder加密返回的密码与数据库存储的加密密码(区分大小写)是否一致。
修改完成后采用mavenpackge命令或者直接再eclipse中执行maven install进行构建即可.
默认情况下cas认证完成后只会返回username给客户端,如果需要返回更多信息需要修改deployerConfigContext.xml文件,同时配置attributeRepository如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 <bean id ="attributeRepository" class ="org.jasig.services.persondir.support.jdbc.SingleRowJdbcPersonAttributeDao" > <constructor-arg index ="0" ref ="dataSource" /> <constructor-arg index ="1" value ="select username,loginname from ITSM_USER where {0}" /> <property name ="queryAttributeMapping" > <map > <entry key ="username" value ="loginname" /> </map > </property > <property name ="resultAttributeMapping" > <map > <entry key ="loginname" value ="loginname" /> <entry key ="username" value ="username" /> </map > </property > </bean > <bean id ="serviceRegistryDao" class ="org.jasig.cas.services.InMemoryServiceRegistryDaoImpl" > <property name ="registeredServices" > <list > <bean class ="org.jasig.cas.services.RegexRegisteredService" > <property name ="id" value ="0" /> <property name ="name" value ="HTTP and IMAP" /> <property name ="description" value ="Allows HTTP(S) and IMAP(S) protocols" /> <property name ="serviceId" value ="^(https?|imaps?)://.*" /> <property name ="evaluationOrder" value ="10000001" /> <property name ="allowedAttributes" > <list > <value > username</value > <value > loginname</value > </list > </property > </bean > </list > </property > </bean >
找到casServiceValidationSuccess.jsp。此文件作用是在server验证成功后,这个页面负责生成与客户端交互的xml信息,在默认的casServiceValidationSuccess.jsp中,只包括用户登录名,并不提供其他的属性信息,因此需要对页面进行扩展。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <%@ page session="false" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %> <cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas' > <cas:authenticationSuccess> <cas:user>${fn:escapeXml(assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1 ].principal.id)}</cas:user> <c:if test="${not empty pgtIou}" > <cas:proxyGrantingTicket>${pgtIou}</cas:proxyGrantingTicket> </c:if > <c:if test="${fn:length(assertion.chainedAuthentications) > 1}" > <cas:proxies> <c:forEach var ="proxy" items="${assertion.chainedAuthentications}" varStatus="loopStatus" begin="0" end="${fn:length(assertion.chainedAuthentications)-2}" step="1" > <cas:proxy>${fn:escapeXml(proxy.principal.id)}</cas:proxy> </c:forEach> </cas:proxies> </c:if > <!-- 在server验证成功后,这个页面负责生成与客户端交互的xml信息,在默认的casServiceValidationSuccess.jsp中,只包括用户名,并不提供其他的属性信息,因此需要对页面进行扩展 --> <c:if test="${fn:length(assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.attributes) > 0}" > <cas:attributes> <c:forEach var ="attr" items="${assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.attributes}" > <cas:${fn:escapeXml(attr.key)}>${fn:escapeXml(attr.value)}</cas:${fn:escapeXml(attr.key)}> </c:forEach> </cas:attributes> </c:if > </cas:authenticationSuccess> </cas:serviceResponse>
客户端获取用户信息
1 2 3 4 5 AttributePrincipal principal = (AttributePrincipal) request.getUserPrincipal();String loginName = principal.getName();System.out.println("loginName:" + loginName); Map<String, Object> attributes = principal.getAttributes(); System.out.println("username:" + attributes.get("username" ));
1.cas统一认证的登陆页面位于:cas目录/WEB-INF/view/jsp/default 文件夹里,其中ui/casLoginView.jsp为登陆页面
2.首先复制一份default文件夹 重命名为myview
3.然后复制classes/default.properties 到 classes/mytheme.properties 打开mytheme.properties 修改登陆页面的路径为我们复制的myview 文件夹。
1 2 3 casLoginView.url =/WEB-INF/view/jsp/myview/ui/casLoginView.jsp
4 修改 cas目录/cas.properties 中 cas.viewResolver.basename =mytheme
到这一步我们只是将登陆页面拷贝了一份然后指向这份拷贝,接下来我们就可以随意修改我们拷贝的页面,这样做的目的是如果以后想还原回来比较方便,只需要修改引用就行。
接下来我们修改casLoginView.jsp页面:
修改的要求是:需要保留登陆form表单(但可以修改样式,虽然表单标签是带前缀的,但和普通html标签一样修改其样式,也可以将所有的信息输出删除 如<spring:message code=”screen.welcome.label.netid.accesskey” var=”userNameAccessKey” />)但必须保留保单中的用户名、密码输入框,确认按钮,而且最好复制其标签,修改其class属性来修改样式.
__参考资料 *Jboss as 7 wiki - Installing cas-web *WebLogic上CAS服务器搭建 *WebSphere上搭建CAS Server *CAS实现单点登陆的原理 *CAS单点登陆(SSO)完整教程 *修改cas登陆页面-服务器端 *单点登录 - CAS【四】获取更全面的用户信息