一.目标:
实现SSO (Single Sign On) :单点登陆。
一般来说,两个不同系统的整合的难点部分,便是需要解决单点登陆问题。
对于我们已有的WEB应用中的用户,若该用户已经登陆,并通过 联结迁移到JForum页面时,JForum要能够识别该用户已经登陆(不需要二次登陆)才不会让用户感到别扭(对用户来说,就好像使用的是同一个系统似的)。
但由于分属于2个不同的系统,所以它们之间不能共用同一套Session,这就需要使用一种特殊的机制来实现它们之间的互相通信。
好在,JForum为我们考虑到了这一点,它提供了灵活的SSO接口与配置机制,我们只需要简单地实现一个SSO类,同时在JForum的配置文件中加以配置即可。
二.JForum SSO机制的原理
- 当一个用户访问JForum时,JForum便会检查是否配置SSO,如果配置了SSO,JForum便会调用authenticateUser()方法。该方法简单地返回username或null。
- 若返回了一个不为空的username时,JForum将会检查是否匹配JForum数据库的userid。
- 若没有匹配的userid,JForum将动态加以创建
- JForum设置该user为登陆状态
- 若返回了一个null,则设置为“Anonymous”
- 若一个“Anonymous”用户试图访问权限以外的页面,JForum将根据SSO的设置导航到登陆页面,同时传递给一个登陆成功后应该迁移到的地址参数给login页面。
三.JForum SSO的实现
上面,我们大致了解了一下JForum的SSO机制的原理。根据SSO机制的原理,我们已经知道该怎么实现SSO。
1,需要实现一个SSO类,该类需要取得从另外一个系统登陆进来的用户信息。
一种最简单的方法是使用Cookie来实现。让已有的WEB应用在用户登陆时写入Cookie信息,然后JForum的SSO实现类中将该Cookie取出即可。
另外,还可以使用web-service,文件的写入等等方式来实现。反正一个最基本的原则是:用户登陆时写入用户信息,然后在JForum一方能取出相同信息即可。
2,配置JForum。
四.JForum SSO的配置
(1)实现net.jforum.sso接口
编写CookieUserSSO类
package net.jforum.sso;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.http.Cookie;
import net.jforum.ControllerUtils;
import net.jforum.JForumExecutionContext;
import net.jforum.context.RequestContext;
import net.jforum.entities.UserSession;
import net.jforum.util.preferences.ConfigKeys;
import net.jforum.util.preferences.SystemGlobals;
import org.apache.log4j.Logger;
public class CookieUserSSO implements SSO {
static final Logger logger = Logger.getLogger(CookieUserSSO.class.getName());
public String authenticateUser(RequestContext request) {
// login cookie set by my web LOGIN application
Cookie myCookie = ControllerUtils.getCookie("jforumSSOCookieNameUser");
String username = null;
if (myCookie != null) {
try {
username = URLDecoder.decode(myCookie.getValue(), "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
if (myCookie == null || username.trim().equals("")) {
//JForumExecutionContext.setRedirect(SystemGlobals.getValue(ConfigKeys.SSO_REDIRECT));
return null; // no cookie found
}
System.out.println("cookie_name1="+myCookie.getName());
System.out.println("cookie value1="+username);
return username; // return username for jforum
// jforum will use this name to regist database or set in HttpSession
}
public boolean isSessionValid(UserSession userSession, RequestContext request) {
Cookie cookieNameUser = ControllerUtils.getCookie("jforumSSOCookieNameUser"); // cookie
String remoteUser = null;
if (cookieNameUser != null) {
try {
// jforum username
remoteUser = URLDecoder.decode(cookieNameUser.getValue(), "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
if (remoteUser == null && userSession.getUserId() != SystemGlobals.getIntValue(ConfigKeys.ANONYMOUS_USER_ID)) {
// user has since logged out
return false;
} else if (remoteUser != null
&& userSession.getUserId() == SystemGlobals.getIntValue(ConfigKeys.ANONYMOUS_USER_ID)) {
// anonymous user has logged in
return false;
} else if (remoteUser != null && !remoteUser.equals(userSession.getUsername())) {
// not the same user (cookie and session)
return false;
}
return true; // myapp user and forum user the same. valid user.
}
}
(2)修改SystemGlobals.properties中的配置:
修改SystemGlobals.properties文件中的一下属性的内容:
authentication.type = sso(代表启用sso)
sso.implementation = net.jforum.sso.CookieUserSSO(启用sso后每次url跳转都会到这个类)
sso.redirect = http://www.123.com/login.jsp //可根据实际的登录页面地址进行修改
cookie.name.user =jforumUserInfo //web项目中保存的cookie名称,可根据实际情况修改
(3)修改web.xml:
添加自己的servlet:
<servlet>
<servlet-name>cookieServlet</servlet-name>
<servlet-class>net.jforum.CookieServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>cookieServlet</servlet-name>
<url-pattern>/cookieServlet</url-pattern>
</servlet-mapping>
(4)添加cookieServlet.java
package net.jforum;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URLDecoder;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
/**
* @author jianghai
* @version
*/
public class CookieServlet extends HttpServlet
{
/**
* @see javax.servlet.GenericServlet#init(javax.servlet.ServletConfig)
*/
public void init(ServletConfig config) throws ServletException
{
super.init(config);
}
/**
* @see javax.servlet.http.HttpServlet#service(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
*/
public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
String username = request.getParameter("username");
System.out.println(username);
if(StringUtils.isNotEmpty(username)){
//String tt = URLDecoder.decode(username, "utf-8");
//这里不解码了,因为cookie中不能直接保存中文
Cookie cookie = new Cookie("jforumSSOCookieNameUser", username);
cookie.setMaxAge(-1); //负值表示浏览器关闭时cookie被删除,零值则是要删除该Cookie。
cookie.setPath("/");
response.addCookie(cookie);
}else{
Cookie cookie = new Cookie("jforumSSOCookieNameUser", "");
cookie.setMaxAge(0); // delete the cookie.
cookie.setPath("/"); //设置目录为根路径使cookie共享
response.addCookie(cookie);
}
String callback = request.getParameter("callback");
PrintWriter out = null;
try {
response.setCharacterEncoding("utf-8");
out=response.getWriter();
out.print(callback+"([ { name:'John',age:'19'},{ name:'joe',age:'20'}] );");
out.flush();
} catch (IOException e) {
e.printStackTrace();
}finally{
out.close();
}
}
}
(5)处理你自己的web项目的登录和注销逻辑
//退出bbs
function exitSystem(){
window.location.href=('/logout');
var url = "http://127.0.0.1:8080";
url += "/JForum/cookieServlet?username=";
url += "&callback=?";
$.getJSON(url, function(json){
console.info(json[0].name);
});
}
//登录bbs
function loginBBS(){
var username = $("#userName").val();//获取登录名
var url = "http://127.0.0.1:8080";
url += "/JForum/cookieServlet?username="+encodeURI(encodeURI(username));
url += "&callback=?";
$.getJSON(url, function(json){
console.info(json[0].name);
});
}
这里是通过jsonp技术实现跨越请求