【Openfire】网页版的用户注册、登录、修改密码

来源:互联网 发布:解除root 知乎 编辑:程序博客网 时间:2024/04/27 21:32

Openfire客户端的开发是借助Smack提供的方法。Smack可以从官网下载。如果是Openfire3.7.1的版本,建议使用Smack3.2.2与其匹配。官网地址:http://www.igniterealtime.org/downloadServlet?filename=smack/smack_3_2_2.zip

下载之后解压,将里面的4个jar复制到网络工程WEB-INF下的lib文件夹,这样,即可以使用smack的api进行开发。


目标是做出如下样子的工程,在网页版提供用户注册、登录和密码修改的功能。


网络工程的目录结构如下:


web.xml,没什么好说的,仅仅是一些简单的struts2配置,没有其它任何的内容。

<?xml version="1.0" encoding="UTF-8"?><web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"version="2.4"><filter><filter-name>struts2</filter-name><filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class></filter><filter-mapping><filter-name>struts2</filter-name><url-pattern>/*</url-pattern></filter-mapping></web-app>  

DB.java这是在《【Java】利用单例模式、可变参数优化Java操作Mysql数据库、JDBC代码的写作》(点击打开链接)我已经详细说明过的工具类,这里不再多说了。

package tool;import java.sql.*;  import java.util.*;    public class DB {      // 一、单例初始化连接      private Connection con;        // 以下代码,保证该类只能有一个实例      private DB() {          try {              Class.forName("com.mysql.jdbc.Driver");              // 其中test是我们要链接的数据库,user是数据库用户名,password是数据库密码。              // 3306是mysql的端口号,一般是这个              // 后面那串长长的参数是为了防止乱码,免去每次都需要在任何语句都加入一条SET NAMES UTF8              String url = "jdbc:mysql://localhost:3306/openfire?useUnicode=true&characterEncoding=utf8&useOldAliasMetadataBehavior=true";              String user = "root";              String password = "root";              con = DriverManager.getConnection(url, user, password);          } catch (Exception e) {              e.printStackTrace();          }      } // 私有无参构造方法        // 在自己内部定义自己的一个实例,只供内部调用      private static DB db = null;        // 这个类必须自动向整个系统提供这个实例对象      // 这里提供了一个供外部访问本class的静态方法,可以直接访问      public static DB getInstance() {          if (db == null) {          db = new DB();          }          return db;      }        // 二、查询      // 使用SQL查询,查询的结果是一个结果集(视图)      public List<Object[]> getBySql(String sql) {          List<Object[]> result_list = new ArrayList<Object[]>();          try {              PreparedStatement ps = con.prepareStatement(sql);              ResultSet rs = ps.executeQuery();              while (rs.next()) {                  ResultSetMetaData md = rs.getMetaData();                  int columnCount = md.getColumnCount();                  Object[] row_data_set = new Object[columnCount];                  for (int i = 1; i <= columnCount; i++) {                      row_data_set[i - 1] = rs.getObject(i);                  }                  result_list.add(row_data_set);              }              return result_list;          } catch (SQLException e) {              e.printStackTrace();              return null;          }      }        // 查询sql语句带参数的情况      public List<Object[]> getBySql(String sql, Object[] param) {          List<Object[]> result_list = new ArrayList<Object[]>();          try {              PreparedStatement ps = con.prepareStatement(sql);              for (int i = 0; i < param.length; i++) {                  ps.setObject(i + 1, param[i]);              }              ResultSet rs = ps.executeQuery();              while (rs.next()) {                  ResultSetMetaData md = rs.getMetaData();                  int columnCount = md.getColumnCount();                  Object[] row_data_set = new Object[columnCount];                  for (int i = 1; i <= columnCount; i++) {                      row_data_set[i - 1] = rs.getObject(i);                  }                  result_list.add(row_data_set);              }              return result_list;          } catch (SQLException e) {              e.printStackTrace();              return null;          }      }        // 使用SQL查询,查询的结果是唯一      public Object getBySql_result_unique(String sql) {          try {              PreparedStatement ps = con.prepareStatement(sql);              ResultSet rs = ps.executeQuery();              rs.next();              return rs.getObject(1);          } catch (SQLException e) {              e.printStackTrace();              return null;          }      }        // 查询sql语句带参数的情况      public Object getBySql_result_unique(String sql, Object[] param) {          try {              PreparedStatement ps = con.prepareStatement(sql);              for (int i = 0; i < param.length; i++) {                  ps.setObject(i + 1, param[i]);              }              ResultSet rs = ps.executeQuery();              rs.next();              return rs.getObject(1);          } catch (SQLException e) {              e.printStackTrace();              return null;          }      }        // 三、增删改      // insert、update、delete等修改数据库的语句      public void setBySql(String sql) {          try {              PreparedStatement ps = con.prepareStatement(sql);              ps.executeUpdate();          } catch (SQLException e) {              e.printStackTrace();          }      }        // sql语句带参数的情况      public void setBySql(String sql, Object[] param) {          try {              PreparedStatement ps = con.prepareStatement(sql);              for (int i = 0; i < param.length; i++) {                  ps.setObject(i + 1, param[i]);              }              ps.executeUpdate();          } catch (SQLException e) {              e.printStackTrace();          }      }        // 析构函数,中断数据库的连接      protected void finalize() throws Exception {          if (!con.isClosed() || con != null) {              con.close();          }      }      }  

index.jsp也就是如上的效果截图,布局很简单,就三个表单指向相应action,<%@ page isELIgnored="false"%>是为了保证EL表达式,能够在Tomcat5.5使用。

<%@ page language="java" contentType="text/html; charset=utf-8"pageEncoding="utf-8"%><%@ page isELIgnored="false"%><%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><title>index</title></head><body>${msg}<hr><p>用户注册<br><form method="post" action="register">用户名:<input type="text" name="username" /><br>密码:<input type="password" name="password" /><br><input type="submit" value="注册" /></form></p><hr><p>用户登录<br><form method="post" action="login">用户名:<input type="text" name="username" /><br>密码:<input type="password" name="password" /><br><input type="submit" value="登录" /></form></p><hr><p>修改密码<br><form method="post" action="modify_password">用户名:<input type="text" name="username" /><br>旧密码:<input type="password" name="password" /><br>新密码:<input type="password" name="newpassword" /><br><input type="submit" value="修改" /></form></p></body></html>

chat.jsp是用户成功登录,所跳转的页面,很简单。

<%@ page language="java" contentType="text/html; charset=UTF-8"    pageEncoding="UTF-8"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>聊天</title></head><body>欢迎,${sessionScope.username},<a href="logout">登出</a></body></html>

核心关键在于两个,一个是从Openfire源代码中拷贝过来的Blowfish.java,这个文件的详细说明请看《【Openfire】验证用户输入密码是否正确》(点击打开链接)。

这个文件很长,修改的地方,主要是里面关于org.slf4j记录日志的信息的注释掉,然后是对其42-53行的构造方法进行修改,这样以后用到就不用每次都要写代码,在数据库中查询ofproperty中passwordKey所对应的值。修改之后如下:

public Blowfish() {// 改过// hash down the password to a 160bit keyDB db = DB.getInstance();String password = (String) db.getBySql_result_unique("select propValue from ofproperty where name='passwordKey'");MessageDigest digest = null;try {digest = MessageDigest.getInstance("SHA1");digest.update(password.getBytes());} catch (Exception e) {// Log.error(e.getMessage(), e);}// setup the encryptor (use a dummy IV)m_bfish = new BlowfishCBC(digest.digest(), 0);digest.reset();}

最后,所有核心方法都在test.user这个类中。对照struts.xml所配的action:

<?xml version="1.0" encoding="UTF-8"?>  <!DOCTYPE struts PUBLIC          "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"          "http://struts.apache.org/dtds/struts-2.0.dtd"><struts><package name="test" extends="struts-default"><action name="register" class="test.user" method="register"><result name="success" type="redirect">/index.jsp</result></action> <action name="login" class="test.user" method="login"><result name="success" type="redirect">/chat.jsp</result><result name="input" type="redirect">/index.jsp</result></action><action name="logout" class="test.user" method="logout"><result name="success" type="redirect">/index.jsp</result></action><action name="modify_password" class="test.user" method="modify_password"><result name="success" type="redirect">/index.jsp</result></action></package></struts>   
这个user.java的代码如下:

package test;import java.util.Map;import org.jivesoftware.smack.AccountManager;import org.jivesoftware.smack.Connection;import org.jivesoftware.smack.ConnectionConfiguration;import org.jivesoftware.smack.XMPPConnection;import org.jivesoftware.smack.XMPPException;import tool.Blowfish;import tool.DB;import com.opensymphony.xwork2.ActionContext;import com.opensymphony.xwork2.ActionSupport;public class user extends ActionSupport {private static final long serialVersionUID = 5570391705428296402L;private String username;private String password;private String newpassword;private String msg;// 判断输入的密码是否与数据库密码一致private boolean checkPassword(String username, String password) {Blowfish blowfish = new Blowfish();DB db = DB.getInstance();String db_password = (String) db.getBySql_result_unique("select encryptedPassword from ofuser where username=?",new Object[] { username });if (blowfish.decryptString(db_password).equals(password)) {return true;} else {return false;}}// 用户注册public String register() throws XMPPException {DB db = DB.getInstance();if (username.length() == 0) {msg = "用户名不得为空!";return SUCCESS;}if (db.getBySql("select * from ofuser where username=?",new Object[] { username }).size() > 0) {msg = "该用户名已经存在!";} else {ConnectionConfiguration config = new ConnectionConfiguration("127.0.0.1", 5222);Connection connection = new XMPPConnection(config);connection.connect();AccountManager amgr = connection.getAccountManager();amgr.createAccount(username, password);// 会自动使用Openfire的Blowfish编码加密密码。msg = "注册成功!";System.out.println("新用户注册:" + username + ",密码:" + password);connection.disconnect();}return SUCCESS;}// 用户登录public String login() throws XMPPException {if (checkPassword(username, password)) {msg = "登录成功!";ConnectionConfiguration config = new ConnectionConfiguration("127.0.0.1", 5222);Connection connection = new XMPPConnection(config);connection.connect();connection.login(username, password);Map session = ActionContext.getContext().getSession();session.put("username", username);session.put("connection", connection);return SUCCESS;} else {msg = "密码错误!";return INPUT;}}// 用户登出public String logout() {Map session = ActionContext.getContext().getSession();Connection connection = (Connection) session.get("connection");connection.disconnect();session.clear();return SUCCESS;}// 修改密码public String modify_password() throws XMPPException {if (checkPassword(username, password)) {ConnectionConfiguration config = new ConnectionConfiguration("127.0.0.1", 5222);Connection connection = new XMPPConnection(config);connection.connect();connection.login(username, password);// 登录之后才能修改密码connection.getAccountManager().changePassword(newpassword);System.out.println("用户:" + username + ",修改密码从:" + password + ",变为:"+ newpassword);connection.disconnect();msg = "修改成功!";} else {msg = "密码错误!";}return SUCCESS;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}public String getNewpassword() {return newpassword;}public void setNewpassword(String newpassword) {this.newpassword = newpassword;}}

注册、登录、修改密码本来没有什么好说的,因为smack已经提供了一定量的api。

但是,由于smack并没有提供判断用户所输入密码是否正确的方法,只能自己手动到数据库,再配合Blowfish算法查询。如果密码是不正确的,直接利用connect.login方法的话,会直接报错,导致这个网络工程崩溃,try和catch也是没用的。所以在connect.login之前,需要自己查询用户输入的密码是否正确。

再有一点是,如果用户登录正确,那么这个connect对象就应该压入session,跟着他所登录的用户名一起走,为后续的聊天和加好友操作的提供依据,也就是以后的所有动作,最简单就是上面代码的登录,都要使用这个connection来操作,而不是自己再new一个。

1 0
原创粉丝点击