1、背景
Sametime提供了非常强大的集成能力,可以广泛地扩展在线感知能力。但是要实现企业应用的在线感知能力,首先需要当前用户登陆到Sametime服务器,显然如果通过提供用户名和密码方式登陆Sametime服务器,对客户来说是非常不安全的,Token代替密码登陆的方式,就是客户最好的选择。
2、了解Sametime
IBM Lotus Sametime是第一个既能够提供完整集成、实时服务,又能够满足企业和电子商务所要求的可伸缩性、可管理性和安全性的实时协作平台级产品。
IBM Lotus Sametime包含客户端和服务器应用程序,允许内网或互联网上某一群体的用户进行实时在线的会议协作。IBM Lotus Sametime用户群成员使用协作功能在即时或是预定的会议中开会、交谈或是一同工作,这些协作功能包括:在线感知、聊天、屏幕共享、共享白板以及实时音视频。 可以分为三大类:
即时通信服务:包括在线感知,即时通信和安全会话功能。使用人员列表,Sametime的用户就可以感知到现在谁在线并可以进行交流(或谁在线,但不希望被打扰),发送或接收即时消息,参与一人或多人的会话;
集成服务:Sametime还提供了全面的基于Java、C++的API,客户可以很轻松地把实时协作功能集成到其它应用程序中,如电子交易站点,帮助台,以及销售自动化等的培训信息传递应用。此外,Sametime秉承IBM Lotus一致的技术结构体系,可以实现完全的集成功能,如与IBM其他的产品(如:Domino,iNotes,K-station,WebSphere等)集成,提供其他应用解决方案的实时协作功能;
会议服务:包括共享白板,共享程序和在线文档的功能。Sametime还提供了一个基于服务器的会议中心,用户可以事先安排在线会议,存储会议议程和管理其它会议资料。另外用户还可以根据需要选择记录会议,以便将来的回放。
Lotus Sametime的“在线感知”技术使得已登录到Sametime服务器的成员能够看到所有其他在线用户(已登录用户)。在线用户的名称显示于Lotus Sametime应用程序的“在线列表”中。从这些在线列表中,群体成员可以通过即时消息会话交谈,或是启动即时会议以包含聊天、屏幕共享、白板、提问/回答调查、发送Web页面和音视频协作的能力。
在线列表支持与其他在线用户的即时感知和即时协作,Lotus Sametime服务器中的Lotus Sametime Meeting Center则为群体用户提供了集中的会议场所。在Meeting Center中,用户可以安排特定时间的会议。用户在计划的时间使用Web浏览器访问Lotus Sametime Meeting Center就可以参加会议了。
Lotus Sametime有两种基本的客户端应用程序:IBM Lotus Sametime Connect客户端和IBM Lotus Sametime Meeting Room客户端。Lotus Sametime Connect客户端是一个基于Eclipse的应用程序,包含了在线列表以显示已选取的群体成员中哪些在线。Lotus Sametime Connect 7.5对于第三方开发人员的一个核心价值就是:可以使用基于Eclipse的插件来扩展产品功能。
图 通过插件,让客户端更加丰富
Lotus Sametime Connect 7.5包括记录扩展开发所需API文档的SDK,您可以使用Eclipse的富客户机应用插件开发模型。使用Lotus Sametime Connect客户端,用户可以向群体中其他任何在线用户发送即时消息、启动即时会议以进行协作。
Lotus Sametime支持广播技术,可以允许会议中大量的只浏览用户(听众)来观看少部分用户(演讲人)的动作。广播技术尤其适用于单个人或一小组人向众多听众作报告。听众成员使用单独的只用于接受的Java客户机(即Lotus Sametime Broadcast客户机)来观看广播会议。
每个Lotus Sametime服务器都包含一个IBM Lotus Domino Directory,用于维护组成Lotus Sametime群的所有用户和服务器的信息。Lotus Sametime服务器也能配置为LDAP服务器(包含LDAP目录)的客户端。
Lotus Sametime通过客户应用与Sametime服务器之间的交互来工作。Lotus Sametime服务包括群服务、 会议服务、广播服务、Lotus Domino/Web 应用服务以及音视频服务(由Lotus Sametime多媒体服务提供)。对于Lotus Sametime服务器最主要的管理任务包括:管理目录,保证Lotus Sametime客户机与Sametime服务器的连接,配置Sametime服务,监控服务器。
Lotus Sametime管理员使用基于Web的Sametime管理客户工具,它运行在Web浏览器中,可以通过Lotus Sametime服务器主页上的“管理服务器”链接访问。
Lotus Sametime 7.5包含服务器群集的概念。Lotus Sametime服务器群集:
-增强了服务器的可扩展性和可靠性,以使得Lotus Sametime服务器能够适用于大用户量的需求;
-为Lotus Sametime群服务和会议服务提供了负载均衡和失效接管能力。
对Sametime有一点认识之后,当中我们最感兴趣的还是Sametime的在线感知功能,但要实现Sametime感知的话,要实现的最重要的一步,就是怎么实现WEB客户端的登录。
3.Sametime实现Web登录方式
越来越多的企业应用逐步实现了WEB化,那么这些应用集成Sametime的在线感知功能,需要使用Sametime的STLink的集成方式,这种方式采用如下方式实现登陆:
在WEB页面上执行javascript:
//用户登录
writeSTLinksApplet(username,pasword,false);
显然,我们的用户名和密码都暴露在客户端的HTML源代码里面,安全——客户能答应吗?No.
我们还有其他方法吗?答案是有的,那就是token:
//用户登录
writeSTLinksApplet(loginname,token,true);
那么,客户问你怎么得到Token? know or don't know.
有以下方式,我们可以获取Token。
4、通过用户登录后,得到该用户的Token。
使用Sametime API的StComm.jar包当中的相关方法我们得到了Token,这种方式仍然需要提供用户名和密码,只不过不会在客户的HTML源代码当中出现,而在服务器端执行,在客户端HTML代码当中的看到的仅仅是Token.
完整代码如下:
(需要StComm.jar包支持)
import com.lotus.sametime.community.*;
import com.lotus.sametime.core.comparch.STSession;
import com.lotus.sametime.token.*;
/**
*实现Token的监听期,服务器通过这种方式反馈Token
*/
public class Tokens implements LoginListener,TokenServiceListener{
private String _loginname; //记录用户登录名 ?纾簔hangshan
private String _password;//记录用户的密码
private String _host;//Sametime的服务器地址
//登录用户的全称,例如 uid=zhangshan,cn=users,dc=your.com,dc=com
private String _fullUsername;
private String _token;//生成的Token
private STSession m_session; //和Sametime服务器连接创建的session
private CommunityService m_comm; //和Sametime CommunityService构建的连接
private TokenService m_token; //Sametime TokenService服务
private String errors;
private boolean success;
private boolean stop;
public Tokens(String host)
{
_host=host;
stop=false;
success=false;
}
//用户登录
public void login(String loginname,String password) throws Exception
{
login(loginname,password,10);
}
//用户登录
public void login(String loginname,String password,int waitSeconds) throws Exception
{
_loginname=loginname;
_password=password;
m_session = new STSession(loginname); //创建一个与Sametime通讯的session
m_session.start(); //通讯启用
m_comm=new STBase(m_session);
m_token=new TokenComp(m_session);
//增加TokensService监听器,监听Token反馈的信息
m_token.addTokenServiceListener(this);
int i=0;
//侦听用户的登录情况
m_comm.addLoginListener(this);//增加登录的监听器,监听是否登录成功
//用户登录
m_comm.loginByPassword(_host,_loginname,_password); //用户登录动作
try
{
while(!stop)
{
Thread.sleep(50);
//waitSecond秒钟不能完成登录,自动退出.
if(i++*50>=waitSeconds*1000){
stop=true;
success=false;
}
}
}
catch(Exception e)
{
e.printStackTrace();
}
m_comm.logout();
m_session.stop();
m_session.unloadSession();//释放资源
if(!success)
{
throw new Exception("生成Token异常!");
}
}
public void loggedIn(LoginEvent arg0) {
try
{
//生成Token字符串
m_token.generateToken();
}
catch(Exception e)
{
errors="创建token发生错误!";
e.printStackTrace();
}
}
/*
* 用户登录注销
*/
public void loggedOut(LoginEvent event) {
if(event.getReason()==0)
{
success=true;
}
else
{
System.out.println("登录异常!");
}
stop=true;
}
/**
* 当Token生成的时候,调用该方法,得到Token
**/
public void tokenGenerated(TokenEvent tokenEvent)
{
//获取Token的字符串及相关的用户名全称
_fullUsername=tokenEvent.getToken().getLoginName();
_token=tokenEvent.getToken().getTokenString();
success=true; //告诉程序我们获取了Token了,可以关闭循环了
stop=true;
}
/**
* 当Token失败的时候,调用该方法,通知失败
**/
public void generateTokenFailed(TokenEvent tokenEvent)
{
errors="生成Token发生错误!";
stop=true;
}
public void serviceAvailable(TokenEvent tokenEvent)
{
}
/**
* Token生成之后,得到用户登录名全称
**/
public String getLoginName()
{
return _fullUsername;
}
/**
* Token生成之后,得到用户的Token
**/
public String getToken()
{
return _token;
}
}
通过调用getLoginName()得到登录的完全用户名,以及getToken()得到Token值。
5、通过服务器登录后,查询用户的Token。
这是我们最希望看到的方案。通过构建一个Servlet或者Web Service,只要提供用户名,就可以通过查询获取Token,这种方式也让你担心其安全性,但让我们放心的是并不是所有运行该代码的客户端,都允许得到Token,该客户端需要得到服务器端的的信任。
配置如下:
打开Sametime 服务器的stconfig.nsf
配置CommunityConnectivity的Community Trusted IPS,增加Sametime信任的服务器IP
之后,以下的Servlet程序可以正确获取Token。
完整代码如下:
(需要StComm.jar包支持)
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import com.lotus.sametime.core.comparch.*;
import com.lotus.sametime.core.types.*;
import com.lotus.sametime.community.*;
import com.lotus.sametime.core.util.connection.*;
import com.lotus.sametime.token.SATokenService;
import com.lotus.sametime.token.Token;
import com.lotus.sametime.token.TokenEvent;
import com.lotus.sametime.token.TokenServiceListener;
/*
*
* 实现一个Servlet,通过Servelt来查询用户的Token,也可以实现Web Service来获取登录用户的Token。
*/
public class SametimeServlet extends HttpServlet implements LoginListener, TokenServiceListener
{
/*
*
*
*
*/
public void serviceAvailable(TokenEvent arg0) {
}
// Sametime 的Session
private STSession m_session;
// 作为应用来登录服务器,而不是使用用户名和密码来登录服务器
private ServerAppService m_saService;
//设置sametime服务器
private String host="testsametime.csvw.com";
// 使用这个服务生成Token
private SATokenService m_tokenService;
//查询的用户名
private STUser m_user;
//查询生成的Token
private Token m_token;
private String m_userName;
private boolean success;//表明是否成功登录服务器
public void init(ServletConfig config) throws ServletException {
super.init(config);
// Init Sametime
log("initialize sametime");
initSametime();
// Wait until initialization complete
try {
synchronized (this) {
wait();
}
} catch (InterruptedException e) {
}
log(">> Sametime servlet was initialized successfully");
}
/**
* 初始化Sametime服务器信息
*/
void initSametime() {
success=false;
// Create the session, load components and start it
try {
m_session = new STSession("" + this);
loadComponents();
m_session.start();
} catch (DuplicateObjectException e) {
e.printStackTrace();
}
m_saService = (ServerAppService) m_session
.getCompApi(ServerAppService.COMP_NAME);
m_tokenService = (SATokenService) m_session
.getCompApi(SATokenService.COMP_NAME);
m_tokenService.addTokenServiceListener(this);
// 作为应用登录服务器r
loginToServer(host);
}
private void loadComponents() {
String[] compNames = { "com.lotus.sametime.community.STBase",
"com.lotus.sametime.token.SATokenComp" };
m_session.loadComponents(compNames);
}
void loginToServer(String serverName) {
m_saService.addLoginListener(this);
//设置登录Sametime类似是应用的方式
short loginType = STUserInstance.LT_SERVER_APP;
//创建一个与Sametime服务器的连接
Connection[] connections = { new SocketConnection(8082, 17000)};
m_saService.setConnectivity(connections);
log(">> login to sametime server name is " + serverName);
//以应用的方式登录Sametime服务器,而且不注销
m_saService.loginAsServerApp(serverName, loginType, "SametimeServlet",
null);
}
public void loggedIn(LoginEvent event) {
log(">> loggedIn to Sametime");
success=true;
synchronized (this) {
log(">> wake up call");
notify();
}
}
public void loggedOut(LoginEvent event) {
log("***** loggedOut from Sametime reason = " + event.getReason());
synchronized (this) {
log(">> wake up call");
notify();
}
}
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
doPost(req, res);
}
public void doPost(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
m_userName = req.getParameter("username");
log(">> do post user name is " + m_userName);
//判断服务器是否登录成功,登陆成功才可以执行查询操作
if(success)
{
//对UID需要根据DN组装
m_user = new STUser(new STId(m_userName, m_userName), "", "");
//请求生成Token
m_tokenService.generateToken(m_user);
synchronized (this) {
try {
log(">> waiting...");
//等待服务器返回Token ,10秒超时
wait(10000);
log(">> waked up...");
} catch (InterruptedException e) {
}
}
}
log(">> creating response...");
//返回得到的登录名和Token
createResponse(res);
}
public void tokenGenerated(TokenEvent event) {
log(">> token generated");
//成功获取Token
m_token = event.getToken();
synchronized (this) {
log(">> wake up call");
//通知完成Token生成
notify();
}
}
public void generateTokenFailed(TokenEvent event) {
log("***** Storage request failed = " + event.getReason());
synchronized (this) {
log(">> wake up call");
notify();
}
}
private void createResponse(HttpServletResponse res) throws IOException {
PrintWriter pw = res.getWriter();
if(success)
{
//在
pw.print(m_token.getLoginName()+"|");
pw.print(m_token.getTokenString());
}
}
/*
* 释放资源
*/
public void destroy() {
m_session.stop();
m_session.unloadSession();
super.destroy();
}
}
这个Servlet会通过我们访问如下URL:
SametimeServlet?username=uid=zhangshan,cn=users,dc=your.com,dc=com
返回:
uid=zhangshan,cn=users,dc=your.com,dc=com|(DDFDFdFDDGGDGDGDG)
注:登录名和Token,并且使用”|”分开
总之,两种方式都可以获取Token,但是从性能方面考虑,采用第二种方式,对认证服务的压力小些,如果把这个Servlet配置在Sametime本身的Domino执行的话,更是优秀的方案。有了这个Token,我们可以实现无论,net、PHP、ASP等WEB页面的Sametime的集成,真正实现企业所有WEB应用的在线感知。