1. 程式人生 > >J2EE與DiscuzX3.2的UCenter實現單點登入

J2EE與DiscuzX3.2的UCenter實現單點登入

最近筆者在實現java專案對discuz的整合。在此過程中,查了很多這方面的資料,發現網上並沒有說得比較全面的文章。筆者博取眾長以及自己在此過程中遇到的問題,寫下來供大家參考,希望大家可以在這過程中少走彎路。筆者在此過程中主要參考了https://code.google.com/p/discuz-ucenter-api-for-java/後,感謝作者ping china.大家可以到此下載介面。

第一步:寫好你的配置檔案config.properties。(把這個檔案放J2EE工程的源路徑下就行)     
 UC_API = http://你的discuz地址/uc_server  如http://10.238.11.11/forum/uc_server
 UC_IP = 正常情況下留空即可
 UC_KEY = 123456(與ucenter通訊的金鑰,這裡自己設定,需與discuz ucenter一支)
 UC_APPID = 2  
 UC_CONNECT = 正常情況下留空即可
第二步:在你的web.xml里加上     

<!-- servlet 'disuz_uc_api' 用於接收Discuz UCenter的同步訊息, 此項配置必須有。(可適當調整 load-on-startup引數) -->  
        <servlet>  
            <servlet-name>disuz_uc_api</servlet-name>  
            <servlet-class>com.fivestars.interfaces.bbs.api.UC</servlet-class>  
            <load-on-startup>2</load-on-startup>  
        </servlet>  
        <servlet-mapping>  
            <servlet-name>disuz_uc_api</servlet-name>  
            <url-pattern>/api/uc.php</url-pattern>  
	</servlet-mapping>  

<servlet-class>com.fivestars.interfaces.bbs.api.UC</servlet-class>如果是直接把使用的UC類放在自已的專案中類包名應該根據自已的包名修改。

<url-pattern>/api/uc.php</url-pattern>(這裡一定要配置成/api/加上Ucenter中的介面檔名一樣,如果J2EE專案中用了許可權控制工具如shiro,記得把你的/api/uc.php開放成無需登入也能訪問的url)

第三步:配置ucenter.

        以管理員身份登入-》管理中心-》ucenter-》應用管理。

        填寫以下資訊:應用名稱:你的應用的名稱

        通訊金鑰:你剛在config.properties的UC_KEY (123456)

        是否開啟同步登入:是

        是否接受通知:是

        其他的留空即可。

如下圖所示:



如果出現以下情形,則Ucenter與J2EE專案聯接完成:

 

通訊不成功記得檢視你的許可權的問題,檢視http://ip:<port>/{project}/api/uc.php是否未登入可以訪問,因為我們的專案中用了shiro對許可權進行控制

http://ip:<port>/{project}/api/uc.php未登入是不能訪問的,一直通訊失敗也是找了好久才找到錯誤的原因。

下面是J2EE對Discuz的同步登入測試:(如果在要測試這一步,只需要把以上第一步和第二步完成後就可以測試,不用Ucenter與J2EE通訊成功)

然後我們先試一下登入,以下是測試程式碼。(到這裡還不支援中文,後面會提到)

首先要將裡的原始碼或者jar匯入J2EE專案中。如果不能翻牆的朋友請到這裡下載:《discuz_ucenter_api_for_java.zip》解壓後把以下目錄的中的全部java原始碼放在自己的專案中就行但要注意,上面web.xml中配置的<servlet-class>com.fivestars.interfaces.bbs.api.UC</servlet-class> 要與這裡匯入包一致。


然後就可以java程式碼就呼叫以下的方法就可以完成功能了:

public static String login(String userName,String pwd){ 
       Client e = new Client();
       String result = e.uc_user_login(userName, pwd);
       String $ucsynlogin = "";
       LinkedList<String> rs = XMLHelper.uc_unserialize(result);
       if(rs.size()>0){
            int $uid = Integer.parseInt(rs.get(0));
            String $username = rs.get(1);
            String $password = rs.get(2);
            String $email = rs.get(3);
            if($uid > 0) {
                $ucsynlogin = e.uc_user_synlogin($uid); 
            } else if($uid == -1) {
                System.out.println("使用者不存在,或者被刪除");
            } else if($uid == -2) {
                System.out.println("密碼錯");
            } else {
                System.out.println("未定義");
            }
        }else{
            System.out.println("Login failed");
            System.out.println(result);
        }
        return $ucsynlogin;
}

建一個測試類,把這個方法加進去,然後呼叫此方法,傳引數值使用者名稱和密碼,然後返回的是一個string。string的內容是一段js程式碼。

js程式碼如下:

<script type="text/javascript" src="http://10.238.18.80:8181/api/uc.php?time=1456454359&code=21a8EQSq6G3mCzbecik4DxlsNWfm0icW44qGLPgcnC2N5QyPfu0EtDpkZJ8xMjFSWVhOwJhxwBJCuCDOFTL6YWkYQgelDp5OzqGmqNpJ3CVqw2Eh4xm2IOO2uoVDzxBFWlXGmTWRAamNmHzPbiylTg6fXnvcRA9T0ihk" reload="1"></script>

只要把這段js程式碼輸出到頁面上去就實現了discuz那邊的登入了。你可以先試試把這段js輸入到瀏覽器的位址列。只要把紅色的url複雜到瀏覽器訪問,然後訪問Discuz的首頁就可以看到相應的使用者已經登入了。(下面包括註冊以及登出啊,原理都是一樣,只要建立Client物件,並且對其呼叫相對應的方法就行)

   退出測試程式碼:

public static String logout(){
           Client uc = new Client();
           String $ucsynlogout = uc.uc_user_synlogout();
           System.out.println("退出成功"+$ucsynlogout);
           return $ucsynlogout;
}
此方法也是返回一段js。輸出頁面即可,測試時可以先在瀏覽器位址列輸入。

註冊測試程式碼:

public static void reg(String userName, String password, String email) {
     Client uc = new Client();
     String $returns = uc.uc_user_register(userName, password ,email);
     int $uid = Integer.parseInt($returns);
     if($uid <= 0) {
          if($uid == -1) {
              System.out.print("使用者名稱不合法");
          } else if($uid == -2) {
              System.out.print("包含要允許註冊的詞語");
          } else if($uid == -3) {
              System.out.print("使用者名稱已經存在");
          } else if($uid == -4) {
              System.out.print("Email 格式有誤");
          } else if($uid == -5) {
              System.out.print("Email 不允許註冊");
          } else if($uid == -6) {
              System.out.print("該 Email 已經被註冊");
          } else {
              System.out.print("未定義");
          }
      } else {
          System.out.println("OK:------------------------"+$returns);
      }
  }

到這裡,還只是實現了java專案登入後論壇那邊能自動登入。但論壇那邊登入了,java這邊還沒登入。

因為我們本身的系統的使用者名稱只能是手機號碼,所以關於中文的問題沒有測試過,這裡複製過來,也只是為了整個問題解決方案的完整性。有需要的朋友自行測試。

別急,我們先解決一下中文使用者名稱的問題,其實這個問題https://code.google.com/p/discuz-ucenter-api-for-java/裡已經提到了,並提供瞭解決方案。感謝原作者。但我還是囉嗦一下,因為我感覺說的不夠詳細,大家可能要花去你的寶貴時間去了解。解決方法如下:

        在PHPFuctions.java裡有一個名叫urlencode的方法。
        只要用以下方法過載一下就好
protected String urlencode(String value,String code){
         try {
             return URLEncoder.encode(value,code);
         } catch (UnsupportedEncodingException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
         }
             return null;
}
並把Client.java裡的uc_api_input方法改成如下
public String uc_api_input(String $data) {
 String $s = urlencode(uc_authcode($data+"&agent="+md5("")+"&time="+time(), "ENCODE"            , UC_KEY),"GBK");
     return $s;
}

好!java中文登入問題解決了。

下面是Discuz那邊登入,J2EE方面也登入的解決方法,這裡就需要前面做換Ucenter和J2EE的通訊成功才能完成以下測試了:

我們來看看discuz那邊登入了,java這邊怎麼實現同步登入。

在UC.java裡有一個doanswer的方法。所有同步操作都在這個方法裡完成。

其邏輯是這樣的,UC這個類繼承了HttpServlet。

當ucenter發出通知時,會訪問這個servlet.並執行他的doGet()方法。doGet()呼叫doAnswer().

        String $code = request.getParameter("code");

會傳一個code進來(加密了的)。

然後呼叫$code = new Client().uc_authcode($code, "DECODE");進行解密操作。

新建一個Map,Map<String,String> $get = new HashMap<String, String>();

然後根據解密出來的code的內容封裝這個Map.

呼叫這個方法,parse_str($code, $get); 經過以上操作後,所有同步操作需要的資訊都在map裡,如action。

action代表的是什麼操作,如同步登入,同步退出,修改密碼等。還有其他資訊,如username.登入的使用者名稱。

改密碼時還會傳新密碼過來。所有的這些都包含在這個叫$get的Map裡。然後我們就可以進行我們自己的邏輯操作了。

(以上邏輯不想理解沒關係,你只要知道當你在discuz登入之後,uc.java的doAnswer()方法就會被呼叫,你所需要的一切資訊都包含在$get這個Map裡)

  操作到這裡,你一定想吐槽筆者!不行啊,discuz那邊登入了,java這邊完全沒反應啊!!doAnswer方法沒有被呼叫啊!是的,你還需要到discuz後臺做一些設定。

管理中心->站長->ucenter設定。把ucenter連線方式改為介面方式。然後,你登入discuz時,doAnswer()方法就會被呼叫了。(其他操作如退出也一樣)

 到這裡,我們又遇到了中文的問題了,discuz用中文登入的時候不行啊!是的,這確實是個比較麻煩的問題。

筆者也弄了好久。原因是這樣的:

上面提到過,傳進來的$code是需要uc_authcode()方法進行解密的,當中文的時候解密出來的$code是"".解決方法如下:在Client.java的uc_authcode()方法裡找到這句String $result = $result1.substring(0, $result1.length());

並把它改成:

   String $result= $result1.toString();  
   try{  
      $result=new String($result.getBytes("iso-8859-1"),"GBK");
    }catch (Exception e) {  
       $result = $result1.substring(0, $result1.length());  
       // TODO: handle exception  
    }

這樣還不行,解密出來的code中文部分仍是亂碼。

所以在uc.java的$code = new Client().uc_authcode($code, "DECODE");這句下面還要加上$code = new String($code.getBytes("GBK"),"UTF-8");

可能有人會說直接轉成utf-8不行嗎?為什麼要先轉成gbk呢?如果直接轉成utf-8的話,解密出來的$code是""。

到此中文的問題就得到解決了。筆者下載的是GBK版本的discuz,其他情況自己改一下編碼應該沒問題。

至此,只要我們在doAnswer()方法裡新增我們自己的邏輯程式碼就已經能實現java和discuz的同步了。

另外,有個問題提醒大家,就是在discuz那邊修改密碼的時候,ucenter並不是馬上發出通知的,這可能會引起同步和安全的問題。