CAS使用账号密码实现单点登陆

  • CAS单点登陆框架
    • 项目需求
    • 实现思路
      • 1、使用用户名密码获取TGT
      • 2、根据TGT和Service获取ST
      • 3、使用ST访问目标资源
    • Java代码实现

CAS单点登陆框架

CAS 是 Yale 大学发起的一个开源项目,旨在为 Web 应用系统提供一种可靠的单点登录方法
官方网站
https://www.apereo.org/projects/cas

这里不详细介绍CAS的单点登陆原理,感兴趣的同学可以自己去研究一下,这里仅就项目中遇到的问题进行记录

项目需求

我们的系统通过单点登陆跳转到另一个系统,我们的系统使用的是Spring Security做用户验证,另一个系统使用的CAS单点登陆框架进行验证,两套系统用户没有任何关系,
对方系统仅提供了一个用户名和密码,所以我们需要通过用户名密码进行CAS登陆验证

实现思路

使用CAS的Restful API进行实现(需要CAS服务端配置REST API启用才可以,我没有找到使用CAS Client直接进行用户名密码验证的方法,有知道方法的大神可以指教一下)

1、使用用户名密码获取TGT

  • api: http://127.0.0.1:8080/cas/v1/tickets

  • method: post

  • RequestHeader:

    • Content-type: application/x-www-form-urlencoded
  • Requestbody:

    • username: user1
    • password: pwd
  • ResponseBody:

(这个可能与CAS服务端的配置或版本有关系,我也只是猜测,我调用的三方厂家服务的API只能获取到html格式的返回值,即使在Header里设置了Accept = application/json)

<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">
<html><head><title>201 Created</title></head><body><h1>TGT Created</h1><form action="http://127.0.0.1/cas/v1/tickets/TGT-2974-4Mki7nXCTz7Q9mQ9CZRbjF9k9WaZCc9bwR42VR46aMchxmS24r" method="POST">Service:<input type="text" name="service" value=""><br><input type="submit" value="Submit"></form></body>
</html>

返回的消息中,对我们有用的数据为http://127.0.0.1/cas/v1/tickets/TGT-2974-4Mki7nXCTz7Q9mQ9CZRbjF9k9WaZCc9bwR42VR46aMchxmS24r,这样我们就获得了TGT

2、根据TGT和Service获取ST

TGT是上一步中获取的返回值,service是指我们需要访问的服务地址,如果只做测试的话可以直接写www.baidu.com也可以,不过为了测试最终获取的ST的有效性,最好还是让三方厂家提供一个他们的使用CAS验证的服务

  • api: http://127.0.0.1/cas/v1/tickets/TGT-2974-4Mki7nXCTz7Q9mQ9CZRbjF9k9WaZCc9bwR42VR46aMchxmS24r

就是上一步中获取到的TGT

  • method: post
  • RequestHeader:
    • Content-type: application/x-www-form-urlencoded
  • Requestbody:
    • service: http://www.baidu.com
  • ResponseBody:
    • ST-4674-nPDWMzyeysgGJFXTYC7J-radar

返回的消息只有一个text文本,我猜测可能也跟CAS服务端的配置或版本有关

3、使用ST访问目标资源

  • url: http://www.baidu.com?ticket=ST-4674-nPDWMzyeysgGJFXTYC7J-radar

直接使用浏览器访问这个服务地址就可以了,正常ST仅一次有效,默认的有效期是10s(我猜CAS服务端可以设置有效期的长短),使用ST访问一次之后,后续就不需要验证ticket了,会直接使用Cookie访问

Java代码实现

public String casSingleLogin() throws Exception{// 1、根据账号密码获取TGT// 访问api:http://127.0.0.1:8080/cas/v1/tickets// username和password以application/x-www-form-urlencoded格式放置到requestbody中// 返回的数据是html格式,正确样例如下,需要解析出action中的地址为下一步做准备// <!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">//<html>//    <head>//        <title>201 Created</title>//    </head>//    <body>//        <h1>TGT Created</h1>//        <form action="http://127.0.0.1:8080/cas/v1/tickets/TGT-2974-4Mki7nXCTz7Q9mQ9CZRbjF9k9WaZCc9bwR42VR46aMchxmS24r" method="POST">Service://            <input type="text" name="service" value="">//            <br>//            <input type="submit" value="Submit">//        </form>//    </body>//</html>String ticketUrlStr = "http://127.0.0.1:8080/cas/v1/tickets";String userName = "username";String password = "password";String tgtResult = "";try{String requestBody = URLEncoder.encode("username","UTF-8")+"="+URLEncoder.encode(userName,"UTF-8")+"&"+URLEncoder.encode("password","UTF-8")+"="+URLEncoder.encode(password,"UTF-8");tgtResult = doPost(ticketUrlStr,requestBody,201);}catch (Exception e){e.printStackTrace();}// 解析html中的数据if(StringUtils.isEmpty(tgtResult)){throw new Exception("连接失败");}String tgt = parseHtmlForTgt(tgtResult);if(StringUtils.isEmpty(tgt)){throw new Exception("连接失败");}// 2、根据TGT和service获取ST// 访问地址就是上一步中获取的 tgt,一般格式为:http://127.0.0.1:8080/cas/v1/tickets/TGT-2974-4Mki7nXCTz7Q9mQ9CZRbjF9k9WaZCc9bwR42VR46aMchxmS24r// 将需要访问的目标系统的地址service以application/x-www-form-urlencoded格式放置到requestbody中// 返回的数据就是ST票据信息String serviceUrl = "http://www.baidu.com";String stResult = "";try{String requestBody = URLEncoder.encode("service","UTF-8")+"="+URLEncoder.encode(serviceUrl,"UTF-8");stResult = doPost(tgt,requestBody,200);}catch (Exception e){logger.error(e.getMessage(),e);throw new Exception("连接失败");}if(StringUtils.isEmpty(stResult)){throw new Exception("连接失败");}// 3、需要访问的目标地址和ST拼接成完整的url即可return serviceUrl+"?ticket="+stResult;}/*** 将html中的TGT解析出来,html的格式如下* <!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">* <html>*     <head>*         <title>201 Created</title>*     </head>*     <body>*         <h1>TGT Created</h1>*         <form action="http://127.0.0.1:8080/cas/v1/tickets/TGT-2974-4Mki7nXCTz7Q9mQ9CZRbjF9k9WaZCc9bwR42VR46aMchxmS24r" method="POST">Service:*             <input type="text" name="service" value="">*             <br>*             <input type="submit" value="Submit">*         </form>*     </body>* </html>* @param htmlStr* @return*/private String parseHtmlForTgt(String htmlStr){// 因为字符串比较短,所以直接使用正则解析,速度也不会很慢(也可以使用其他方式)String regex = ".*action=\"(.*)\" method=.*";Pattern pattern=Pattern.compile(regex);Matcher m=pattern.matcher(htmlStr);if(m.find()){return m.group(1);}else{return "";}}private String doPost(String url,String requestBody,int successCode){if(url.startsWith("http:")){return doHttpPost(url, requestBody,successCode);}else if(url.startsWith("https:")){return doHttpsPost(url, requestBody,successCode);}else{logger.info("url不合法:"+url);return "";}}private String doHttpPost(String url, String requestBody,int successCode) {OutputStreamWriter out = null;BufferedReader in = null;HttpURLConnection conn = null;StringBuilder result = new StringBuilder();try {logger.info("cas单点登陆 post " + url);URL realUrl = new URL(url);conn = (HttpURLConnection) realUrl.openConnection();conn.setRequestMethod("POST");//发送POST请求必须设置为trueconn.setDoOutput(true);conn.setDoInput(true);conn.setRequestProperty("Accept", "*/*");conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");out = new OutputStreamWriter(conn.getOutputStream());out.write(requestBody);out.flush();if (successCode == conn.getResponseCode()) {in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));String line;while ((line = in.readLine()) != null) {result.append(line);}logger.info(url + " recv " + result.toString());} else {logger.error("recv - {}", "调用失败");logger.error("recv - {}", conn.getResponseCode());logger.error("recv - {}", conn.getResponseMessage());}} catch (ProtocolException e) {logger.error(e.getMessage(), e);} catch (MalformedURLException e) {logger.error(e.getMessage(), e);} catch (IOException e) {logger.error(e.getMessage(), e);} finally {try {if (out != null) {out.close();}if (in != null) {in.close();}if (conn != null) {conn.disconnect();}} catch (IOException e) {logger.error(e.getMessage(), e);}}return result.toString();}private String doHttpsPost(String url, String requestBody,int successCode) {OutputStreamWriter out = null;BufferedReader in = null;HttpsURLConnection conn = null;StringBuilder result = new StringBuilder();try {logger.info("cas单点登陆 post " + url);SSLContext sc = SSLContext.getInstance("SSL");sc.init(null, new TrustManager[]{new TrustAnyTrustManager()}, new java.security.SecureRandom());URL realUrl = new URL(url);conn = (HttpsURLConnection) realUrl.openConnection();conn.setRequestMethod("POST");//发送POST请求必须设置为trueconn.setDoOutput(true);conn.setDoInput(true);conn.setRequestProperty("Accept", "*/*");conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");conn.setSSLSocketFactory(sc.getSocketFactory());conn.setHostnameVerifier(new TrustAnyHostnameVerifier());out = new OutputStreamWriter(conn.getOutputStream());out.write(requestBody);out.flush();if (successCode == conn.getResponseCode()) {in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));String line;while ((line = in.readLine()) != null) {result.append(line);}logger.info(url + " recv " + result.toString());} else {logger.error("recv - {}", "调用失败");logger.error("recv - {}", conn.getResponseCode());logger.error("recv - {}", conn.getResponseMessage());}} catch (ProtocolException e) {logger.error(e.getMessage(), e);} catch (MalformedURLException e) {logger.error(e.getMessage(), e);} catch (IOException e) {logger.error(e.getMessage(), e);} catch (NoSuchAlgorithmException e) {logger.error(e.getMessage(), e);} catch (KeyManagementException e) {logger.error(e.getMessage(), e);} finally {try {if (out != null) {out.close();}if (in != null) {in.close();}if (conn != null) {conn.disconnect();}} catch (IOException e) {logger.error(e.getMessage(), e);}}return result.toString();}private static class TrustAnyTrustManager implements X509TrustManager {@Overridepublic void checkClientTrusted(X509Certificate[] chain, String authType) {}@Overridepublic void checkServerTrusted(X509Certificate[] chain, String authType) {}@Overridepublic X509Certificate[] getAcceptedIssuers() {return new X509Certificate[]{};}}private static class TrustAnyHostnameVerifier implements HostnameVerifier {@Overridepublic boolean verify(String hostname, SSLSession session) {return true;}}

CAS使用账号密码实现单点登陆相关推荐

  1. Springboot 整合SpringSecurity实现账号密码+手机验证码登陆

    Springboot 整合SpringSecurity实现账号密码+手机验证码登陆 示例说明 版本 示例安装 Spring-security 介绍 为什么不用 shiro Spring-Securit ...

  2. 光猫超级管理员账号密码和Telnet登陆

    文章目录 三大运营商默认的超级管理员账号密码 移动 联通 电信 动态密码如何处理 初始化前的准备和初始化后的注册 打开Telnet端口 为user用户提权 公网地址 如何判断运营商是否分配了公网地址 ...

  3. php java 单点登录_用cas来实现php的单点登陆

    最近项目中需要做单点登录,客户端包含Java.ruby和PHP,java有几个应用程序,php是discuz+supesite+ucenter,配置步骤如下: 1.cas服务端:下载地址:http:/ ...

  4. 集成基于CAS协议的单点登陆

    相信大家对单点登陆(SSO,Single Sign On)这个名词并不感到陌生吧?简单地说,单点登陆允许多个应用使用同一个登陆服务.一旦一个用户登陆了一个支持单点登陆的应用,那么在进入其它使用同一单点 ...

  5. CAS单点登陆原理简介及环境搭建

    前言 最近这几天在研究CAS 今天终于在本地部署成功了 今天写一篇文章记录下 原理简介 SSO单点登录 在多个相互信任的系统中,用户只需要登录一次就可以访问其他受信任的系统. 新浪微博与新浪博客是相互 ...

  6. nodebb接入已有的账号体系及实现单点登陆、更改nodebb样式及页面

    一.前言 首先,当接到这个实现nodebb单点登陆这个功能需求时,自己还不太了解单点登陆的概念或者说过程原理.所以就只能一步一步入手,从接入自己的账号体系,覆盖已有的登陆体系开始. 二.接入自己的账号 ...

  7. cas单点登陆。就这一篇就够了!!!!!

    前言: cas是什么我就不累赘说了.就简单说下大致的流程.首先,cas是一个独立的项目.就是一个war包,部署在tomcat上面启动就ok.然后我们要实现单点登陆,无疑是访问系统1,如果没有登录,就跳 ...

  8. 单点登陆框架CAS的研究

    CAS作为开源的单点登陆框架已经非常的流行了.由于它对已有系统的入侵性小,支持的语言多,备受广大开发者关注:也是很多公司将之作为单点登陆的首选框架.关于CAS如何搭建的文章,网上已经非常多了,随便搜搜 ...

  9. PHP使用phpCAS对接CAS单点登陆系统

    PHP使用phpCAS对接CAS单点登陆系统 综述 `CAS`单点登陆原理 搭建`CAS SSO`SERVER服务端 下载`phpCAS`客户端 phpCAS客户端配置 PHP开发对接 注意 综述 本 ...

最新文章

  1. VIVO X1手机通过USB连接电脑访问tomcat
  2. html给文字加黑色边框,如何给显示文字加一层黑色边框
  3. 咏南新CS三层开发框架
  4. Angular 依赖注入学习笔记之工厂函数的用法
  5. onnx 测试_ONNX 现场演示教程
  6. netbeans代码提示_NetBeans可用性提示
  7. java 绘制sin函数图像_MATLAB基础学习之三维曲线的绘制
  8. python爬虫更换ip_爬虫务必要改ip吗?
  9. HashTable、HashSet和Dictionary的区别
  10. Java_基础(一)
  11. ACL2021 | CMU和北邮联合提出的DualGCN在基于Aspect的情感分析任务上达到了SOTA
  12. 对于制造企业来说,APS的价值在哪里?
  13. C# 导出Excel 多个Sheet
  14. LM算法+推导+C++代码实践
  15. 完美世界hr给大家的简历修改和面试的建议
  16. ipadpro画流程图_流程图制作软件,这款软件让你5分钟就能搞定流程图
  17. 字节跳动Java工资待遇等级_字节跳动面试题:你的平均薪水是多少?
  18. RedHat下载安装JDK的方法(方法二)
  19. FairyGUI增益BUFF数值改变的显示
  20. 从零开始Android游戏编程(第二版) 前言

热门文章

  1. 微信小程序支付java服务端集成采坑总结
  2. 计算机自考本科英语二可以用什么代替,自考英语二用什么可以代替免考
  3. 根据拼音首字母筛选人名 1
  4. div显示在上层_DIV重叠 如何优先显示(div浮在重叠的div上面)
  5. Python 网易邮箱简单发送邮件
  6. ESB+MDM预置样例测试总结
  7. ANSI编码文件批量转换为UTF-8编码小tips
  8. 关于英语单词单复数转换方法在程序中的实现
  9. 什么是因果?什么是相关?
  10. cf登录游戏只显示服务器的字体,cf辅助灭天大神修复文件字体花样请求解决方法...