使用django的用户帐号登录openfire
生活随笔
收集整理的這篇文章主要介紹了
使用django的用户帐号登录openfire
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
openfire是一個基于XMPP協議開源的及時通信服務器系統。利用它再加上同樣開源的spark,可以輕松的為你的網站用戶提供一個類似QQ這樣的及時通訊軟件,來擴展網站服務,增加用戶黏度。openfire擁有強大功能的同時還擁有強勁的性能,據稱單臺普通配置的服務器可以支撐1W+的并發。另外它還擁有一個完備的插件系統,可以通過插件實現你需要的任何功能。總之好處多多,務須多言,如果你的網站或者應用系統有IM的需求,openfire是不二選擇。 openfire架構 openfire的安裝配置很簡單,這里就不再贅述。本文要講的內容是將openfire同既有系統結合的第一步:整合用戶系統,即讓網站現有用戶使用自己的帳號進行登錄。由于公司的網站基于django開發,使用的是django自帶的用戶系統,因此本文內容及提供的源代碼適用于所有使用django開發的網站。 本文測試用的客戶端使用的是spark,可以到這里下載。 一、使用外部用戶系統的配置 安裝openfire,啟動服務控制臺,進入管理界面。首次進入管理,會自動進入配置界面,配置很簡單。注意一點,在選擇數據庫時,使用“外部數據庫”,openfire支持常用的數據庫,包括mysql、oracle、mssql等。我使用的是mysql,這樣openfire就會自動在指定的mysql數據庫上建表,并寫入初始數據。 openfire自帶有一套用戶系統,實現了用戶登錄驗證功能,同時還允許通過配置,使用JDBC訪問指定的數據庫,這樣就可以通過配置訪問外部的用戶系統了。最簡單的方式是直接修改數據庫進行配置。 1.在mysql客戶端中打開openfire的數據庫,打開表ofproperty,在里面添加4條記錄:
2.修改name為provider.auth.className的記錄的propValue字段值為:org.jivesoftware.openfire.auth.JDBCAuthProvider 3.重啟openfire控制臺,如果你網站的密碼字段采用的是標準MD5算法(另外也支持明文和SHA算法),那么現在就可以啟動spark使用網站的帳號進行登錄了。 4.在上表添加記錄: admin.authorizedJIDs??? ——??? [username]@[openfire-domain] username是網站用戶表內存在的一個用戶名,openfire-domain是你的openfire的域。這樣再需要登錄管理界面時,使用這個用戶名即可。 二、django用戶系統的問題 是不是很簡單?但如果你跟我一樣,使用django開發網站,并且使用了django提供的用戶系統。那么很遺憾,經過上面的步驟,你依然無法登錄。原因就在于django的密碼字段并不是單純保存了密碼,而是采用了和unix的shadow文件一樣的格式,像下面這樣: sha1$3d5fd$8bf87829f5749c57f8336007c5fc6cbb9cfbb1f3 這個字段里包含了3個信息,即:本密碼的算法、鹽、密文,使用$進行分隔。上面這個JDBCAuthProvider使用的是常規的密碼驗證方法,即: 1.讀取指定用戶名的密碼字段值;
2.使用passwordType指定的算法加密用戶輸入的密碼;
3.比較本次加密的的字符串和讀取出來的密碼字段值是否相等。 由此可以看出,用指定算法加密的密碼不可能和django的用戶表中的密碼字段值相等。 三、搞定它 問題找到了,是JDBCAuthProvider的密碼驗證算法不能滿足要求,那么就從它開刀了。下載openfire的源碼(猛擊這里),用eclipse打開,設置方法請參見這里,很nice的一篇文章,清晰、翔實、可操作性強,我這個java門外漢照著步驟做都很快搞定了,只提醒初學者一點,不要把你的源碼放在名字有中文的目錄下——高手請無視:)。 openfire源碼很贊,這里就不再多說了。在org.jivesoftware.openfire.auth這個包里可以找到JDBCAuthProvider這個類,驗證密碼的函數就是authenticate。 那么,我們修改它吧?呃,這樣子似乎有點太暴力了,另外,如果將來官方在新版本里修改了這個類,就杯具了…… 正確的做法:新建一個類,像JDBCAuthProvider一樣實現AuthProvider接口的相關方法,這樣子驗證函數就隨便你怎么折騰了,下面是我的類,你可以拿去直接使用: package org.firefishsoftware.openfire.plugin; import org.jivesoftware.openfire.user.UserAlreadyExistsException;
import org.jivesoftware.openfire.user.UserManager;
import org.jivesoftware.openfire.user.UserNotFoundException;?
import org.jivesoftware.openfire.auth.*;?
import org.jivesoftware.openfire.auth.JDBCAuthProvider.PasswordType;
import org.jivesoftware.openfire.XMPPServer;?
import org.jivesoftware.util.JiveGlobals;?
import org.jivesoftware.util.Log;?
import org.jivesoftware.util.StringUtils;?
import org.jivesoftware.database.DbConnectionManager;?
import java.io.UnsupportedEncodingException;
import java.sql.*;?
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; /**
* Created by firefish
* Date: 2010-5-6
* Time: 11:06
* 仿照JDBCAuthProvider
*/?
public class DjangoAuthProvider implements AuthProvider {?
??? private String connectionString; private String passwordSQL;
??? private String userIdSQL;
??? private PasswordType passwordType;
??? private boolean useConnectionProvider; /**
???? * Constructs a new JDBC authentication provider.
???? */
??? public DjangoAuthProvider() {
??????? // Convert XML based provider setup to Database based
??????? JiveGlobals.migrateProperty("jdbcProvider.driver");
??????? JiveGlobals.migrateProperty("jdbcProvider.connectionString");
??????? JiveGlobals.migrateProperty("djangoAuthProvider.passwordSQL");
??????? JiveGlobals.migrateProperty("djangoAuthProvider.userIdSQL");
??????? 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("djangoAuthProvider.passwordSQL");
??????? userIdSQL = JiveGlobals.getProperty("djangoAuthProvider.userIdSQL");
??????? passwordType = PasswordType.sha1;
??? } 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();
??????? } String[] passwordData = userPassword.split("\\$");
??????? if (passwordData.length != 3){
??????????? Log.info("無效的密碼數據:"+userPassword);
??????????? throw new UnauthorizedException();
??????? }
??????? // If the user's password doesn't match the password passed in, authentication
??????? // should fail.
??????? MessageDigest digst;
??????? try {
??????????? digst = MessageDigest.getInstance("SHA-1");
??????????? digst.update(passwordData[1].getBytes("UTF8"));??
??????????? digst.update(password.getBytes("UTF8"));??
??????????? password = StringUtils.encodeHex(digst.digest());???????
??????? } catch (NoSuchAlgorithmException e) {
??????????? Log.info("加密失敗:"+e.toString());
??????? } catch (UnsupportedEncodingException e) {
??????????? Log.info("加密失敗:"+e.toString());
??????? }?? if (!password.equals(passwordData[2])) {
??????????? 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
??? {
} 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
??? {
??? } 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; 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;
??? } /**
???? * 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("DjangoAuthProvider: Automatically creating new user account for " + username);
??????????????? UserManager.getUserProvider().createUser(username, StringUtils.randomString(8),
??????????????????????? null, null);
??????????? }
??????????? catch (UserAlreadyExistsException uaee) {
??????????????? // Ignore.
??????????? }
??????? }
??? }
}? 刪掉了一些不需要代碼,修改了authenticate函數,將讀出來的密碼進行拆分,獲取鹽和密文。將用戶輸入的密碼用sha算法加上鹽進行加密后,比較密文是否相等。簡單起見,本文只實現了sha算法,再做的完善一點可以使用密碼字段中指定的算法進行加密。 最后修改ofproperty表的provider.auth.className記錄對應的值為:org.firefishsoftware.openfire.plugin.DjangoAuthProvider,重啟openfire控制臺,再用客戶端登錄試試! 如果你的密碼采用了自定義的算法進行加密,那么上面的方法同樣適用你。
| name | propValue |
| jdbcProvider.driver | com.mysql.jdbc.Driver |
| jdbcProvider.connectionString | jdbc:mysql://[host]:3306/[database]?user=[user]&password=[pwd] |
| jdbcProvider.passwordSQL | select password from auth_user where username=? |
| jdbcProvider.passwordType | md5 |
2.使用passwordType指定的算法加密用戶輸入的密碼;
3.比較本次加密的的字符串和讀取出來的密碼字段值是否相等。 由此可以看出,用指定算法加密的密碼不可能和django的用戶表中的密碼字段值相等。 三、搞定它 問題找到了,是JDBCAuthProvider的密碼驗證算法不能滿足要求,那么就從它開刀了。下載openfire的源碼(猛擊這里),用eclipse打開,設置方法請參見這里,很nice的一篇文章,清晰、翔實、可操作性強,我這個java門外漢照著步驟做都很快搞定了,只提醒初學者一點,不要把你的源碼放在名字有中文的目錄下——高手請無視:)。 openfire源碼很贊,這里就不再多說了。在org.jivesoftware.openfire.auth這個包里可以找到JDBCAuthProvider這個類,驗證密碼的函數就是authenticate。 那么,我們修改它吧?呃,這樣子似乎有點太暴力了,另外,如果將來官方在新版本里修改了這個類,就杯具了…… 正確的做法:新建一個類,像JDBCAuthProvider一樣實現AuthProvider接口的相關方法,這樣子驗證函數就隨便你怎么折騰了,下面是我的類,你可以拿去直接使用: package org.firefishsoftware.openfire.plugin; import org.jivesoftware.openfire.user.UserAlreadyExistsException;
import org.jivesoftware.openfire.user.UserManager;
import org.jivesoftware.openfire.user.UserNotFoundException;?
import org.jivesoftware.openfire.auth.*;?
import org.jivesoftware.openfire.auth.JDBCAuthProvider.PasswordType;
import org.jivesoftware.openfire.XMPPServer;?
import org.jivesoftware.util.JiveGlobals;?
import org.jivesoftware.util.Log;?
import org.jivesoftware.util.StringUtils;?
import org.jivesoftware.database.DbConnectionManager;?
import java.io.UnsupportedEncodingException;
import java.sql.*;?
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; /**
* Created by firefish
* Date: 2010-5-6
* Time: 11:06
* 仿照JDBCAuthProvider
*/?
public class DjangoAuthProvider implements AuthProvider {?
??? private String connectionString; private String passwordSQL;
??? private String userIdSQL;
??? private PasswordType passwordType;
??? private boolean useConnectionProvider; /**
???? * Constructs a new JDBC authentication provider.
???? */
??? public DjangoAuthProvider() {
??????? // Convert XML based provider setup to Database based
??????? JiveGlobals.migrateProperty("jdbcProvider.driver");
??????? JiveGlobals.migrateProperty("jdbcProvider.connectionString");
??????? JiveGlobals.migrateProperty("djangoAuthProvider.passwordSQL");
??????? JiveGlobals.migrateProperty("djangoAuthProvider.userIdSQL");
??????? 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("djangoAuthProvider.passwordSQL");
??????? userIdSQL = JiveGlobals.getProperty("djangoAuthProvider.userIdSQL");
??????? passwordType = PasswordType.sha1;
??? } 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();
??????? } String[] passwordData = userPassword.split("\\$");
??????? if (passwordData.length != 3){
??????????? Log.info("無效的密碼數據:"+userPassword);
??????????? throw new UnauthorizedException();
??????? }
??????? // If the user's password doesn't match the password passed in, authentication
??????? // should fail.
??????? MessageDigest digst;
??????? try {
??????????? digst = MessageDigest.getInstance("SHA-1");
??????????? digst.update(passwordData[1].getBytes("UTF8"));??
??????????? digst.update(password.getBytes("UTF8"));??
??????????? password = StringUtils.encodeHex(digst.digest());???????
??????? } catch (NoSuchAlgorithmException e) {
??????????? Log.info("加密失敗:"+e.toString());
??????? } catch (UnsupportedEncodingException e) {
??????????? Log.info("加密失敗:"+e.toString());
??????? }?? if (!password.equals(passwordData[2])) {
??????????? 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
??? {
} 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
??? {
??? } 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; 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;
??? } /**
???? * 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("DjangoAuthProvider: Automatically creating new user account for " + username);
??????????????? UserManager.getUserProvider().createUser(username, StringUtils.randomString(8),
??????????????????????? null, null);
??????????? }
??????????? catch (UserAlreadyExistsException uaee) {
??????????????? // Ignore.
??????????? }
??????? }
??? }
}? 刪掉了一些不需要代碼,修改了authenticate函數,將讀出來的密碼進行拆分,獲取鹽和密文。將用戶輸入的密碼用sha算法加上鹽進行加密后,比較密文是否相等。簡單起見,本文只實現了sha算法,再做的完善一點可以使用密碼字段中指定的算法進行加密。 最后修改ofproperty表的provider.auth.className記錄對應的值為:org.firefishsoftware.openfire.plugin.DjangoAuthProvider,重啟openfire控制臺,再用客戶端登錄試試! 如果你的密碼采用了自定義的算法進行加密,那么上面的方法同樣適用你。
轉載于:https://blog.51cto.com/firefish/312464
總結
以上是生活随笔為你收集整理的使用django的用户帐号登录openfire的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: benet 3.0的构建企业网络视频第二
- 下一篇: 大型门户网站架构设计的可伸缩性