CAS使用账号密码实现单点登陆
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使用账号密码实现单点登陆相关推荐
- Springboot 整合SpringSecurity实现账号密码+手机验证码登陆
Springboot 整合SpringSecurity实现账号密码+手机验证码登陆 示例说明 版本 示例安装 Spring-security 介绍 为什么不用 shiro Spring-Securit ...
- 光猫超级管理员账号密码和Telnet登陆
文章目录 三大运营商默认的超级管理员账号密码 移动 联通 电信 动态密码如何处理 初始化前的准备和初始化后的注册 打开Telnet端口 为user用户提权 公网地址 如何判断运营商是否分配了公网地址 ...
- php java 单点登录_用cas来实现php的单点登陆
最近项目中需要做单点登录,客户端包含Java.ruby和PHP,java有几个应用程序,php是discuz+supesite+ucenter,配置步骤如下: 1.cas服务端:下载地址:http:/ ...
- 集成基于CAS协议的单点登陆
相信大家对单点登陆(SSO,Single Sign On)这个名词并不感到陌生吧?简单地说,单点登陆允许多个应用使用同一个登陆服务.一旦一个用户登陆了一个支持单点登陆的应用,那么在进入其它使用同一单点 ...
- CAS单点登陆原理简介及环境搭建
前言 最近这几天在研究CAS 今天终于在本地部署成功了 今天写一篇文章记录下 原理简介 SSO单点登录 在多个相互信任的系统中,用户只需要登录一次就可以访问其他受信任的系统. 新浪微博与新浪博客是相互 ...
- nodebb接入已有的账号体系及实现单点登陆、更改nodebb样式及页面
一.前言 首先,当接到这个实现nodebb单点登陆这个功能需求时,自己还不太了解单点登陆的概念或者说过程原理.所以就只能一步一步入手,从接入自己的账号体系,覆盖已有的登陆体系开始. 二.接入自己的账号 ...
- cas单点登陆。就这一篇就够了!!!!!
前言: cas是什么我就不累赘说了.就简单说下大致的流程.首先,cas是一个独立的项目.就是一个war包,部署在tomcat上面启动就ok.然后我们要实现单点登陆,无疑是访问系统1,如果没有登录,就跳 ...
- 单点登陆框架CAS的研究
CAS作为开源的单点登陆框架已经非常的流行了.由于它对已有系统的入侵性小,支持的语言多,备受广大开发者关注:也是很多公司将之作为单点登陆的首选框架.关于CAS如何搭建的文章,网上已经非常多了,随便搜搜 ...
- PHP使用phpCAS对接CAS单点登陆系统
PHP使用phpCAS对接CAS单点登陆系统 综述 `CAS`单点登陆原理 搭建`CAS SSO`SERVER服务端 下载`phpCAS`客户端 phpCAS客户端配置 PHP开发对接 注意 综述 本 ...
最新文章
- VIVO X1手机通过USB连接电脑访问tomcat
- html给文字加黑色边框,如何给显示文字加一层黑色边框
- 咏南新CS三层开发框架
- Angular 依赖注入学习笔记之工厂函数的用法
- onnx 测试_ONNX 现场演示教程
- netbeans代码提示_NetBeans可用性提示
- java 绘制sin函数图像_MATLAB基础学习之三维曲线的绘制
- python爬虫更换ip_爬虫务必要改ip吗?
- HashTable、HashSet和Dictionary的区别
- Java_基础(一)
- ACL2021 | CMU和北邮联合提出的DualGCN在基于Aspect的情感分析任务上达到了SOTA
- 对于制造企业来说,APS的价值在哪里?
- C# 导出Excel 多个Sheet
- LM算法+推导+C++代码实践
- 完美世界hr给大家的简历修改和面试的建议
- ipadpro画流程图_流程图制作软件,这款软件让你5分钟就能搞定流程图
- 字节跳动Java工资待遇等级_字节跳动面试题:你的平均薪水是多少?
- RedHat下载安装JDK的方法(方法二)
- FairyGUI增益BUFF数值改变的显示
- 从零开始Android游戏编程(第二版) 前言