您的位置:首页 > 运维架构

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

2016-07-22 10:49 609 查看
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 key
DB 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一个。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: