`
denger
  • 浏览: 356775 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

CAS 之 集成RESTful API

阅读更多
     最近因为公司另一款基于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:
<dependency>
	<groupId>org.jasig.cas</groupId>
	<artifactId>cas-server-integration-restlet</artifactId>
	<version>3.4.2.1</version>
	<type>jar</type>
</dependency>

     然后再在 web.xml 中加入:
<servlet>
		<servlet-name>restlet</servlet-name>
		<servlet-class>com.noelios.restlet.ext.spring.RestletFrameworkServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<servlet-mapping>
		<servlet-name>restlet</servlet-name>
		<url-pattern>/v1/*</url-pattern>
	</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 进行登录验证
      
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/

HTTP/1.1 201 Created
Date: Wed, 23 Mar 2011 12:42:52 GMT
Location: http://192.168.41.107:8080/member/v1/tickets/TGT-14-gDOn9hhSYmq3xfeTRNhTAjZgOMdCdyuVNfsuLRs6onNv7fVmmX-cas
Accept-Ranges: bytes
Server: Noelios-Restlet-Engine/1.1.6
Content-Type: text/html;charset=ISO-8859-1
Content-Length: 437

<!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
   
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

HTTP/1.1 200 OK
Date: Wed, 23 Mar 2011 12:48:03 GMT
Accept-Ranges: bytes
Server: Noelios-Restlet-Engine/1.1.6
Content-Type: text/plain;charset=ISO-8859-1
Content-Length: 29

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. 注销用户
  
DengerMacBook:cas-server denger$ curl -i  -X DELETE http://192.168.41.107:8080/member/v1/tickets/TGT-14-gDOn9hhSYmq3xfeTRNhTAjZgOMdCdyuVNfsuLRs6onNv7fVmmX-cas

HTTP/1.1 200 OK
Date: Wed, 23 Mar 2011 12:54:28 GMT
Accept-Ranges: bytes
Server: Noelios-Restlet-Engine/1.1.6
Content-Length: 0
  注销用户就很简单了,直接 SUBMIT DELETE 删除 TGT即可.

  Java代码调用示例:
package cas;

import java.io.IOException;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.PostMethod;


public final class Client
{
  private static final Logger LOG = Logger.getLogger(Client.class.getName());

  private Client()
  {
    // static-only access
  }

  public static String getTicket(final String server, final String username,
      final String password, final String service)
  {
    notNull(server, "server must not be null");
    notNull(username, "username must not be null");
    notNull(password, "password must not be null");
    notNull(service, "service must not be null");

    return getServiceTicket(server, getTicketGrantingTicket(server, username,
        password), service);
  }

  private static String getServiceTicket(final String server,
      final String ticketGrantingTicket, final String service)
  {
    if (ticketGrantingTicket == null)
      return null;

    final HttpClient client = new HttpClient();

    final PostMethod post = new PostMethod(server + "/" + ticketGrantingTicket);

    post.setRequestBody(new NameValuePair[] { new NameValuePair("service",
        service) });

    try
    {
      client.executeMethod(post);

      final String response = post.getResponseBodyAsString();

      switch (post.getStatusCode())
      {
        case 200:
          return response;

        default:
          LOG.warning("Invalid response code (" + post.getStatusCode()
              + ") from CAS server!");
          LOG.info("Response (1k): "
              + response.substring(0, Math.min(1024, response.length())));
          break;
      }
    }

    catch (final IOException e)
    {
      LOG.warning(e.getMessage());
    }

    finally
    {
      post.releaseConnection();
    }

    return null;
  }

  private static String getTicketGrantingTicket(final String server,
      final String username, final String password)
  {
    final HttpClient client = new HttpClient();

    final PostMethod post = new PostMethod(server);

    post.setRequestBody(new NameValuePair[] {
        new NameValuePair("username", username),
        new NameValuePair("password", password) });

    try
    {
      client.executeMethod(post);

      final String response = post.getResponseBodyAsString();

      switch (post.getStatusCode())
      {
        case 201:
        {
          final Matcher matcher = Pattern.compile(".*action=\".*/(.*?)\".*")
              .matcher(response);

          if (matcher.matches())
            return matcher.group(1);

          LOG
              .warning("Successful ticket granting request, but no ticket found!");
          LOG.info("Response (1k): "
              + response.substring(0, Math.min(1024, response.length())));
          break;
        }

        default:
          LOG.warning("Invalid response code (" + post.getStatusCode()
              + ") from CAS server!");
          LOG.info("Response (1k): "
              + response.substring(0, Math.min(1024, response.length())));
          break;
      }
    }

    catch (final IOException e)
    {
      LOG.warning(e.getMessage());
    }

    finally
    {
      post.releaseConnection();
    }

    return null;
  }

  private static void notNull(final Object object, final String message)
  {
    if (object == null)
      throw new IllegalArgumentException(message);
  }

  public static void main(final String[] args)
  {
    final String server = "http://192.168.41.107:8080/member/v1/tickets";
    final String username = "admin";
    final String password = "111111";
    final String service = "http://localhost:8080/service";

    LOG.info(getTicket(server, username, password, service));
  }
}

   参考:https://wiki.jasig.org/display/CASUM/RESTful+API
分享到:
评论
21 楼 WANTAWAY314 2016-03-22  
还有一个问题,ST是根据service生成的,需要把多个应用的service配置成一样的,才能实现一个ST在多个应用中登录? 但是service好像要跟各自应用的ip或者域名有关,生成的ST才可以登录
20 楼 WANTAWAY314 2016-03-22  
你好。。。。。。如,用户中心的后台地址首页是:http://www.google.com.hk/userCenter 则URL为:  http://www.google.com.hk/userCenter?ticket=ST-3-9QkpLsFmCEqIXSVvGH9P-cas 并可进行登录。。。。我获取到了tickets,直接按照你上面说的不行,但是浏览器上service?tickets=ST,可以转到目标地址,,,但是转向后才是  目标url?tickets=ST.........请问下这个是为什么
19 楼 飘渺青衣 2016-01-04  
rryqsh123 写道
final String service = "http://localhost:8080/service";  这是做什么用的?
还有ST也拿到了 但是按照 http://localhost:18080/CAS_BS_Client?ticket=ST-26-iYVAEuSwtaICMcmgbgts-cas01.example.org  登录报错


错误信息:
org.jasig.cas.client.validation.TicketValidationException:
绁ㄦ牴'ST-26-iYVAEuSwtaICMcmgbgts-cas01.example.org'涓嶇鍚堢洰鏍囨湇鍔?


好像ST是有时效的,必须在短时间使用,超时就这个错误了
18 楼 lysunki 2015-08-08  
郑州蘑菇云 写道
你这个文章我看了,也试验成功能够进行登录了,虽然能够登录,但是进入其他系统的话还是需要跳转到cas认证中心的登录界面进行登录?这是怎么回事?

请问有解决吗
17 楼 郑州蘑菇云 2015-05-08  
你这个文章我看了,也试验成功能够进行登录了,虽然能够登录,但是进入其他系统的话还是需要跳转到cas认证中心的登录界面进行登录?这是怎么回事?
16 楼 rryqsh123 2015-03-27  
final String service = "http://localhost:8080/service";  这是做什么用的?
还有ST也拿到了 但是按照 http://localhost:18080/CAS_BS_Client?ticket=ST-26-iYVAEuSwtaICMcmgbgts-cas01.example.org  登录报错


错误信息:
org.jasig.cas.client.validation.TicketValidationException:
绁ㄦ牴'ST-26-iYVAEuSwtaICMcmgbgts-cas01.example.org'涓嶇鍚堢洰鏍囨湇鍔?
15 楼 rryqsh123 2015-03-24  
为什么返回404啊
14 楼 chenhbc 2014-10-06  
hae 写道
经过无数次的失败终于搞定了

curl -i -X  POST -d "username=admin&password=123456&service=http://www.google.com" http://192.168.41.107:8080/cas-server/v1/tickets/

楼主的这个命令也不说明下 害我搞了半天
大家要注意的是:
1: "username=admin&password=123456&service=http://www.google.com"  这个service大家随便写
2: http://192.168.41.107:8080/member/v1/tickets/  这个memeber容易让人误解 member其实是你的CAS服务器,如
http://192.168.41.107:8080/文字cas-server/v1/tickets/ 这样写我相信大家会更明白点
 

 
请教下,我的验证成功了,拿到Ticket了,可是用http://XX.com?ticket=ST-3-9QkpLsFmCEqIXSVvGH9P-cas 跳转过去后说票根无效,求解。
13 楼 renlei413326889 2014-09-18  
感谢博主,您的一系列文章都很有价值~~
12 楼 dphenixiong 2014-08-07  
带上ST后,无效,ST无法识别....
11 楼 冬天秋天 2014-07-10  
magic_wz 写道
请问,我拿到里ST之后,怎么登录不了?


使用这种方式进行浏览器登录:
http://localhost:8090/console/?ticket=ST-21-qn0wsQN9VK0cQ1pfemW9-sso
10 楼 冬天秋天 2014-07-10  
获取ST之后点击链接可以打开浏览器并进行登录,但是浏览器中也有退出登录的按钮,如何在浏览器中获取TGT,完成REST 方式的退出登录呢?
9 楼 magic_wz 2014-01-07  
请问,我拿到里ST之后,怎么登录不了?
8 楼 glassesbamboo 2013-12-23  
xiaokang1582830 写道
  获取成功之后则返回了 ST,这时候对于客户端而言就已经拿到了登录的TIcket, 如果需要在Web中自动登录的话,只需要弹出浏览器,将ST作为 ticket参数传入即可。如,用户中心的后台地址首页是:http://www.google.com.hk/userCenter 则URL为:  http://www.google.com.hk/userCenter?ticket=ST-3-9QkpLsFmCEqIXSVvGH9P-cas 并可进行登录?登录时总是提示票根无效

我也觉得这里的方法是错误的,正如楼主在一开始自己说的,ST一旦验证过用户的身份是合法以后,ST就失效了,这里再带上ST去访问,肯定吃闭门羹啊。
7 楼 chengtianfei 2013-12-05  
service=http://www.google.com
这个service 可以随便写吗,我配置了一个service 他就只能访问这一个,其它的都包错误说票据无效,其它的系统都实现了cas 的客户端,有人回答一下原因吗?
6 楼 hae 2013-08-14  
hae 写道
经过无数次的失败终于搞定了

curl -i -X  POST -d "username=admin&password=123456&service=http://www.google.com" http://192.168.41.107:8080/cas-server/v1/tickets/

楼主的这个命令也不说明下 害我搞了半天
大家要注意的是:
1: "username=admin&password=123456&service=http://www.google.com"  这个service大家随便写
2: http://192.168.41.107:8080/member/v1/tickets/  这个memeber容易让人误解 member其实是你的CAS服务器,如
http://192.168.41.107:8080/cas-server/v1/tickets/ 这样写我相信大家会更明白点
 

5 楼 hae 2013-08-14  
经过无数次的失败终于搞定了

curl -i -X  POST -d "username=admin&password=123456&service=http://www.google.com" http://192.168.41.107:8080/cas-server/v1/tickets/

楼主的这个命令也不说明下 害我搞了半天
大家要注意的是:
1: "username=admin&password=123456&service=http://www.google.com"  这个service大家随便写
2: http://192.168.41.107:8080/member/v1/tickets/  这个memeber容易让人误解 member其实是你的CAS服务器,如
http://192.168.41.107:8080/文字cas-server/v1/tickets/ 这样写我相信大家会更明白点
 
4 楼 bmwcyq520 2013-08-08  
为什么我的返回结果集里面没有这一行呢?
Location: http://192.168.41.107:8080/member/v1/tickets/TGT-14-gDOn9hhSYmq3xfeTRNhTAjZgOMdCdyuVNfsuLRs6onNv7fVmmX-cas 
3 楼 bmwcyq520 2013-08-07  
我怎么返回的是404,是不是需要在restlet-servlet.xml配置什么啊?
2 楼 fogu 2013-03-07  
我访问v1/tickets的时候,返回的是200码,debug的时候发现代码去跳转访问/cas/login接口了,访问完了以后就传回200了,而不是希望的201 create,不知道是什么原因,我用户名和密码也已经传递过去了

相关推荐

Global site tag (gtag.js) - Google Analytics