JForum做為一個成熟的開源BBS論壇解決方案,提供了非常方便的SSO集成接口。它的主頁上和網(wǎng)上都有許多介紹如何用SSO方式進(jìn)行集成的辦法。這里不羅列,google可以找到許多資料,主要描述一下如何解決用戶名重名的一種方式。目前使用的JForum版本是2.1.8
簡單地介紹一下采用的SSO方式。由于應(yīng)用上需要一個BBS,找了JForum做為一個子系統(tǒng),集成到現(xiàn)成的一個管理系統(tǒng)當(dāng)中,管理系統(tǒng)本身有一套完全的身份權(quán)限認(rèn)證方案,由于系統(tǒng)的安全要求不是特別嚴(yán)格,所以采了最直接和最省事的方式:Cookie寫入。即在管理系統(tǒng)登錄時,把用戶信息寫入Cookie,JForum從Cookie中讀取用戶信息進(jìn)行登錄。
為JForum項目添加一個SSO接口的擴(kuò)展類CookieSSO,主要實現(xiàn)authenticateUser(RequestContext request)方法。方法大體如下:
public String authenticateUser(RequestContext request) {
String userId = null;
Cookie c = ControllerUtils.getCookie("ehrbbsuserid");
if(c!=null){
userId = c.getValue();
}
logger.info("單點登錄BBS用戶ID為:" + userId);
return userId;
}
就是從Cookie中讀取在管理系統(tǒng)中放入的username。但是這里寫的是:ehrbbsuserid,主要是這個變量名來區(qū)分傳過來的內(nèi)容的不同。
按照正常的方式就是從管理系統(tǒng)把一個用戶的username傳過來。然后在這里取出,return給調(diào)用方法進(jìn)行驗證。在實際項目中,問題就出在這里了!
舉例說有二個用戶名字都叫:李四,那么當(dāng)二個李四都同時登錄時,JForum的驗證方式就會出問題!它認(rèn)不清到底是哪個李四,數(shù)據(jù)庫查詢時只按第一個來!
仔細(xì)看一下jforum的數(shù)據(jù)庫表設(shè)計,jforum_user這個表沒有display_user_name類似的字段,它就是把username做為顯示在頁面上的用戶名,如果不做集成,把它單獨做為一個BBS時,username既是登錄的用戶名,也是顯示在頁面上的用戶名。主要原因我想大概就是老外的思維方式跟中國人的不太一樣。中國人登錄的時候用英文,顯示的時候還有一個昵稱或是中文名等。
所以把它集成的時候,用Cookie傳送一個username給JForum時就得用中文(而且重名的概率很大)。我們再跟進(jìn)一個它的代碼,看是誰調(diào)用了SSO接口的這個方法:
結(jié)果發(fā)現(xiàn)是類:net.jforum.ControllerUtils的checkSSO(UserSession userSession)方法
/**
* Checks for user authentication using some SSO implementation
* @param userSession UserSession
*/
protected void checkSSO(UserSession userSession)
{
try {
SSO sso = (SSO) Class.forName(SystemGlobals.getValue(ConfigKeys.SSO_IMPLEMENTATION)).newInstance();
String username = sso.authenticateUser(JForumExecutionContext.getRequest());

if (username == null || username.trim().equals("")) {
userSession.makeAnonymous();
}
else {
SSOUtils utils = new SSOUtils();

if (!utils.userExists(username)) {
SessionContext session = JForumExecutionContext.getRequest().getSessionContext();

String email = (String) session.getAttribute(SystemGlobals.getValue(ConfigKeys.SSO_EMAIL_ATTRIBUTE));
String password = (String) session.getAttribute(SystemGlobals.getValue(ConfigKeys.SSO_PASSWORD_ATTRIBUTE));

if (email == null) {
email = SystemGlobals.getValue(ConfigKeys.SSO_DEFAULT_EMAIL);
}

if (password == null) {
password = SystemGlobals.getValue(ConfigKeys.SSO_DEFAULT_PASSWORD);
}

utils.register(password, email);
}

this.configureUserSession(userSession, utils.getUser());
}
}
catch (Exception e) {
e.printStackTrace();
throw new ForumException("Error while executing SSO actions: " + e);
}
}
在這里明顯的,它是通過username去確定這個人是否存在?再繼續(xù)跟進(jìn)代碼就會發(fā)現(xiàn)最后調(diào)用了Dao的selectUserByName方法。
解決的思路:修改JForum的數(shù)據(jù)庫表,并更改代碼和頁面文件是不科學(xué)的,工作量大,而且風(fēng)險比較高!那么就繼續(xù)把username用來保存中文名稱,它的user_id是一個自增的數(shù)字序列。在管理系統(tǒng)的用戶表中,擴(kuò)展一個字段bbs_user_id,用來保存在JForum中的用戶id,這個字段就肯定是唯一的,在管理系統(tǒng)登錄時,把這個bbs_user_id查詢出來,放到Cookie中。在JForum驗證時,不再使用它推薦的返回username方式,而是返回它的user_id值。
那么回到最上面的CookieSSO類的代碼,這里返回的String其實是jforum_user表中user_id字段。為了匹配,那么net.jforum.ControllerUtils的checkSSO(UserSession userSession)方法也要改!改為下面的方式:
protected void checkSSO(UserSession userSession)
{
try {
SSO sso = (SSO) Class.forName(SystemGlobals.getValue(ConfigKeys.SSO_IMPLEMENTATION)).newInstance();
String username = sso.authenticateUser(JForumExecutionContext.getRequest());

if (username == null || username.trim().equals("")) {
userSession.makeAnonymous();
}
else {
// SSOUtils utils = new SSOUtils();
/* 重構(gòu)為按userId驗證身份 */
// if (!utils.userExists(username)) {
// SessionContext session = JForumExecutionContext.getRequest().getSessionContext();
//
// String email = (String) session.getAttribute(SystemGlobals.getValue(ConfigKeys.SSO_EMAIL_ATTRIBUTE));
// String password = (String) session.getAttribute(SystemGlobals.getValue(ConfigKeys.SSO_PASSWORD_ATTRIBUTE));
//
// if (email == null) {
// email = SystemGlobals.getValue(ConfigKeys.SSO_DEFAULT_EMAIL);
// }
//
// if (password == null) {
// password = SystemGlobals.getValue(ConfigKeys.SSO_DEFAULT_PASSWORD);
// }
//
// utils.register(password, email);
// }
/* 新添加的代碼 */
UserDAO dao = DataAccessDriver.getInstance().newUserDAO();
User user = dao.selectById(Integer.parseInt(username));
// this.configureUserSession(userSession, utils.getUser());
this.configureUserSession(userSession, user);
}
}
catch (Exception e) {
e.printStackTrace();
throw new ForumException("Error while executing SSO actions: " + e);
}
}
這樣就可以解決認(rèn)證的問題!同時又保證username可以是中文的,而且重名也無所謂。
附加:查看它的SQL配置文件,發(fā)現(xiàn)有selectUserByName這樣的方法,通過用戶名來查找用戶,起初怕是它在某些模塊中使用了。后來詳細(xì)查看,發(fā)現(xiàn)它只使用在后臺管理(即admin模塊)中的用戶管理。這個頁面提供了一個按用戶名來查找用戶的功能,所以也是非常合理的!
剛進(jìn)場的時候戲就落幕
簡單地介紹一下采用的SSO方式。由于應(yīng)用上需要一個BBS,找了JForum做為一個子系統(tǒng),集成到現(xiàn)成的一個管理系統(tǒng)當(dāng)中,管理系統(tǒng)本身有一套完全的身份權(quán)限認(rèn)證方案,由于系統(tǒng)的安全要求不是特別嚴(yán)格,所以采了最直接和最省事的方式:Cookie寫入。即在管理系統(tǒng)登錄時,把用戶信息寫入Cookie,JForum從Cookie中讀取用戶信息進(jìn)行登錄。
為JForum項目添加一個SSO接口的擴(kuò)展類CookieSSO,主要實現(xiàn)authenticateUser(RequestContext request)方法。方法大體如下:









按照正常的方式就是從管理系統(tǒng)把一個用戶的username傳過來。然后在這里取出,return給調(diào)用方法進(jìn)行驗證。在實際項目中,問題就出在這里了!
舉例說有二個用戶名字都叫:李四,那么當(dāng)二個李四都同時登錄時,JForum的驗證方式就會出問題!它認(rèn)不清到底是哪個李四,數(shù)據(jù)庫查詢時只按第一個來!
仔細(xì)看一下jforum的數(shù)據(jù)庫表設(shè)計,jforum_user這個表沒有display_user_name類似的字段,它就是把username做為顯示在頁面上的用戶名,如果不做集成,把它單獨做為一個BBS時,username既是登錄的用戶名,也是顯示在頁面上的用戶名。主要原因我想大概就是老外的思維方式跟中國人的不太一樣。中國人登錄的時候用英文,顯示的時候還有一個昵稱或是中文名等。
所以把它集成的時候,用Cookie傳送一個username給JForum時就得用中文(而且重名的概率很大)。我們再跟進(jìn)一個它的代碼,看是誰調(diào)用了SSO接口的這個方法:
結(jié)果發(fā)現(xiàn)是類:net.jforum.ControllerUtils的checkSSO(UserSession userSession)方法









































解決的思路:修改JForum的數(shù)據(jù)庫表,并更改代碼和頁面文件是不科學(xué)的,工作量大,而且風(fēng)險比較高!那么就繼續(xù)把username用來保存中文名稱,它的user_id是一個自增的數(shù)字序列。在管理系統(tǒng)的用戶表中,擴(kuò)展一個字段bbs_user_id,用來保存在JForum中的用戶id,這個字段就肯定是唯一的,在管理系統(tǒng)登錄時,把這個bbs_user_id查詢出來,放到Cookie中。在JForum驗證時,不再使用它推薦的返回username方式,而是返回它的user_id值。
那么回到最上面的CookieSSO類的代碼,這里返回的String其實是jforum_user表中user_id字段。為了匹配,那么net.jforum.ControllerUtils的checkSSO(UserSession userSession)方法也要改!改為下面的方式:









































這樣就可以解決認(rèn)證的問題!同時又保證username可以是中文的,而且重名也無所謂。
附加:查看它的SQL配置文件,發(fā)現(xiàn)有selectUserByName這樣的方法,通過用戶名來查找用戶,起初怕是它在某些模塊中使用了。后來詳細(xì)查看,發(fā)現(xiàn)它只使用在后臺管理(即admin模塊)中的用戶管理。這個頁面提供了一個按用戶名來查找用戶的功能,所以也是非常合理的!
剛進(jìn)場的時候戲就落幕