记录一下cas sso的开发过程

目录

目录... 1

开发环境:... 1

CAS支持的两种协议访问方式:... 3

方式一:http协议... 3

方式二:https协议:... 4

1:win+R 快捷键打开DOS命令窗口... 4

2:输入以下命令,生成证书... 4

3:导出证书... 6

4:JDK导入证书... 8

部署CAS-Server相关的Tomcat 11

1.配置HTTPS. 11

2.  验证HTTPS配置... 11

3 .部署CAS-Server 12

客户端和cas server的交互... 15

1.部署CAS客户端相关的Tomcat 15

2.在客户端的web.xml中添加配置信息... 16

单点登录整合... 20

1.新建一个cas.jsp文件有如下内容... 20

2后台代码修改... 22

可能遇到的问题总结:... 29

1.question_ONE. 29

2.question_TWO.. 29

3.question_THREE. 30

 

开发环境:

windows7 64

jdk1.7.0_17

apache-tomcat-7.0.78-windows-x64

cas-server-webapp-4.0.0.warcas-client-core-3.2.1.jarcommons-logging.jar

确保本地jdk环境已经搭建好

War包及安装包下载地址:https://pan.baidu.com/s/1hrY4BBm

根据演示需求,用修改hosts 文件的方法添加域名最简单方便(这个非常重要,因为CAS单点登录系统是基于JAVA安全证书的 https 访问, 要使用CAS单点登录必须要配置域名, cas是不能通过ip访问的.),编辑文件C:\Windows\System32\drivers\etc\hosts 在文件末端添加以下信息:

127.0.0.1      server.ywj123.com

截图如下:

注意:这个就是即将用来配置在自己电脑上的第三方单点登录的服务器:cas server

#     127.0.0.1      client1.ywj123.com

#     127.0.0.1      client2.ywj123.com

这两个客户端是我刚开始测试时配置的,真实项目里都是直接放在本机的Tomcat\webapps下面。

上面3ip都是127.0.0.1,这是为什么呢?因为我的环境都是在同一台机器,所以ip都是一致的,我们再把不同的服务端和客户端应用,使用不同域名加以区分。一个域名对应一个应用,模拟多端!

CAS支持的两种协议访问方式:

方式一:http协议

 WEB-INF/deployerConfigContext.xml

    

    bean class "org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler"     p:httpClient-ref "httpClient" />

增加参数 p:requireSecure="false" ,是否需要安全验证,即 HTTPS  false 为不采用 如下:

bean class "org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler" p:httpClient-ref "httpClient" p:requireSecure= "false" />

      

 WEB-INF/spring-configuration/ticketGrantingTicketCookieGenerator.xml 
         
修改 p:cookieSecure="true" 
 p:cookieSecure=" false  即不需要安全 cookie

如下部分:

    bean id "ticketGrantingTicketCookieGenerator" class "org.jasig.cas.web.support.CookieRetrievingCookieGenerator"

       p:cookieSecure false "

       p:cookieMaxAge "-1"

       p:cookieName "CASTGC"

       p:cookiePath "/cas" />

 

 WEB-INF\spring-configuration\warnCookieGenerator.xml

修改 p:cookieSecure="true"  p:cookieSecure=" false  即不需要安全 cookie

结果如下:

    bean id "warnCookieGenerator" class "org.jasig.cas.web.support.CookieRetrievingCookieGenerator"

       p:cookieSecure false "

       p:cookieMaxAge "-1"

       p:cookieName "CASPRIVACY"

       p:cookiePath "/cas" />

方式二:https协议:

生成安全证书

1win+R 快捷键打开DOS命令窗口

2:输入以下命令,生成证书

[html] view plaincopy

keytool -genkey -alias ssodemo -keyalg RSA -keysize 1024 -keypass ywj123 -validity 365 -keystore G:\ywj_cas.keystore -storepass ywj123  

解释一下:

-alias后面的ssodemo 是我生成证书的别名 ,

-keypass ywj123是我要生成证书的密码,

-storepass ywj123要与上面的-keypass密码相同

-keystore G:\ywj_cas.keystore 指定证书的位置

【注意】:第一个让你输入的您的名字与姓氏是什么,请必须输入在C:\Windows\System32\drivers\etc\hosts文件中加入的服务端的域名。

我这里也就是server.ywj123.com,为何这么做?

首先cas只能通过域名来访问,不能通过ip访问,同时上方是生成证书,所以要求比较严格,所以如果不这么做的话,及时最终按照教程配置完成,cas也可以正常访问,访问一个客户端应用虽然能进入cas验证首页,但是,当输入信息正确后,cas在回调转入你想访问的客户端应用的时候,会出现No subject alternative names present错误异常信息,这个错误也就是在上面输入的第一个问题答案不是域名导致、或者与hosts文件配置的不一致导致

3:导出证书

cmd窗口继续输入以下命令,导出证书:

[html] view plaincopy

keytool -export -alias ssodemo -keystore G:\ywj_cas.keystore -file G:\ssodemo.crt -storepass ywj123  

 

说明:-alias后面的名称要与生成证书的命令里面的alias的名称一致. –keystore后面指定证书存放的位置,这里我放在G盘根目录,同时证书名称要与【生成证书】对应的命令里的keystore名称一致.这里是 ywj_cas.keystore -file后面才crt路径,我也指定在G盘根目录.–storepass的证书密码要与上面输入的密码一致.

补充一下:这一步可能出现的权限问题如下图:

这个很简单,只要在所需要写入的磁盘中增加访问:

 

4JDK导入证书

由于是本地没有证书,证书是自己生成的,所以,务必将生成的证书导入到jre的证书链中,不然是无法支持CAS认证服务的。

 cmd窗口输入命令:

 

[html] view plaincopy

keytool -import -keystore "%JAVA_HOME%\jre\lib\security\cacerts" -file G:\ssodemo.crt -alias ssodemo  

这里一定要注意一点首先使用%JAVA_HOME%这个路径你是在自己系统环境变量中配置好的jdk路径的,其次一定要主要双""别忘记加了。我就是在这里采坑的,另外还有就是输入这行命令的前提路径是进入你的cd G:\ProgramFiles\Java\jdk1.7.0_17\jre\lib\security这个jdk的安装路径

上图:

第一个红色框是我进入的JDK目录第二个我没有加""报的错,第三个是我正确输入路径之后展示的效果就是正确的

接下来输入密码changeit这是默认额的密码建议不要修改,不是输入上面的keypass密码啊,不要弄错了。

紧接着输入y便可JDK导入安全证书成功。

附常用命令:

//查看cacerts中的证书列表:

             keytool -list -keystore "%JAVA_HOME%/jre/lib/security/cacerts" -storepass changeit

//删除cacerts中指定名称的证书:

             keytool -delete -alias ssodemo-keystore "%JAVA_HOME%/jre/lib/security/cacerts" -storepass changeit

//导入指定证书到cacerts
              keytool -import -alias ssodemo -file ssodemo.cer-keystore"%JAVA_HOME%/jre/lib/security/cacerts" -storepasschangeit-trustcacerts

至此,CAS所需的证书环境,已经配置好。
下面,开始我们的CAS服务、Tomcat、客户端的配置及测试访问。

 

部署CAS-Server相关的Tomcat

1.配置HTTPS
解压apache-tomcat-7.0.78-windows-x64.zip,我本地路径为G:\apache-tomcat-7.0.57,编辑G:\apache-tomcat-7.0.78\conf\server.xml,找到下面片段:  

<!--

   <Connector executor="tomcatThreadPool"port="8080" protocol="HTTP/1.1"connectionTimeout="20000" redirectPort="8443" />

-->

去掉注释,修改成:

<Connectorport="8443" protocol="org.apache.coyote.http11.Http11Protocol"

   maxThreads="150" SSLEnabled="true" scheme="https" secure="true"

   keystoreFile="G:/ ywj_cas.keystore" keystorePass="ywj123"

   clientAuth="false" sslProtocol="TLS" />

 

其中,keystoreFile就是创建证书的路径,keystorePass就是创建证书的密码.

2.  验证HTTPS配置

其他按照默认配置不作修改,双击G:\apache-tomcat-7.0.57\bin \startup.bat 启动tomcat验证https是否配置成功,我本地使用谷歌览器访问,在地址栏输入
https://server.ywj123.com:8443/ 
出现下面画面,其实这就表明cas服务端tomcathttps配置是没有问题了

3 .部署CAS-Server

    CAS-Server 下载地址: http://www.jasig.org/cas/download  
    
本文以cas-server-webapp-4.0.0.rar为例,解压提取cas-server-webapp-4.0.0.war文件,把改文件copyG:\apache-tomcat-7.0.57\webapps 目下,并重命名为:cas.war

    启动tomcat,在浏览器地址栏输入:https://server.ywj123.com:8443/cas,回车,出现CAS服务端的登录验证首页:

这个页面不是原始的页面我修改了背景图片,登录框的位置还没来得及修改,问题不大。

【说明】:此时,CAS只是单独运行,至于登录的用户名和密码是什么,请查看:G:\apache-tomcat-7.0.57\webapps\cas\WEB-INF\deployerConfigContext.xml文件中有这样一段配置:

<bean id="primaryAuthenticationHandler"class="org.jasig.cas.authentication.AcceptUsersAuthenticationHandler">

   <property name="users">

       <map>

           <entry key="casuser" value="Mellon"/>

       </map>

   </property>

</bean>

代码中key="casuser" value="Mellon就是默认的用户名和密码。

用户名和密码肯定需要和数据库进行交互验证的,,那么,如何配置呢?
【说明】:我本地使用的是oracle数据库。
1、需要将几个jar文件,放到CAS服务的lib目录下

我本地使用的jar版本分别是c3p0-0.9.1.2.jarcas-server-support-jdbc-4.0.0.jaroracle6-11.2.0.1.0.jar,这3个缺一不可。将这3jar放到G:\apache-tomcat-7.0.57\webapps\cas\WEB-INF\lib目录下。
2、修改配置,支持oracle数据库交互验证
编辑G:\apache-tomcat-7.0.57\webapps\cas\WEB-INF\deployerConfigContext.xml文件,你会看到有这样一段配置:

注释掉第二行entry key,就是注释掉默认的登录名和密码

然后再在这个xml中新加入2bean配置,如下:

要说明的是,红色标注的内容,我想大家都能看的明白,就是指定数据库驱动和连接信息。其中,sql语句的意思就是,根据用户名获取密码,CAS会根据你页面输入的用户名获取该用户密码,和你输入的密码进行校验,来判断输入是否正确。

其中,sql中的表换成你自己本地的表即可,或者自己手动新建一个表,加入几条测试数据即可。只要根据用户名查询密码即可。

至此,CAS与数据库交互验证的配置已经配置完成,你可以重新访问cas,输入数据库中存在的用户名和密码,来看看效果如何~如果登录成功,说明配置无误。否则,请耐心检查配置是否有问题,jar包是否缺少。

 

客户端和cas server的交互

1.部署CAS客户端相关的Tomcat

首先,客户端应用是要和CAS服务端进行交互的,所以这里需要jar文件,放在客户端应用的lib目录下。分别是:cas-client-core-3.2.1.jarcommons-logging.jar

2.在客户端的web.xml中添加配置信息

让客户端应用和CAS服务连接:
编辑G:\apache-tomcat-7.0.57-client1\webapps\examples\WEB-INF\web.xml,在最下面加入如下配置:

<!-- 用于单点退出,该过滤器用于实现单点登出功能,可选配置-->

   <listener>

        <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>

   </listener>

  <!--  该过滤器用于实现单点登出功能,可选配置 -->

   <filter>

        <filter-name>CASSingle Sign Out Filter</filter-name>

        <filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class>

   </filter>

   <filter-mapping>

        <filter-name>CASSingle 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://server.ywj123.com:8443/cas/login</param-value>

        </init-param>

        <init-param>

            <param-name>serverName</param-name>

            <param-value>http://localhost:8080</param-value>

        </init-param>

   </filter>

   <filter-mapping>

        <filter-name>CASFilter</filter-name>

        <url-pattern>/*</url-pattern>

   </filter-mapping>

 <!--   该过滤器负责对Ticket的校验工作,必须启用它 -->

   <filter>

        <filter-name>CASValidation Filter</filter-name>

        <filter-class>

           org.jasig.cas.client.validation.Cas10TicketValidationFilter</filter-class>

        <init-param>

            <param-name>casServerUrlPrefix</param-name>

            <param-value>https://server.ywj123.com:8443/cas</param-value>

        </init-param>

        <init-param>

            <param-name>serverName</param-name>

            <param-value>http://localhost:8080</param-value>

        </init-param>

   </filter>

   <filter-mapping>

        <filter-name>CASValidation Filter</filter-name>

        <url-pattern>/*</url-pattern>

   </filter-mapping>

   

  <!--      该过滤器负责实现HttpServletRequest请求的包裹,比如允许开发者通过HttpServletRequestgetRemoteUser()方法获得SSO登录用户的登录名,可选配置。 -->

  

   <filter>

        <filter-name>CASHttpServletRequest Wrapper Filter</filter-name>

        <filter-class>org.jasig.cas.client.util.HttpServletRequestWrapperFilter</filter-class>

   </filter>

   <filter-mapping>

        <filter-name>CASHttpServletRequest Wrapper Filter</filter-name>

        <url-pattern>/*</url-pattern>

   </filter-mapping>

   

<!--        该过滤器使得开发者可以通过org.jasig.cas.client.util.AssertionHolder来获取用户的登录名。比如AssertionHolder.getAssertion().getPrincipal().getName() -->

  

   <filter>

        <filter-name>CASAssertion 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>

单点登录整合

基本上到这里所有的配置都结束了,项目可以正常运行了。但是具体情况还有具体说明和解决。因为我在配置这些服务之前其实本身项目里就有自己的登录的页面和流程,所以你不可能让客户登录一次总的cas server页面时还再次登录各个子系统自己网站吧,那就失去了单点登录的意义了,所以我在思考一段时间之后决定是有选择性的绕过子系统的登录流程,不影响子系统的运行做法,具体如下。

1.新建一个cas.jsp文件有如下内容

$(document).ready(function(){

        var self= this;

        var data= {};

        /*data.name= $("#userName").val();

        data.password =$("#password").val();

        data.name ="admin";

        data.password ="1";*/

        $.ajax({

            url:"/ptgl/LOGIN/login.do",

            type:"POST",

            data:JSON.stringify(data),

            success:function(json){

                    

                varresult = JSON.parse($.base64().decode(json));

                console.log(result);

                if(result.success){

                   window.open("/ptgl/ptgl/main.do","_self");                  

                }else{

                   alert("ajax失败!");

                }

            }

        });

          

});

截图如下:

说明:这里我是做了一个自动登录的AJAX方法,当我选择跳转到这个cas.jsp的时候会自动跳转到之前登录的Controller方法里面。但是也需要在之前的Controller里面改动一些代码以配合当前的登录要求。可以看到我这里的data的参数已经被我注释掉了。这里是不需要的,因为我在后台帮助获取到了。

2后台代码修改

后台如下:

       @RequestMapping(value="/login",method=RequestMethod.POST)

       public voidlogin(HttpServletRequest request, HttpServletResponse response)

                     throws UnsupportedEncodingException{

             

              //cas server 中获取登录名

              AttributePrincipalprincipal = (AttributePrincipal)request.getUserPrincipal();

 

              StringcasLoginName = principal.getName();

             

              PrintWriterwriter = null;

              ResponseVovo = new ResponseVo();

              try { 

              WebApplicationContextwebApplicationContext = ContextLoader.getCurrentWebApplicationContext();   

       ServletContext servletContext =webApplicationContext.getServletContext(); 

       String bb=(String)servletContext.getAttribute("bb");

       request.getSession().setAttribute("key",bb);

       String kk=LL.kk(bb);

       SimpleDateFormat format = newSimpleDateFormat("yyMMdd");

              Stringdatestr = format.format(new Date());

               Calendar calendar = Calendar.getInstance();

               calendar.setTime(newDate());

               calendar.add(Calendar.YEAR, -1);

             Date date = calendar.getTime();

             String datestr2 = format.format(date);

             writer = response.getWriter();

             if((kk.compareTo(datestr)<=0)&&(kk.compareTo(datestr2)>=0))

             {

                     response.setHeader(Constant.CROSS_DOMAIN, "*");

//                  String[]dataOne=request.getParameterValues("data");

//                  RestServerserver = new RestServer(request, response);

//                  此处正是从前台传过来的账户名密码这两个参数,通过server.getData()获得

//                  Stringdata = server.getData();

//                  UserLoginVologinVo = JSONUtil.toBean(data, UserLoginVo.class);

                     UserLoginVologinVo = new UserLoginVo();

//                  此处拿到账户名密码之后通过loginBusiness.login(loginVo)方法得到用户的所有信息

                     loginVo.setName(casLoginName);

                     vo= loginBusiness.login(loginVo);

                    

                     if(vo.isSuccess())

                       request.getSession().setAttribute("user", vo.getData());

                    

                     else

                     {

                             vo.setSuccess(false);

                                   vo.setErrorCode(-1000);

                                   vo.setMsg("&nbsp;&nbsp;用户名或密码错误!");

                     }

              }else

             {

                    log.info(bb);  

             }

      } catch (Exception e) {

               vo.setSuccess(false);

                     vo.setErrorCode(-1000);

                     vo.setMsg("错误原因:" +e.getMessage());

              }

             

       writer.print(Base64.encodeBase64String(JSONObject.fromObject(vo).toString().getBytes("utf-8")));

       }

截图如下:

另:

public ResponseVologin(UserLoginVo loginVo){

              ResponseVoresponseVo = new ResponseVo();

              SysUserEntityentity = new SysUserEntity();

              try{

                     /*if(StringUtils.isEmpty(loginVo.getName()) ||StringUtils.isEmpty(loginVo.getPassword())){

                     responseVo.setMsg(ErrorCodeType.USERNAME_OR_PASSWORD_IS_NULL.getErrorMsg());

                            responseVo.setSuccess(false);

                            returnresponseVo;

                     }*/

                     entity.setUsName(loginVo.getName());

                    

              System.out.println("loginVo.getName():"+loginVo.getName()+"====qweqwe=====11111======begin==========="+entity.getUsName());

                     SysUserEntityuserEntity =

                                   sysUserService.selectUserInfo(entity);

                        

              System.out.println("===========22222============"+userEntity.getUsId());

                     responseVo.setData(userEntity);

                     //若查询为null,则直接返回不存在该用户

                     if(userEntity == null){

                            responseVo.setMsg(ErrorCodeType.USERNAME_IS_NOT_EXIST.getErrorMsg());

                            responseVo.setSuccess(false);

                            return responseVo;

                     }

              /*    if(StringUtils.isNotBlank(loginVo.getPassword())

                                   &&CommonUtil.md5Encode(loginVo.getPassword()).

                                   equalsIgnoreCase(userEntity.getUsPassword())){

                            //如果都正确,则将状?设置成在线状??

//                  sysUserService.updateOnlineStatus(OnlineType.ONLINE.getStatus(),userEntity.getUsTname());

                           

                            intrememberStatus = 0;

                            //获取他?中记住密码的状?

                            responseVo.setData(userEntity);

                      

                            //若不存在,则说明他的记住状?已经过期,或?之前没有??

                            if(rememberStatus< 1 && loginVo.getRememberStatus() ==RememberType.REMEMBER.getCode()){

                                   //如果选中了记住密码,则用redis记住他的状?,过期时间为一个月[?个月内免登陆]

                                  }else{

                                   //responseVo.setData(rememberStatus);

                                   returnresponseVo;

                            }

                           

                           

                     }else{

                            responseVo.setSuccess(false);

                            responseVo.setMsg(ErrorCodeType.PASSWORD_IS_ERROR.getErrorMsg());

                     }*/

              }catch(Exception e){

                     e.printStackTrace();

              }

             

              return responseVo;

       }

可以对照上面的代码把红色方框中代码注释掉逻辑就全部通了,这也就是代码整合时难点的地方,把不同的逻辑理通不出现前后矛盾。

可能遇到的问题总结:

1.question_ONE

此项目中我是把第三方的cas server服务器单独建一个Tomcat进行运行的,要注意端口会和自己的真实项目的端口号会重复,所以只需简单修改一下端口号就可以了。如果不会的话我贴上解决方案:https://blog.csdn.net/jay_1989/article/details/52870760这篇博客已经说明的很详细了,不会的看一下。

2.question_TWO

如果你仔细观看我的代码及了解cas原理过后的话会发现很多不便的地方,比如配置文件应该单独提取才是,cas 客户端应该获取更多的登录信息等。但是这些都不是最重要的,我发现了一个我自己也没办法解决的问题那就是我发现当登录过后controllerrequest里面的信息是cas服务端发过来的,所以我能取到登录名,但是我自己新建的jsp文件自动登录功能绕过了此前的子系统的登录页面。后台的controller就无法获取到准确的request了。

就是说原先应该是子系统的request变成了cas服务端的request了,这个后面有时间再去研究。

3.question_THREE

其实子系统的客户端应该本着资源不浪费的情况下不需要再根据用户名去查询对应的信息了。贴上一些有用的博客信息。

https://blog.csdn.net/hejingyuan6/article/details/45126373

http://www.cnblogs.com/vhua/p/cas_4.html

https://blog.csdn.net/redstarofsleep/article/details/51145062


猜你喜欢

转载自blog.csdn.net/qq_24571209/article/details/80257663
今日推荐