CAS 之 集成RESTful API

简介: 原文地址: http://denger.iteye.com/blog/973068最近因为公司另一款基于C/S的产品也需要整合到CAS 的 SSO,但是 CAS 本身对于客户端或浏览器而言是基于其COOKIE来存储用户(TGT)Ticket的,所以这时候就需要使用 CAS 的 RestFul API 来进行登录验证,并支持在C/S软件中点击用户头象时打开浏览器并跳转至用户中心,而且这时候在 WEB 上应该是已登录的状态(类似QQ点击自己的头象时,马上就进入到了QQ空间。
最近因为公司另一款基于C/S的产品也需要整合到CAS 的 SSO,但是 CAS 本身对于客户端或浏览器而言是基于其COOKIE来存储用户(TGT)Ticket的,所以这时候就需要使用 CAS 的 RestFul API 来进行登录验证,并支持在C/S软件中点击用户头象时打开浏览器并跳转至用户中心,而且这时候在 WEB 上应该是已登录的状态(类似QQ点击自己的头象时,马上就进入到了QQ空间。) 

     关于CAS的登录验证流程,可以参考“ CAS 之 实现用户注册后自动登录”,这里的RESTful登录验证流程与其大致相似,大体流程为:首先客户端提交用户名、密码、及Service三个参数,如果验证成功便返回用户的TGT(Ticket Granting Ticket)至客户端, 然后客户端再根据 TGT 获取用户的 ST(Service Ticket)来进行验证登录。 故名思意,TGT是用于生成一个新的Ticket(ST)的Ticket,而ST则是提供给客户端用于登录的Ticket,两者最大的区别在于,TGT是用户名密码验证成功之后所生成的Ticket,并且会保存在Server中及Cookie中,而ST则必须是是根据TGT来生成,主要用于登录,并且当登录成功之后 ST 则会失效。 

     CAS本身已经提供了 restlet 的集成包,如果你用的是 maven 的话直接加入,我这里的Cas-server的版本是 3.4.2.1: 
Xml代码   收藏代码
  1. <dependency>  
  2.     <groupId>org.jasig.cas</groupId>  
  3.     <artifactId>cas-server-integration-restlet</artifactId>  
  4.     <version>3.4.2.1</version>  
  5.     <type>jar</type>  
  6. </dependency>  

     然后再在 web.xml 中加入: 
Xml代码   收藏代码
  1. <servlet>  
  2.         <servlet-name>restlet</servlet-name>  
  3.         <servlet-class>com.noelios.restlet.ext.spring.RestletFrameworkServlet</servlet-class>  
  4.         <load-on-startup>1</load-on-startup>  
  5.     </servlet>  
  6.   
  7.     <servlet-mapping>  
  8.         <servlet-name>restlet</servlet-name>  
  9.         <url-pattern>/v1/*</url-pattern>  
  10.     </servlet-mapping>  


    因为使用到了 Restlat 框架,所以还需要依赖几个 jar 包,分别是: 
  •      com.noelios.restlet.ext.servlet.jar
  •      com.noelios.restlet.ext.spring-1.1.0.jar
  •      com.noelios.restlet.jar
  •      org.restlet-1.1.10.jar
  •      org.restlet.ext.spring-1.1.10.jar

   这几个jar已经打包在附件中了,另外 restlet.org 的 maven库中也有,需要的话可以去  maven.restlet.org 上找。
   另外关于 restlet的配置在 cas-server中已经存在在: /WEB-INF/restlet-servlet.xml文件。 

   配置OK之后直接启动Server,下面来进行简单登录验证的测试: 

   1. 提交用户名密码及Service 进行登录验证 
      
Shell代码   收藏代码
  1. DengerMacBook:cas-server denger$ curl -i -X  POST -d "username=admin&password=123456&service=http://www.google.com" http://192.168.41.107:8080/member/v1/tickets/  
  2.   
  3. HTTP/1.1 201 Created  
  4. Date: Wed, 23 Mar 2011 12:42:52 GMT  
  5. Location: http://192.168.41.107:8080/member/v1/tickets/TGT-14-gDOn9hhSYmq3xfeTRNhTAjZgOMdCdyuVNfsuLRs6onNv7fVmmX-cas  
  6. Accept-Ranges: bytes  
  7. Server: Noelios-Restlet-Engine/1.1.6  
  8. Content-Type: text/html;charset=ISO-8859-1  
  9. Content-Length: 437  
  10.   
  11. <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"><html><head><title>201 The request has been fulfilled and resulted in a new resource being created</title></head><body><h1>TGT Created</h1><form action="http://192.168.41.107:8080/member/v1/tickets/TGT-14-gDOn9hhSYmq3xfeTRNhTAjZgOMdCdyuVNfsuLRs6onNv7fVmmX-cas" method="POST">Service:<input type="text" name="service" value=""><br><input type="submit" value="Submit"></form></body></html>  

    在以上Response信息及 Header中可以看到生成的 TGT,接下来是再重新根据 TGT 获取 ST,将请求的 URI 地址就是以上 Header中的 Location地址。 

   2. 根据返回的 TGT 来获取 ST 
   
Shell代码   收藏代码
  1. DengerMacBook:cas-server denger$ curl -i  -X POST -d "service=http://www.google.com" http://192.168.41.107:8080/member/v1/tickets/TGT-14-gDOn9hhSYmq3xfeTRNhTAjZgOMdCdyuVNfsuLRs6onNv7fVmmX-cas  
  2.   
  3. HTTP/1.1 200 OK  
  4. Date: Wed, 23 Mar 2011 12:48:03 GMT  
  5. Accept-Ranges: bytes  
  6. Server: Noelios-Restlet-Engine/1.1.6  
  7. Content-Type: text/plain;charset=ISO-8859-1  
  8. Content-Length: 29  
  9.   
  10. ST-2-lJfQyJMMEnNGnKcglf1d-cas  

    获取成功之后则返回了 ST,这时候对于客户端而言就已经拿到了登录的TIcket, 如果需要在Web中自动登录的话,只需要弹出浏览器,将ST作为 ticket参数传入即可。如,用户中心的后台地址首页是:http://www.google.com.hk/userCenter 则URL为:  http://www.google.com.hk/userCenter?ticket=ST-3-9QkpLsFmCEqIXSVvGH9P-cas 并可进行登录。 当然前提是在该Web应用中需要部署cas-client应用。 

   3. 注销用户 
  
Shell代码   收藏代码
  1. DengerMacBook:cas-server denger$ curl -i  -X DELETE http://192.168.41.107:8080/member/v1/tickets/TGT-14-gDOn9hhSYmq3xfeTRNhTAjZgOMdCdyuVNfsuLRs6onNv7fVmmX-cas  
  2.   
  3. HTTP/1.1 200 OK  
  4. Date: Wed, 23 Mar 2011 12:54:28 GMT  
  5. Accept-Ranges: bytes  
  6. Server: Noelios-Restlet-Engine/1.1.6  
  7. Content-Length: 0  
  注销用户就很简单了,直接 SUBMIT DELETE 删除 TGT即可. 

   Java代码调用示例: 
Java代码   收藏代码
  1. package cas;  
  2.   
  3. import java.io.IOException;  
  4. import java.util.logging.Logger;  
  5. import java.util.regex.Matcher;  
  6. import java.util.regex.Pattern;  
  7.   
  8. import org.apache.commons.httpclient.HttpClient;  
  9. import org.apache.commons.httpclient.NameValuePair;  
  10. import org.apache.commons.httpclient.methods.PostMethod;  
  11.   
  12.   
  13. public final class Client  
  14. {  
  15.   private static final Logger LOG = Logger.getLogger(Client.class.getName());  
  16.   
  17.   private Client()  
  18.   {  
  19.     // static-only access  
  20.   }  
  21.   
  22.   public static String getTicket(final String server, final String username,  
  23.       final String password, final String service)  
  24.   {  
  25.     notNull(server, "server must not be null");  
  26.     notNull(username, "username must not be null");  
  27.     notNull(password, "password must not be null");  
  28.     notNull(service, "service must not be null");  
  29.   
  30.     return getServiceTicket(server, getTicketGrantingTicket(server, username,  
  31.         password), service);  
  32.   }  
  33.   
  34.   private static String getServiceTicket(final String server,  
  35.       final String ticketGrantingTicket, final String service)  
  36.   {  
  37.     if (ticketGrantingTicket == null)  
  38.       return null;  
  39.   
  40.     final HttpClient client = new HttpClient();  
  41.   
  42.     final PostMethod post = new PostMethod(server + "/" + ticketGrantingTicket);  
  43.   
  44.     post.setRequestBody(new NameValuePair[] { new NameValuePair("service",  
  45.         service) });  
  46.   
  47.     try  
  48.     {  
  49.       client.executeMethod(post);  
  50.   
  51.       final String response = post.getResponseBodyAsString();  
  52.   
  53.       switch (post.getStatusCode())  
  54.       {  
  55.         case 200:  
  56.           return response;  
  57.   
  58.         default:  
  59.           LOG.warning("Invalid response code (" + post.getStatusCode()  
  60.               + ") from CAS server!");  
  61.           LOG.info("Response (1k): "  
  62.               + response.substring(0, Math.min(1024, response.length())));  
  63.           break;  
  64.       }  
  65.     }  
  66.   
  67.     catch (final IOException e)  
  68.     {  
  69.       LOG.warning(e.getMessage());  
  70.     }  
  71.   
  72.     finally  
  73.     {  
  74.       post.releaseConnection();  
  75.     }  
  76.   
  77.     return null;  
  78.   }  
  79.   
  80.   private static String getTicketGrantingTicket(final String server,  
  81.       final String username, final String password)  
  82.   {  
  83.     final HttpClient client = new HttpClient();  
  84.   
  85.     final PostMethod post = new PostMethod(server);  
  86.   
  87.     post.setRequestBody(new NameValuePair[] {  
  88.         new NameValuePair("username", username),  
  89.         new NameValuePair("password", password) });  
  90.   
  91.     try  
  92.     {  
  93.       client.executeMethod(post);  
  94.   
  95.       final String response = post.getResponseBodyAsString();  
  96.   
  97.       switch (post.getStatusCode())  
  98.       {  
  99.         case 201:  
  100.         {  
  101.           final Matcher matcher = Pattern.compile(".*action=\".*/(.*?)\".*")  
  102.               .matcher(response);  
  103.   
  104.           if (matcher.matches())  
  105.             return matcher.group(1);  
  106.   
  107.           LOG  
  108.               .warning("Successful ticket granting request, but no ticket found!");  
  109.           LOG.info("Response (1k): "  
  110.               + response.substring(0, Math.min(1024, response.length())));  
  111.           break;  
  112.         }  
  113.   
  114.         default:  
  115.           LOG.warning("Invalid response code (" + post.getStatusCode()  
  116.               + ") from CAS server!");  
  117.           LOG.info("Response (1k): "  
  118.               + response.substring(0, Math.min(1024, response.length())));  
  119.           break;  
  120.       }  
  121.     }  
  122.   
  123.     catch (final IOException e)  
  124.     {  
  125.       LOG.warning(e.getMessage());  
  126.     }  
  127.   
  128.     finally  
  129.     {  
  130.       post.releaseConnection();  
  131.     }  
  132.   
  133.     return null;  
  134.   }  
  135.   
  136.   private static void notNull(final Object object, final String message)  
  137.   {  
  138.     if (object == null)  
  139.       throw new IllegalArgumentException(message);  
  140.   }  
  141.   
  142.   public static void main(final String[] args)  
  143.   {  
  144.     final String server = "http://192.168.41.107:8080/member/v1/tickets";  
  145.     final String username = "admin";  
  146.     final String password = "111111";  
  147.     final String service = "http://localhost:8080/service";  
  148.   
  149.     LOG.info(getTicket(server, username, password, service));  
  150.   }  
  151. }  

   参考: https://wiki.jasig.org/display/CASUM/RESTful+API
目录
相关文章
|
1月前
|
JSON API 数据库
解释如何在 Python 中实现 Web 服务(RESTful API)。
解释如何在 Python 中实现 Web 服务(RESTful API)。
26 0
|
9天前
|
安全 Java API
RESTful API设计与实现:Java后台开发指南
【4月更文挑战第15天】本文介绍了如何使用Java开发RESTful API,重点是Spring Boot框架和Spring MVC。遵循无状态、统一接口、资源标识和JSON数据格式的设计原则,通过创建控制器处理HTTP请求,如示例中的用户管理操作。此外,文章还提及数据绑定、验证、异常处理和跨域支持。最后,提出了版本控制、安全性、文档测试以及限流和缓存的最佳实践,以确保API的稳定、安全和高效。
|
12天前
|
小程序 前端开发 API
小程序全栈开发中的RESTful API设计
【4月更文挑战第12天】本文探讨了小程序全栈开发中的RESTful API设计,旨在帮助开发者理解和掌握相关技术。RESTful API基于REST架构风格,利用HTTP协议进行数据交互,遵循URI、客户端-服务器架构、无状态通信、标准HTTP方法和资源表述等原则。在小程序开发中,通过资源建模、设计API接口、定义资源表述及实现接口,实现前后端高效分离,提升开发效率和代码质量。小程序前端利用微信API与后端交互,确保数据流通。掌握这些实践将优化小程序全栈开发。
|
21天前
|
前端开发 Java API
构建RESTful API:Java中的RESTful服务开发
【4月更文挑战第3天】本文介绍了在Java环境中构建RESTful API的重要性及方法。遵循REST原则,利用HTTP方法处理资源,实现CRUD操作。在Java中,常用框架如Spring MVC简化了RESTful服务开发,包括定义资源、设计表示层、实现CRUD、考虑安全性、文档和测试。通过Spring MVC示例展示了创建RESTful服务的步骤,强调了其在现代Web服务开发中的关键角色,有助于提升互操作性和用户体验。
构建RESTful API:Java中的RESTful服务开发
|
25天前
|
XML JSON 安全
谈谈你对RESTful API设计的理解和实践。
RESTful API是基于HTTP协议的接口设计,通过URI标识资源,利用GET、POST、PUT、DELETE等方法操作资源。设计注重无状态、一致性、分层、错误处理、版本控制、文档、安全和测试,确保易用、可扩展和安全。例如,`/users/{id}`用于用户管理,使用JSON或XML交换数据,提升系统互操作性和可维护性。
18 4
|
27天前
|
安全 API 开发者
构建高效可扩展的RESTful API服务
在数字化转型的浪潮中,构建一个高效、可扩展且易于维护的后端API服务是企业竞争力的关键。本文将深入探讨如何利用现代后端技术栈实现RESTful API服务的优化,包括代码结构设计、性能调优、安全性强化以及微服务架构的应用。我们将通过实践案例分析,揭示后端开发的最佳实践,帮助开发者提升系统的响应速度和处理能力,同时确保服务的高可用性和安全。
27 3
|
1月前
|
缓存 前端开发 API
构建高效可扩展的RESTful API:后端开发的最佳实践
【2月更文挑战第30天】 在现代Web应用和服务端架构中,RESTful API已成为连接前端与后端、实现服务间通信的重要接口。本文将探讨构建一个高效且可扩展的RESTful API的关键步骤和最佳实践,包括设计原则、性能优化、安全性考虑以及错误处理机制。通过这些实践,开发者可以确保API的健壮性、易用性和未来的可维护性。
|
1月前
|
API 开发者 UED
深入探讨RESTful API设计原则及最佳实践
在当今互联网时代,RESTful API已成为各种软件系统之间进行通信的重要方式。本文将从资源定义、URI设计、HTTP方法选择、状态码规范等方面深入探讨RESTful API设计的原则与最佳实践,帮助开发者更好地构建高效、健壮的API。
|
1月前
|
JSON Java API
Springboot项目中如何设计一个规范的统一的Restful API 响应框架?
Springboot项目中如何设计一个规范的统一的Restful API 响应框架?
23 1
|
1月前
|
XML JSON API
通过Flask框架创建灵活的、可扩展的Web Restful API服务
通过Flask框架创建灵活的、可扩展的Web Restful API服务

热门文章

最新文章