說明
Jasig CAS如何實作RESTful CAS API,讓Application可透過程式向CAS Server取得認證,但這部份的認證不具備Single Sign On的功能,只是單純的帳號/密碼認證。
準備執行環境
除了Maven外其他為測試時會用到的環境
- JDK (Java Development Kit) version 1.6+
- Apache Maven 2.2.1+
- Apache Tomcat 6+
- JA-SIG CAS 3.4.2.1+
下載CAS integration RESTful Library
cas-server-integration-restlet-3.4.3.jar
可在cas-server-3.4.3-release.zip解壓縮後的路徑cas-server-3.4.3\cas-server-integration-restlet下由maven build(mvn clean package -Dmaven.test.skip=true
)取得,另外此路徑cas-server-3.4.3\modules下也可取得這個檔案,此測試由maven build取得。
下載RESTful API 相關 Library,以下jar可由maven.restlet.org取得
com.noelios.restlet.ext.servlet-1.1.10.jar
com.noelios.restlet.ext.spring-1.1.10.jar
com.noelios.restlet-1.1.10.jar
org.restlet.ext.spring-1.1.10.jar
org.restlet-1.1.10.jar
取得Code Generation Library,可能版本有衝突直接download的無法使用
net.sf.cglib.jar
可在Restlet Framework下載restlet-jse-2.0.10.zip後解壓縮可在此restlet-jse-2.0.10\lib\net.sf.cglib_2.2路徑取得
將上述取得的.jar
檔放到Tomcat6\webapps\cas\WEB-INF\lib,.jar
檔有試圖使用其他版本但目前測試結果就這些版本可配置成功。
CAS Server web.xml config Restlet
<!--
CAS RESTful API is configured
-->
<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>
配置完成則啟動Tomcat
Java REST Client Example
此範例由
RESTful API提供,有做些微修改以利測試
import java.io.IOException;
import java.util.logging.Logger;
import java.util.logging.FileHandler;
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;
/**
* An example Java client to authenticate against CAS using REST services.
* Please ensure you have followed the necessary setup found on the wiki.
*
* @author jesse lauren farinacci
* @since 3.4.2
*/
public final class CASHttpclient
{
private static final Logger LOG = Logger.getLogger(CASHttpclient.class.getName());
private CASHttpclient()
{
// 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://localhost:8080/cas/v1/tickets";
final String username = "scott";
final String password = "tiger";
final String service = "http://localhost:8080/CASClient/secured/personal_info.jsp";
try {
FileHandler fileHandler = new FileHandler("CASHttpclient.log");
LOG.addHandler(fileHandler);
} catch (SecurityException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
LOG.info(getTicket(server, username, password, service));
}
}
Compiler & Run CASHttpclient.java
CAS RESTful API驗證流程
測試結果
取得認證成功結果如下,PostMethod.getStatusCode()=200,並取得Ticket line-13
<?xml version="1.0" encoding="x-windows-950" standalone="no"?>
<!DOCTYPE log SYSTEM "logger.dtd">
<log>
<record>
<date>2012-03-15T20:01:24</date>
<millis>1331812884249</millis>
<sequence>0</sequence>
<logger>CASHttpclient</logger>
<level>INFO</level>
<class>CASHttpclient</class>
<method>main</method>
<thread>10</thread>
<message>ST-2-clxIV2vf9M0ylP7z3Pl2-cas</message>
</record>
</log>
另外認證成功後也有request service url,此url為需要CAS 認證的頁面
取得認證失敗結果如下
<?xml version="1.0" encoding="x-windows-950" standalone="no"?>
<!DOCTYPE log SYSTEM "logger.dtd">
<log>
<record>
<date>2012-03-15T20:09:07</date>
<millis>1331813347545</millis>
<sequence>0</sequence>
<logger>CASHttpclient</logger>
<level>WARNING</level>
<class>CASHttpclient</class>
<method>getTicketGrantingTicket</method>
<thread>10</thread>
<message>Invalid response code (400) from CAS server!</message>
</record>
<record>
<date>2012-03-15T20:09:07</date>
<millis>1331813347579</millis>
<sequence>1</sequence>
<logger>CASHttpclient</logger>
<level>INFO</level>
<class>CASHttpclient</class>
<method>getTicketGrantingTicket</method>
<thread>10</thread>
<message>Response (1k):
<html>
<head>
<title>Status page</title>
</head>
<body>
<h3>error.authentication.credentials.bad</h3><p>You can get technical details <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1">here</a>.<br>
Please continue your visit at our <a href="/">home page</a>.
</p>
</body>
</html>
</message>
</record>
<record>
<date>2012-03-15T20:09:07</date>
<millis>1331813347600</millis>
<sequence>2</sequence>
<logger>CASHttpclient</logger>
<level>INFO</level>
<class>CASHttpclient</class>
<method>main</method>
<thread>10</thread>
</record>
</log>
Get CAS User
Jasig CAS 提供以下方式透過Service Ticket 取得CAS User,service為CAS Client URL,ticket為Service Ticket
http://localhost:8080/cas/serviceValidate?service=http://localhost:8080/CASClient/secured/personal_info.jsp&ticket=ST-2-clxIV2vf9M0ylP7z3Pl2-cas
結果為
<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>
<cas:authenticationSuccess>
<cas:user>scott</cas:user>
</cas:authenticationSuccess>
</cas:serviceResponse>
相關設定可參考:
CAS 之 集成RESTful API
CAS RESTful API 開發文件
RESTful API
沒有留言:
張貼留言