天天看點

openfire 綁定第三方賬号 設定密碼編碼格式

最近公司用到android的即時通訊功能。折騰了一天,和同僚決定用xmpp協定openfire來作為IM伺服器。

        安裝,此處省略若幹字。由于安裝比較簡單,網上有很多的安裝資料,找到一篇文章就可以正确的安裝。這裡不多費口舌了。

        安裝完成之後,發現需要在openfire裡面注冊新的賬号才行。直接瞪眼!這不苦逼了,原來的使用者非得要再重新注冊一個賬号才行?這麼做肯定不行。想想,既然這個工具已經如此受到歡迎,不可能有這麼打一個卻低啊。于是就在網上找了一下,還真有呢!!!再次灰常感謝那些編寫openfire的工程師門。

       好了,廢話不多說,直接些方法。

     綁定第三方賬号:

      擴充綁定第三方的賬号主要有兩種方法,網上有很多介紹的。再次做一個備份。雖然是兩種方法,但是感覺都差不過的,都是通過修改一個openfire系統表裡的一個叫做ofProperty的表來實作功能的。(再次小編批注一下,我用的是将openfire的系統安裝到mysql一個第三方的資料庫中,沒有使用openfire自帶的資料庫。這樣的話,即時由于修改錯了參數的話,直接在資料中,将資料的參數update修改回來,也很友善。 要不然,找不到openfire自帶的資料庫,萬一修改錯了參數,就進不去配置界面。那時候,就叫天天不應,叫地地不靈了。 估計也有能夠找到openfire自帶資料在哪裡的,不過小編太懶了,沒去關注這方面的資料。如果有知道的朋友們,可以直接給我留言,分享進步嘛~  我得意滴笑!)

      方法一: 通過配置界面,進入到控制前台。 小編是将openfire服務安裝到自己的機器上,是以位址是: http://localhsot:9090  如果安裝到其他的地方,将localhost換成IP就好了進入到如下界面:

openfire 綁定第三方賬号 設定密碼編碼格式

     不用說,你懂得,使用者密碼全都放上,沒啥也,進入到管理界面。

     進去之後, 左上角那去找   伺服器->伺服器管理器->系統屬性  進入到系統屬性編輯界面。如下圖:

openfire 綁定第三方賬号 設定密碼編碼格式

     然後,拖到最下面,去找添加屬性的配置

 屬性名稱: jdbcProvider.driver  值:com.mysql.jdbc.Driver

 屬性名稱: jdbcProvider.connectionString  值:jdbc:mysql://localhost/同步的資料庫名?user=資料庫使用者名&password=資料庫密碼

 屬性名稱: admin.authorizedJIDs  值:[email protected]              //admin是openfire的管理者賬号,@amy-tang是安裝openfire時填寫的域名,也可以寫成@192.168.0.1即openfire安裝伺服器電腦的IP位址

 屬性名稱: jdbcAuthProvider.passwordSQL  值:SELECT Password FROM Profiles WHERE NickName=?          //Profiles是使用者表,NickName使用者名,Password登入碼。

 屬性名稱: jdbcAuthProvider.passwordType  值:md5                              //選擇的加密方式,可以選擇MD5,plain(明文)或者其他隻要和現有使用者表加密方式一樣就行 

 屬性名稱: jdbcUserProvider.loadUserSQL  值:SELECT NickName,Email FROM Profiles WHERE NickName=?

 屬性名稱: jdbcUserProvider.userCountSQL  值:SELECT COUNT(*) FROM Profiles

 屬性名稱: jdbcUserProvider.allUsersSQL  值:SELECT NickName FROM Profiles

 屬性名稱: jdbcUserProvider.usernameField  值:NickName

 屬性名稱: jdbcUserProvider.nameField  值:NickName

 屬性名稱: jdbcUserProvider.emailField  值:Email

 然後再更新兩個 

屬性名稱:provider.user.className    值:org.jivesoftware.openfire.user.JDBCUserProvider    

屬性名稱:provider.auth.className    值:org.jivesoftware.openfire.auth.JDBCAuthProvider

這樣就好了, 第二種方法就是直接取背景修改sql語句将上面的資料用sql寫入到資料庫中去。 insert 語句在這裡就省去了~  自己寫就好了。直接用工具, 複制+粘貼也能搞定,沒必要非得寫sql語句的。

如果你的資料庫中的密碼用的是标準md5加密,那麼  jdbcAuthProvider.passwordType 設定為md5就好了,如果明文(情況很少,并不是沒有)那就是plain了。

(此處感謝  尋夢  在她的空間部落格裡面找到的這些資料,對我有很大的幫助。 http://blog.sina.com.cn/u/1785328994)

好了,修改完了,重新開機一下openfire重新進入openfire的管理界面。

下面是見證奇迹的時刻:

到登入态,輸入使用者名,密碼  進入。此時,會有兩個情況:

情況一: 能進去,嘛事沒有! 此人必有老天幫助。這個時候你可以 出門鞭炮眼花一起放,敲鑼打鼓慶祝吧,因為你成功了。

情況二: 苦逼小編我就是這樣的情況,進不去,就是說我使用者名密碼不對。(登入背景的使用者名密碼 是 你在上面設定屬性的時候的   屬性名稱: admin.authorizedJIDs  值:[email protected]  的賬号,其他的,就是錯的了,哪怕你是密碼正确呢,因為你沒有這個權利啊。)我用的是md5 編碼啊,怎麼會不對呢。好吧,被鄙視了,确定問題,尋找解決辦法。

我用的方法就是,在背景,将使用者名密碼全都貼出來, 然後修改秘密編碼格式為明文。我将機密後的密碼當做密碼登陸。 修改完了之後,重新開機openfire,激動人心的時刻到了,使用者名密碼,能進去了!!!! 我去, 真的被鄙視了,丫的!!!!!

修改密碼編碼格式:

好了,被鄙視之後,就找到問題所在了。找到問題,解決問題。

在網上找一下,嘿,果然有相關資料。 但是内容總結出來就是一句話:修改源檔案(萬能得開源啊)

好吧,去官網下載下傳源檔案:

下載下傳下來之後,去找org.jivesoftware.openfire.auth這個包裡可以找到JDBCAuthProvider這個類,驗證密碼的函數就是authenticate。一進去,代碼還不少嘞。

/**
 * $Revision: 1116 $
 * $Date: 2005-03-10 20:18:08 -0300 (Thu, 10 Mar 2005) $
 *
 * Copyright (C) 2005-2008 Jive Software. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jivesoftware.openfire.auth;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.jivesoftware.database.DbConnectionManager;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.user.UserAlreadyExistsException;
import org.jivesoftware.openfire.user.UserManager;
import org.jivesoftware.openfire.user.UserNotFoundException;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The JDBC auth provider allows you to authenticate users against any database
 * that you can connect to with JDBC. It can be used along with the
 * {@link HybridAuthProvider hybrid} auth provider, so that you can also have
 * XMPP-only users that won't pollute your external data.<p>
 *
 * To enable this provider, set the following in the system properties:
 * <ul>
 * <li><tt>provider.auth.className = org.jivesoftware.openfire.auth.JDBCAuthProvider</tt></li>
 * </ul>
 *
 * You'll also need to set your JDBC driver, connection string, and SQL statements:
 *
 * <ul>
 * <li><tt>jdbcProvider.driver = com.mysql.jdbc.Driver</tt></li>
 * <li><tt>jdbcProvider.connectionString = jdbc:mysql://localhost/dbname?user=username&password=secret</tt></li>
 * <li><tt>jdbcAuthProvider.passwordSQL = SELECT password FROM user_account WHERE username=?</tt></li>
 * <li><tt>jdbcAuthProvider.passwordType = plain</tt></li>
 * <li><tt>jdbcAuthProvider.allowUpdate = true</tt></li>
 * <li><tt>jdbcAuthProvider.setPasswordSQL = UPDATE user_account SET password=? WHERE username=?</tt></li>
 * </ul>
 *
 * In order to use the configured JDBC connection provider do not use a JDBC
 * connection string, set the following property
 *
 * <ul>
 * <li><tt>jdbcAuthProvider.useConnectionProvider = true</tt></li>
 * </ul>
 *
 * The passwordType setting tells Openfire how the password is stored. Setting the value
 * is optional (when not set, it defaults to "plain"). The valid values are:<ul>
 *      <li>{@link PasswordType#plain plain}
 *      <li>{@link PasswordType#md5 md5}
 *      <li>{@link PasswordType#sha1 sha1}
 *      <li>{@link PasswordType#sha256 sha256}
 *      <li>{@link PasswordType#sha512 sha512}
 *  </ul>
 *
 * @author David Snopek
 */
public class JDBCAuthProvider implements AuthProvider {

	private static final Logger Log = LoggerFactory.getLogger(JDBCAuthProvider.class);

    private String connectionString;

    private String passwordSQL;
    private String setPasswordSQL;
    private PasswordType passwordType;
    private boolean allowUpdate;
    private boolean useConnectionProvider;

    /**
     * Constructs a new JDBC authentication provider.
     */
    public JDBCAuthProvider() {
        // Convert XML based provider setup to Database based
        JiveGlobals.migrateProperty("jdbcProvider.driver");
        JiveGlobals.migrateProperty("jdbcProvider.connectionString");
        JiveGlobals.migrateProperty("jdbcAuthProvider.passwordSQL");
        JiveGlobals.migrateProperty("jdbcAuthProvider.passwordType");
        JiveGlobals.migrateProperty("jdbcAuthProvider.setPasswordSQL");
        JiveGlobals.migrateProperty("jdbcAuthProvider.allowUpdate");

        useConnectionProvider = JiveGlobals.getBooleanProperty("jdbcAuthProvider.useConnectionProvider");

        if (!useConnectionProvider) {
            // Load the JDBC driver and connection string.
            String jdbcDriver = JiveGlobals.getProperty("jdbcProvider.driver");
            try {
               Class.forName(jdbcDriver).newInstance();
            }
            catch (Exception e) {
                Log.error("Unable to load JDBC driver: " + jdbcDriver, e);
                return;
            }
            connectionString = JiveGlobals.getProperty("jdbcProvider.connectionString");
        }

        // Load SQL statements.
        passwordSQL = JiveGlobals.getProperty("jdbcAuthProvider.passwordSQL");
        setPasswordSQL = JiveGlobals.getProperty("jdbcAuthProvider.setPasswordSQL");

        allowUpdate = JiveGlobals.getBooleanProperty("jdbcAuthProvider.allowUpdate",false);

        passwordType = PasswordType.plain;
        try {
            passwordType = PasswordType.valueOf(
                    JiveGlobals.getProperty("jdbcAuthProvider.passwordType", "plain"));
        }
        catch (IllegalArgumentException iae) {
            Log.error(iae.getMessage(), iae);
        }
    }

    public void authenticate(String username, String password) throws UnauthorizedException {
        if (username == null || password == null) {
            throw new UnauthorizedException();
        }
        username = username.trim().toLowerCase();
        if (username.contains("@")) {
            // Check that the specified domain matches the server's domain
            int index = username.indexOf("@");
            String domain = username.substring(index + 1);
            if (domain.equals(XMPPServer.getInstance().getServerInfo().getXMPPDomain())) {
                username = username.substring(0, index);
            } else {
                // Unknown domain. Return authentication failed.
                throw new UnauthorizedException();
            }
        }
        String userPassword;
        try {
            userPassword = getPasswordValue(username);
        }
        catch (UserNotFoundException unfe) {
            throw new UnauthorizedException();
        }
        // If the user's password doesn't match the password passed in, authentication
        // should fail.
        if (passwordType == PasswordType.md5) {
            password = StringUtils.hash(password, "MD5");
        }
        else if (passwordType == PasswordType.sha1) {
            password = StringUtils.hash(password, "SHA-1");
        }
        else if (passwordType == PasswordType.sha256) {
            password = StringUtils.hash(password, "SHA-256");
        }
        else if (passwordType == PasswordType.sha512) {
            password = StringUtils.hash(password, "SHA-512");
        }
        if (!password.equals(userPassword)) {
            throw new UnauthorizedException();
        }

        // Got this far, so the user must be authorized.
        createUser(username);
    }

    public void authenticate(String username, String token, String digest)
            throws UnauthorizedException
    {
        if (passwordType != PasswordType.plain) {
            throw new UnsupportedOperationException("Digest authentication not supported for "
                    + "password type " + passwordType);
        }
        if (username == null || token == null || digest == null) {
            throw new UnauthorizedException();
        }
        username = username.trim().toLowerCase();
        if (username.contains("@")) {
            // Check that the specified domain matches the server's domain
            int index = username.indexOf("@");
            String domain = username.substring(index + 1);
            if (domain.equals(XMPPServer.getInstance().getServerInfo().getXMPPDomain())) {
                username = username.substring(0, index);
            } else {
                // Unknown domain. Return authentication failed.
                throw new UnauthorizedException();
            }
        }
        String password;
        try {
            password = getPasswordValue(username);
        }
        catch (UserNotFoundException unfe) {
            throw new UnauthorizedException();
        }
        String anticipatedDigest = AuthFactory.createDigest(token, password);
        if (!digest.equalsIgnoreCase(anticipatedDigest)) {
            throw new UnauthorizedException();
        }

        // Got this far, so the user must be authorized.
        createUser(username);
    }

    public boolean isPlainSupported() {
        // If the auth SQL is defined, plain text authentication is supported.
        return (passwordSQL != null);
    }

    public boolean isDigestSupported() {
        // The auth SQL must be defined and the password type is supported.
        return (passwordSQL != null && passwordType == PasswordType.plain);
    }

    public String getPassword(String username) throws UserNotFoundException,
            UnsupportedOperationException
    {

        if (!supportsPasswordRetrieval()) {
            throw new UnsupportedOperationException();
        }
        if (username.contains("@")) {
            // Check that the specified domain matches the server's domain
            int index = username.indexOf("@");
            String domain = username.substring(index + 1);
            if (domain.equals(XMPPServer.getInstance().getServerInfo().getXMPPDomain())) {
                username = username.substring(0, index);
            } else {
                // Unknown domain.
                throw new UserNotFoundException();
            }
        }
        return getPasswordValue(username);
    }

    public void setPassword(String username, String password)
            throws UserNotFoundException, UnsupportedOperationException
    {
        if (allowUpdate && setPasswordSQL != null) {
            setPasswordValue(username, password);
        } else { 
            throw new UnsupportedOperationException();
        }
    }

    public boolean supportsPasswordRetrieval() {
        return (passwordSQL != null && passwordType == PasswordType.plain);
    }

    private Connection getConnection() throws SQLException {
        if (useConnectionProvider)
            return DbConnectionManager.getConnection();
        return DriverManager.getConnection(connectionString);
    }

    /**
     * Returns the value of the password field. It will be in plain text or hashed
     * format, depending on the password type.
     *
     * @param username user to retrieve the password field for
     * @return the password value.
     * @throws UserNotFoundException if the given user could not be loaded.
     */
    private String getPasswordValue(String username) throws UserNotFoundException {
        String password = null;
        Connection con = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        if (username.contains("@")) {
            // Check that the specified domain matches the server's domain
            int index = username.indexOf("@");
            String domain = username.substring(index + 1);
            if (domain.equals(XMPPServer.getInstance().getServerInfo().getXMPPDomain())) {
                username = username.substring(0, index);
            } else {
                // Unknown domain.
                throw new UserNotFoundException();
            }
        }
        try {
            con = getConnection();
            pstmt = con.prepareStatement(passwordSQL);
            pstmt.setString(1, username);

            rs = pstmt.executeQuery();

            // If the query had no results, the username and password
            // did not match a user record. Therefore, throw an exception.
            if (!rs.next()) {
                throw new UserNotFoundException();
            }
            password = rs.getString(1);
        }
        catch (SQLException e) {
            Log.error("Exception in JDBCAuthProvider", e);
            throw new UserNotFoundException();
        }
        finally {
            DbConnectionManager.closeConnection(rs, pstmt, con);
        }
        return password;
    }

    private void setPasswordValue(String username, String password) throws UserNotFoundException {
        Connection con = null;
        PreparedStatement pstmt = null;
        if (username.contains("@")) {
            // Check that the specified domain matches the server's domain
            int index = username.indexOf("@");
            String domain = username.substring(index + 1);
            if (domain.equals(XMPPServer.getInstance().getServerInfo().getXMPPDomain())) {
                username = username.substring(0, index);
            } else {
                // Unknown domain.
                throw new UserNotFoundException();
            }
        }
        try {
            con = getConnection();
            pstmt = con.prepareStatement(setPasswordSQL);
            pstmt.setString(2, username);
            if (passwordType == PasswordType.md5) {
                password = StringUtils.hash(password, "MD5");
            }
            else if (passwordType == PasswordType.sha1) {
                password = StringUtils.hash(password, "SHA-1");
            }
            else if (passwordType == PasswordType.sha256) {
                password = StringUtils.hash(password, "SHA-256");
            }
            else if (passwordType == PasswordType.sha512) {
                password = StringUtils.hash(password, "SHA-512");
            }
            pstmt.setString(1, password);
            pstmt.executeQuery();
        }
        catch (SQLException e) {
            Log.error("Exception in JDBCAuthProvider", e);
            throw new UserNotFoundException();
        }
        finally {
            DbConnectionManager.closeConnection(pstmt, con);
        }
        
    }

    /**
     * Indicates how the password is stored.
     */
    @SuppressWarnings({"UnnecessarySemicolon"})  // Support for QDox Parser
    public enum PasswordType {

        /**
         * The password is stored as plain text.
         */
        plain,

        /**
         * The password is stored as a hex-encoded MD5 hash.
         */
        md5,

        /**
         * The password is stored as a hex-encoded SHA-1 hash.
         */
        sha1,
        
        /**
         * The password is stored as a hex-encoded SHA-256 hash.
         */
        sha256,
              
        /**
          * The password is stored as a hex-encoded SHA-512 hash.
          */
        sha512;
   }

    /**
     * Checks to see if the user exists; if not, a new user is created.
     *
     * @param username the username.
     */
    private static void createUser(String username) {
        // See if the user exists in the database. If not, automatically create them.
        UserManager userManager = UserManager.getInstance();
        try {
            userManager.getUser(username);
        }
        catch (UserNotFoundException unfe) {
            try {
                Log.debug("JDBCAuthProvider: Automatically creating new user account for " + username);
                UserManager.getUserProvider().createUser(username, StringUtils.randomString(8),
                        null, null);
            }
            catch (UserAlreadyExistsException uaee) {
                // Ignore.
            }
        }
    }
}
           

好吧,好多,但是代碼看起來很是很不錯的,能看懂。下面就修改了, 都說在源代碼上修改時屠夫的做法,太暴力了,咱也君子一下,新添加一個類,繼承 AuthProvider 這個類 寫一個新的類就好了。

小編我比較懶,就在三個地方,添加了點代碼。

在  authenticate方法中,在

if (passwordType == PasswordType.md5) {
            password = StringUtils.hash(password, "MD5");
        }
        else if (passwordType == PasswordType.sha1) {
            password = StringUtils.hash(password, "SHA-1");
        }
        else if (passwordType == PasswordType.sha256) {
            password = StringUtils.hash(password, "SHA-256");
        }
        else if (passwordType == PasswordType.sha512) {
            password = StringUtils.hash(password, "SHA-512");
           

這些代碼的基礎上,添加了一個自己定義的邊編碼格式,添加完成之後如下,就是最後多了一個判斷:

if (passwordType == PasswordType.md5) {
            password = StringUtils.hash(password, "MD5");
        }
        else if (passwordType == PasswordType.sha1) {
            password = StringUtils.hash(password, "SHA-1");
        }
        else if (passwordType == PasswordType.sha256) {
            password = StringUtils.hash(password, "SHA-256");
        }
        else if (passwordType == PasswordType.sha512) {
            password = StringUtils.hash(password, "SHA-512");
        }else if(passwordType == PasswordType.demo){
        	  password = demoPassword(password);
        }
           

然後,自己寫一個 demoPassword的密碼編碼格式就好了。最後,還要再  public enum PasswordType {……} 枚舉裡面添加你自己寫的那個demo就好了。(果真懶到家了) 完成之後,生成class,(應該有兩個,一個是這個編碼class 還有一個是type枚舉的),然後,将class放到安裝目錄下的Openfire\lib\openfire.jar包中對應的位置就好了。添加的時候,也費率一點勁,想偷懶,結果消耗時間了。用要壓軟體死活就是添加不進去,沒辦法,安裝了一個WinRAR來添加的,再次被鄙視,囧啊~!!!!

将寫的java生成jar包,然後放到openfire.jar裡面,記錄一下路徑。然後,在後天資料庫中,修改一下  ‘provider.auth.className’ 屬性對應的值。 我上面的類名為 ZYJDBCAuthProvider  路徑為 org.zysecondedeveloper.openfire.plugin  是以,我就将值修改為 org.zysecondedeveloper.openfire.plugin.ZYJDBCAuthProvider  

然後,替換點安裝路徑下的openfire  重新開機openfire就好了

安裝完了之後,重新開機。 輸入使用者名密碼, 恩,不錯,這回能進去了。 小得意一下。

再釋出的時候遇到的問題一:

有的時候,自己弄的class檔案放到openfire.jar包中去。當替換掉安裝目錄下的時候,一啟動,就報錯,說是不支援,粗無内容如下:

Unsupported major.minor version 51.0    糾結了一小段時間,發現時javajdk版本不比對。 可以用eclipse 修改一下java的jdk 的編碼格式,解決方法參考:http://www.111cn.net/jsp/Jsp-Servlet/45158.htm 然後重新打成jar包,将class放到openfire.jar裡面就好了。

修改密碼這部分,感謝 hustpzb 的文章http://blog.csdn.net/hustpzb/article/details/7647801

和  hetylei的文章http://hetylei.iteye.com/blog/290519 對我的幫助。

希望這邊文章對大家有幫助。   QQ: 2281879713   有問題可以留言或者qq  

繼續閱讀