cas5.3.9实现多客户端单点登录单点登出案例

经过一个星期的业余时间研究,终于学会了用cas5.3.9这个版本做单点登录单点登出功能,还是比较简单方便的,在此记录一下。

cas5.3.9是基于springboot开发,只支持1.8jdk以上和tomcat8以上,这里用1.8jdk和tomcat8.5跑cas服务端,用1.8jdk和tomcat7跑普通ssm web客户端

操作步骤大体可分为以下几步:
1.去cas官网github地址下载cas5.3.9的cas-overlay-template-5.3.zip
2.根据解压的这个文件overlay一个cas服务端,基于maven构建方式
3.建立maven cas项目,把生成的cas web服务端文件复制到这个项目中,更改一些必要配置,兼容http访问和数据库读取user表方式验证,跑起cas服务端
4.ssm web客户端引入cas必要的jar包依赖,解决jar包冲突问题,配置web.xml实现单点登入登出
5.写测试页面测试登入登出功能

一、去cas官网github地址下载cas5.3.9的cas-overlay-template-5.3.zip
地址:https://github.com/apereo/cas-overlay-template/tree/5.3
二、根据解压的这个文件overlay一个cas服务端,基于maven构建方式
下载解压之后,按照里面的readme.md指导文件上所说的方式编译一个cas项目出来
./build.sh help可以看到里面的各种命令
./build.sh clean 清除target
./build.sh package 打包
也可以直接在解压的文件里直接运行:
mvn clean
mvn install
以上都是linux的构建方式 如果是windows,则要在cmd里打开这个解压的文件夹运行build.cmd clean
build.cmd package
命令就可以打包啦

有可能会打包失败,因为国内被墙的原因,下载速度奇慢,所以如果打包失败了,八成原因是maven库里的
.m2/repository/org/apereo/cas/cas-server-webapp-tomcat/5.3.9/cas-server-webapp-tomcat-5.3.9.war这个文件下载不成功导致,这个还比较大,有一百多M
所以最简单的方法就是去一个maven项目里,在pom.xml里加入这个文件的依赖地址,点击保存就会自动下载到库里啦,如果maven库用的是国内比如阿里云的私服就很快下载完啦。
依赖地址:

        
            org.apereo.cas
            cas-server-webapp-tomcat
            5.3.9
            war
        

下载完这个war包之后,再重新进行上面的打包操作就很快打包成功啦,打包成功后会看到一个target文件夹,里面的cas.war和解压的cas就是项目代码啦是可以直接在tomcat8.5下运行的

三、建立maven cas项目,把生成的cas web服务端文件复制到这个项目中,更改一些必要配置,兼容http访问和数据库读取user表方式验证,跑起cas服务端
用eclipse New一个maven web项目,把解压出来的cas文件夹下的文件都放在webapp下面


深度截图_选择区域_20190803172010.png

生成的结构如图:


深度截图_选择区域_20190803172157.png

把WEB-INF/classes/application.properties复制到resources文件夹下
把WEB-INF/classes/services文件夹复制到resources文件夹下
如图:


深度截图_选择区域_20190803172625.png

更改HTTPSandIMAPS-10000001.json 加入|http


深度截图_选择区域_20190803172854.png

更改application.properties 加入以下配置

cas.tgc.secure=false
cas.serviceRegistry.initFromJson=true
# Json services \u914D\u7F6E\u4F4D\u7F6E\u8BBE\u5B9A
#cas.serviceRegistry.json.location=classpath:/services

至此加入http协议支持就完成啦

接下来加入jar包支持oracle数据库,pom.xml里加入以下依赖

        
            org.apereo.cas
            cas-server-support-jdbc-drivers
            5.3.9
        
        
        
            org.apereo.cas
            cas-server-support-jdbc
            5.3.9
        
        
        
        
            com.oracle.jdbc
            ojdbc6
            11.1.0.6.0
        

oracle驱动是试了很久才知道必须用ojdbc6的,用其他版本的oracle或许会jar包冲突,项目要求jdk1.8的,ojdbc6是支持1.8的,ojdbc14就不行,上面的两个cas-server jdbc jar包也必须是5.3.9的 用其他版本也会冲突 如果是mysql数据库则可以用mysql的数据库驱动jar包
application.properties里加入数据库配置

cas.authn.jdbc.query[0].url=jdbc\:oracle\:thin\:@127.0.0.1\:1521\:xe
cas.authn.jdbc.query[0].user=zhaohy
cas.authn.jdbc.query[0].password=oracle
cas.authn.jdbc.query[0].sql=select * from da_user where user_name=?
cas.authn.jdbc.query[0].fieldPassword=password
cas.authn.jdbc.query[0].driverClass=oracle.jdbc.OracleDriver

至此cas就可以读取数据库里的user表来完成账户用户名和密码验证啦,对了,cas默认的账户配置不要忘了注释掉:

#cas.authn.accept.users=casuser::Mellons

以上,cas的服务端配置就完成了,把cas项目部署到tomcat8.5中可以运行成功,数据库里面未加密的用户名密码可以通过验证。

项目跑起来后访问本地cas地址:

http://127.0.0.1:8082/cas/login

可以跳转到登录页面 输入数据库中的用户名和密码即可登录成功

补充:

配置cas自带md5加密方式

只需要在application.properties增加如下代码:

#配置md5加密
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

数据库存储md5加密之后的密码,再重新启动cas,输入明文密码也可以登录

配置自定义加密类

自定义加密类必须要实现org.springframework.security.crypto.password.PasswordEncoder这个接口
创建自定义加密类MD5Util

package com.zhaohy.app.util;

import java.security.MessageDigest;

import org.springframework.security.crypto.password.PasswordEncoder;


/**
 * MD5加密
 * @author Administrator
 *
 */
public class MD5Util implements PasswordEncoder {
    /**
     * Title: MD5加密 生成32位md5码
     * Description: TestDemo
     * @author lu
     * @date 2016年6月23日 下午2:36:07
     * @param inStr
     * @return 返回32位md5码
     * @throws Exception
     */
    public static String md5Encode(String inStr) throws Exception {
        MessageDigest md5 = null;
        try {
            md5 = MessageDigest.getInstance("MD5");
        } catch (Exception e) {
            System.out.println(e.toString());
            e.printStackTrace();
            return "";
        }
        byte[] byteArray = inStr.getBytes("UTF-8");
        byte[] md5Bytes = md5.digest(byteArray);
        StringBuffer hexValue = new StringBuffer();
        for (int i = 0; i < md5Bytes.length; i++) {
            int val = ((int) md5Bytes[i]) & 0xff;
            if (val < 16) {
                hexValue.append("0");
            }
            hexValue.append(Integer.toHexString(val));
        }
        return hexValue.toString();
    }
    /**
     * Title: MD5加密
     * Description: TestDemo
     * @author lu
     * @date 2016年6月23日 下午2:43:31
     * @param inStr
     * @return
     */
    public static String string2MD5(String inStr) {
        MessageDigest md5 = null;
        try {
            md5 = MessageDigest.getInstance("MD5");
        } catch (Exception e) {
            System.out.println(e.toString());
            e.printStackTrace();
            return "";
        }
        char[] charArray = inStr.toCharArray();
        byte[] byteArray = new byte[charArray.length];

        for (int i = 0; i < charArray.length; i++)
            byteArray[i] = (byte) charArray[i];
        byte[] md5Bytes = md5.digest(byteArray);
        StringBuffer hexValue = new StringBuffer();
        for (int i = 0; i < md5Bytes.length; i++) {
            int val = ((int) md5Bytes[i]) & 0xff;
            if (val < 16)
                hexValue.append("0");
            hexValue.append(Integer.toHexString(val));
        }
        return hexValue.toString();

    }

    /**
     * Title: 加密解密算法 执行一次加密,两次解密
     * Description: TestDemo
     * @author lu
     * @date 2016年6月23日 下午2:37:29
     * @param inStr
     * @return
     */
    public static String convertMD5(String inStr) {

        char[] a = inStr.toCharArray();
        for (int i = 0; i < a.length; i++) {
            a[i] = (char) (a[i] ^ 't');
        }
        String s = new String(a);
        return s;

    }
    public static String md5Decode(String str) {
        return convertMD5(convertMD5(str));
    }

    public static void main(String[] args) {//e10adc3949ba59abbe56e057f20f883e
        String s = new String("123456");
        System.out.println(md5Decode("a6aeb3ffa55fc7d664406af9c3bd0f1b"));
        System.out.println("原始:" + s);
        System.out.println("MD5后:" + string2MD5(s));
        System.out.println("加密的:" + convertMD5(s));
        System.out.println("解密的:" + convertMD5(convertMD5(s)));
    }
    
    /**
     * 对输入的密码加密过程
     */
    @Override
    public String encode(CharSequence inputPwd) {
        System.out.println("inputPwd===" + inputPwd);
        System.out.println("string2MD5==" + string2MD5(inputPwd.toString()));
        return string2MD5(inputPwd.toString());
    }
    
    /**
     * 密码校验过程
     */
    @Override
    public boolean matches(CharSequence inputPwd, String dbPwd) {
        // 判断密码是否存在
        if (inputPwd == null) {
            return false;
        }
        System.out.println("inputPwd==" + inputPwd + "  " + "dbPwd==" + dbPwd);
        if(dbPwd.contentEquals(this.encode(inputPwd))){
            return true;
        }
        return false;
    }
}

其中的encode和matches方法是实现PasswordEncoder接口的方法,一个做加密处理,一个做校验处理。
application.properties增加如下代码:

#自定义密码加密方式
cas.authn.jdbc.query[0].passwordEncoder.type=com.zhaohy.app.util.MD5Util
cas.authn.jdbc.query[0].passwordEncoder.characterEncoding=UTF-8

重启cas之后也能登录成功。

四、ssm web客户端引入cas必要的jar包依赖,解决jar包冲突问题,配置web.xml实现单点登入登出

建立两个ssm客户端项目,分别为ssmTest05,ssmTest05-1
在客户端项目中pom.xml引入jar包:

        
            org.jasig.cas.client
            cas-client-core
            3.5.1
            
                com.fasterxml.jackson.core
                jackson-databind
            
        
        
            com.fasterxml.jackson.core
            jackson-databind
            2.10.0.pr1
        
        
        
        
            org.apache.commons
            commons-collections4
            4.4
        
        
        
            commons-logging
            commons-logging
            1.2
        

上面的cas-client-core.3.5.1.jar依赖jackson-databind 2.8.8的版本,但是这个版本跟我用的spring 5.1.8.RELEASE 版本冲突,所以剔除了这个jackson,用了2.10.0.pr1版本的jackson-databind

web.xml配置:


    
    
        org.jasig.cas.client.session.SingleSignOutHttpSessionListener
    

    
    
        CAS Single Sign Out Filter
        org.jasig.cas.client.session.SingleSignOutFilter
        
          casServerUrlPrefix
          http://localhost:8082/cas
       
    
    
        CAS Single Sign Out Filter
        /*
    

    
    
        CASFilter
        org.jasig.cas.client.authentication.AuthenticationFilter
        
            casServerLoginUrl
            http://127.0.0.1:8082/cas/login
        
        
        
            serverName
            http://127.0.0.1:8081
        
    
    
        CASFilter
        /*
    

    
    
        CAS Validation Filter
        
            org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter
        
            casServerUrlPrefix
            http://127.0.0.1:8082/cas
        
        
            serverName
            http://127.0.0.1:8081
        
    
    
        CAS Validation Filter
        /*
    

    
    
        CAS HttpServletRequest Wrapper Filter
        
            org.jasig.cas.client.util.HttpServletRequestWrapperFilter
    
    
        CAS HttpServletRequest Wrapper Filter
        /*
    

    
    
        CAS Assertion Thread Local Filter
        org.jasig.cas.client.util.AssertionThreadLocalFilter
    
    
        CAS Assertion Thread Local Filter
        /*
    
    
        
        

        
        
                2
        

两个ssm客户端项目都是这样配置,至此客户端就配置完了

cas服务端application.properties文件里加入单点登出设置:

#配置单点登出
#配置允许登出后跳转到指定页面
cas.logout.followServiceRedirects=true
#跳转到指定页面需要的参数名为 service
cas.logout.redirectParameter=service
#登出后需要跳转到的地址,如果配置该参数,service将无效。
#cas.logout.redirectUrl=https://www.taobao.com
#在退出时是否需要 确认退出提示   true弹出确认提示框  false直接退出
cas.logout.confirmLogout=false
#是否移除子系统的票据
cas.logout.removeDescendantTickets=true
#禁用单点登出,默认是false不禁止
#cas.slo.disabled=true
#默认异步通知客户端,清除session
cas.slo.asynchronous=true

至此,整个项目的配置都配置好了

五、写测试页面测试登入登出功能
在ssmTest05客户端里webapp下建立index.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>




Insert title here



Hello SpringMVC!


在controller里写一个/user/logout.do接口

@RequestMapping("/user/logout")
  public void logout(HttpServletResponse response) {
      try {
        response.sendRedirect("http://127.0.0.1:8082/cas/logout?service=http://127.0.0.1:8081/ssmTest05/logout.jsp");
    } catch (IOException e) {
        e.printStackTrace();
    }
  }

在webapp下再写一个logout.jsp,退出后重定向到项目会被cas拦截进入登录页面

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>




Insert title here


<% 
 response.sendRedirect("http://localhost:8081/ssmTest05");  //重定向
%>



在ssmTest05-1中webapp下写一个index.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>




Insert title here



Hello SpringMVC-222!


运行cas和两个客户端项目
访问

http://127.0.0.1:8081/ssmTest05

会被cas拦截至登录页面 输入数据库中的用户名和密码后成功进入项目
这时访问

http://127.0.0.1:8081/ssmTest05-1

也可以成功进入第二个项目
随便在两个项目中点击哪个退出按钮,会跳转到cas登录页面,在不登录的情况下访问哪个项目都会被拦截,至此单点登录和登出就实现成功啦!

在cas客户端里面如何拿到用户的登录信息呢

cas客户端提供了org.jasig.cas.client.util.AbstractCasFilter这个类
可以在客户端代码中加入如下判断来判断用户有没有登录以及拿到用户名

      if(null == request.getSession().getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION)) {
            System.out.println("用户未登录!");
        } else {
            Assertion assertion = (Assertion) request.getSession().getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION);  
            AttributePrincipal principal = assertion.getPrincipal();  
            String username = principal.getName(); 
            System.out.println("userName=op==" + username);
        }

自定义login页面

官方参考文档:
https://apereo.github.io/cas/5.3.x/installation/Configuration-Properties.html#themes
https://apereo.github.io/cas/5.3.x/installation/User-Interface-Customization-Themes.html

主题意味着风格不一样,目的就是不同的接入端,显示不同的登录页面,就像阿里旗下的各种登录,支付宝登录,淘宝,天猫,用的可能就是同一个sso,但是各自的登录主题不一样。
简略看完后,会有以下的规范:
● 静态资源(js,css)存放目录为src/main/resources/static
● html资源存(thymeleaf)放目录为src/main/resources/templates
● 主题配置文件存放在src/main/resources并且命名为[theme_name].properties
● 主题页面html存放目录为src/main/resources/templates/

自定义login页面cas5也支持同时定制多种主题,可以为不同客户端灵活使用,如果不设置主题配置,cas就使用默认主题。
可以定制动态主题和静态主题,时间有限,只研究了静态主题,这里只介绍一下静态主题的配置方法。

在客户端注册的json文件中添加theme属性

如图,在src/main/resources/services/文件夹下之前已经添加了两个json文件,
在json文件种添加theme属性


深度截图_选择区域_20190820225531.png

如:我们在HTTPSandIMAPS-10000001.json里修改

{
  "@class" : "org.apereo.cas.services.RegexRegisteredService",
  "serviceId" : "^(https|imaps|http)://127.0.0.1:8081/ssmTest05-1/.*",
  "name" : "HTTPS and IMAPS",
  "id" : 10000003,
  "description" : "This service definition authorizes all application urls that support HTTPS and IMAPS protocols.",
  "evaluationOrder" : 10001,
  "theme" : "app2"
}

其中的theme就是指定自定义的主题 ,这里指定app2主题,serviceId是对客户端的请求使用该主题,可以看到这个主题对ssmTest05-1这个项目的请求使用
在app2.json里修改:

{
  "@class" : "org.apereo.cas.services.RegexRegisteredService",
  "serviceId" : "^(https|imaps|http)://127.0.0.1:8081/ssmTest05/.*",
  "name" : "HTTPS and IMAPS",
  "id" : 10000001,
  "description" : "This service definition authorizes all application urls that support HTTPS and IMAPS protocols.",
  "evaluationOrder" : 10000,
  "theme" : "app1"
}

这个主题对ssmTest05这个项目的请求使用

在src/main/resources下创建app1.properties 和 app2.properties

根据官网文档,需要在src/main/resources文件夹的根目录下创建 与 json文件中theme属性值 对应的properties
所以要在src/main/resources新建app1.properties 和 app2.properties
app1.properties:

#cas css
cas.standard.css.file=/css/cas.css
#my css
cas.myself.css=/themes/app1/css/cas.css
cas.javascript.file=/themes/app1/js/jquery-3.4.1/jquery-3.4.1.min.js
cas.page.title=app1Theme

app2.properties:

#cas css
cas.standard.css.file=/css/cas.css
#my css
cas.myself.css=/themes/app2/css/cas.css
cas.javascript.file=/themes/app1/js/jquery-3.4.1/jquery-3.4.1.min.js
cas.page.title=app2Theme

在app1.properties 和 app2.properties 中的属性值都是随便起,只要在html中指明引用的key就可以了,例如:properties中指明css和js文件地址,然后在html中用下面的方式使用。



注意:上面配置文件中有cas.standard.css.file属性,这个属性默认就是指向/css/cas.css也就是cas默认的css文件,这个我们要指明,否则你只是自定义了登录页面,其他页面的样式将丢失。我们在自定义的登录页面使用自己的css文件,不跟cas的默认css混淆。

创建cas.css文件

app1对应的cas.css:

h2 {
    color: red;
}

app2对应的cas.css

h2 {
    color: blue;
}

在application.properties中添加以下属性,配置默认主题

# 默认主题
cas.theme.defaultThemeName=app1

配置不同客户端的登录页面

两个客户端的页面都是一样的,给一个页面,其他复制就可以了
app1下的 casLoginView.html




    
    
    
    



搭建完成后目录如下:


深度截图_选择区域_20190820231222.png

测试

一、先直接登录cas服务,默认使用的是app1的主题
二、访问http://127.0.0.1:8081/ssmTest05 显示app1的主题。

深度截图_选择区域_20190820231732.png

三、访问http://127.0.0.1:8081/ssmTest05-1 显示app2的主题。

深度截图_选择区域_20190820231821.png

至此demo已写完 cas就可以用啦
项目demo代码已上传github:
https://github.com/haiyong6/haiyongsRepository/tree/master/code/casDemo

你可能感兴趣的:(cas5.3.9实现多客户端单点登录单点登出案例)