在实际应用或业务中往往需要用户注册以及登录,需要用Swing构造相关的界面以及背后运行的服务器,另外还需要有一个数据结构来保存用户信息
甚至需要链接数据库(MySQL)来进行用户信息的存储和读取
目录
需求分析
1. 一个User类,表示一个用户,内含有用户信息
2. 一个Server类,表示服务器,用于处理用户的操作
- 登录操作,判断用户名是否存在,密码是否正确
- 注册操作,判断注册信息的合法性,注册成功要将一个User类插入到数据库中
3. 一个UI类,用于创建登录和注册界面,并提供交互功能和交互信息
4. 创建一个数据库user_info,用于存储用户信息
需求分析过后就可以按部就班地编写这些模块了
模块编写
User类
这里作为一个学习记录,只创建一个简单的用户类,里面有用户信息:用户名、密码、用户ID、最近登录时间、是否在线。这六个基本的数据结构构成了用户类
public class User {
private String user_name;
private String user_pwd;
private String user_id;
private String user_login_time;
private boolean user_if_online;
}
除此之外,还需要编写构造函数,用于用不同的参数创建用户类
- 在注册时没有最近登录时间,并且在线状态设置为离线
public User(String user_name, String user_pwd, String user_id) {
this.user_name = user_name;
this.user_pwd = user_pwd;
this.user_id = user_id;
this.user_login_time = "";
this.user_if_online = false;
}
还需要注意的是,为了便于用户哈希结构的运算,重写hashCode
public int hashCode() {
return Objects.hash(user_name, user_pwd, user_id);
}
Server类
首先Server类要有一个能够存储用户信息的数据结构,考虑到用户数量可能相当庞大,为搜索性能考虑,采用哈希表的结构,以用户名作Key,User类作Value
HashMap<String, User> user_info = new HashMap<>();
每次运行Server时都会创建新的HashMap,而之前注册和登录过的用户信息就不能够保存,为了提供非易失性,这里链接一个存储用户信息的数据库,每次运行都从数据库中读取用户信息,这里为了方便测试,直接连接本地的数据库
public Connection connection;
public Statement statement;
public ResultSet rs_query;
public int rs_update;
String dbURL = "jdbc:mysql://localhost:3306/user_info";
String name_sql = "root";
String pwd_sql = "...";
在server的构造函数中,加入连接数据库并且读取相关数据的方法块
值得注意的是,这里不要在读取完后就关闭连接,因为后续用户操作还可能会更新数据库,这样可以避免重复的连接操作,相当于长连接
Server() throws SQLException {
String sql = "select * from user_info";
connection = DriverManager.getConnection(dbURL, name_sql, pwd_sql);
statement = connection.createStatement();
rs_query = statement.executeQuery(sql);
while (rs_query.next()) {
String user_num = rs_query.getString("user_num");
String user_name = rs_query.getString("user_name");
String user_pwd = rs_query.getString("user_pwd");
String user_id = rs_query.getString("user_id");
String user_login_time = rs_query.getString("user_login_time");
boolean user_if_online = rs_query.getBoolean("user_if_online");
System.out.println(user_num+" "+user_name+" "+user_pwd+" "+user_id+" "+user_login_time+""+user_if_online);
user_info.put(user_name,new User(user_name,user_pwd,user_id,user_login_time,user_if_online));
}
rs_query.close();
//statement.close();
//connection.close();
}
server需要提供登录服务,并且对用户名和密码进行判断,所以写上login方法
login有两个参数,分别是用户名和密码,在哈希表中查找是否有这个用户,如果没有则返回响应错误信息,如果有则判断用户是否在线以及密码是否正确
需要注意在判断用户登陆成功前,需要将用户的登录信息上传到数据库,更新最近登录时间以及在线状态
public int login(String name, String pwd) throws SQLException {
User user = user_info.get(name);
if (user == null) {
return USER_LOGIN_USERNAME_NOT_EXIST;
}
if (user.getUser_pwd().equals(pwd)) {
if (user.isUser_if_online()) {
return USER_LOGIN_USER_ONLINE;
}
user.setUser_if_online(true);
//获取登录时间
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String datetime = df.format(new Date());
user.setUser_login_time(datetime);
String sql_update = "update user_info set user_if_online = true,user_login_time = now() where user_name='"+name+"'";
System.out.println(sql_update);
rs_update=statement.executeUpdate(sql_update);
//statement.close();
//connection.close();
return USER_LOGIN_SUCCESS;
}
return USER_LOGIN_PASSWORD_ERROR;
}
server提供注册服务与登录服务类似,这里不做过多解释,只需要进行判断并且成功后将新用户的信息添加到数据库中即可
public int register(String name, String pwd, String repwd,String id) throws SQLException {
//用户名长度不合规
if (name.length() < 5 || name.length() > 12) {
return USER_REGISTER_FAIL_WRONG_NAME_LENGTH;
}
//用户名存在空格
if (name.contains(" ")) {
return USER_REGISTER_FAIL_CONTAIN_SPACE;
}
User user = user_info.get(name);
//用户不存在
if (user != null) {
return USER_REGISTER_USERNAME_EXIST;
}
//密码长度不合规
if (pwd.length() < 6 || pwd.length() > 20) {
return USER_REGISTER_FAIL_WRONG_PWD_LENGTH;
}
if(!pwd.equals(repwd)){
return USER_REGISTER_FAIL_WRONG_REPWD;
}
else {
user = new User(name, pwd, id);
user_info.put(name, user);
connection = DriverManager.getConnection(dbURL, name_sql, pwd_sql);
statement = connection.createStatement();
String sql_insert = "insert into user_info(user_name, user_pwd, user_id, user_if_online) values ('" +name+ "','"+pwd+"','"+id+"',"+"false)";
System.out.println(sql_insert);
rs_update=statement.executeUpdate(sql_insert);
//statement.close();
//connection.close();
return USER_REGISTER_SUCCESS;
}
}
为了方便测试,这里还写了一个方法用于显示所有用户信息,这个方法在用户登录后才会调用,相当于一个测试方法,它代表的是用户登录成功后的主程序,这是一个Demo
因此,在关闭这个窗口后,用户相应的应该是下线状态,所以在这个方法里还需要在关闭窗口时向数据库更新用户在线信息
public String show_all_user_info(String name){
String info="";
JFrame jf = new JFrame("所有用户信息");
jf.setSize(500,500);
jf.setLocationRelativeTo(null);
int user_num=user_info.size();
jf.setLayout(new GridLayout(user_num,1));
jf.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
String sql_update = "update user_info set user_if_online = false where user_name='"+name+"'";
System.out.println(sql_update);
String dbURL = "jdbc:mysql://localhost:3306/user_info";
String name = "root";
String pwd = "wujiayi0303";
Connection connection = null;
try {
connection = DriverManager.getConnection(dbURL, name, pwd);
} catch (SQLException ex) {
throw new RuntimeException(ex);
}
Statement statement = null;
try {
statement = connection.createStatement();
} catch (SQLException ex) {
throw new RuntimeException(ex);
}
try {
int rs_update=statement.executeUpdate(sql_update);
} catch (SQLException ex) {
throw new RuntimeException(ex);
}
try {
statement.close();
} catch (SQLException ex) {
throw new RuntimeException(ex);
}
try {
connection.close();
} catch (SQLException ex) {
throw new RuntimeException(ex);
}
super.windowClosing(e);
}
});
for(String key : user_info.keySet ()){
User user = user_info.get (key);
String sinfo = user.getUser_name () + " - " + user.getUser_id () + " - " + user.getUser_login_time ()+"-"+user.isUser_if_online();
info += sinfo + " ";
jf.add (new JLabel (sinfo));
}
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
return info;
}
UI类
首先要有一个登录界面和一个注册界面,两个窗体
JFrame login_frame = new JFrame("登录");
JFrame register_frame = new JFrame("注册");
提供一个创建登录界面的方法,添加登录按钮和注册按钮,这里先讲登录按钮
按下登录按钮,应该用server对用户名和密码进行判断,按下注册按钮,应该弹出注册界面,因此,在登录按钮和注册按钮的事件监听中加入相关方法
创建一个Login_Listener类,编写actionPerformed函数,如果按下的是注册,则显示注册界面,并且隐藏登录界面;如果按下的是登录按钮,则获取文本框的数据,并且调用server的login方法,进行数据检验,根据返回的内容做出相应的交互信息显示
@Override
public void actionPerformed(ActionEvent e) {
//获取按钮上的字符串
String action = e.getActionCommand();
if (action.equals("注册")) {
ui.login_frame.setVisible(false);
ui.register_frame.setVisible(true);
}
if (action.equals("登录")) {
String name = ui.login_info[0].getText();
String pwd = ui.login_info[1].getText();
System.out.println(name + " " + pwd);
int login_state;
try {
login_state = server.login(name, pwd);
} catch (SQLException ex) {
throw new RuntimeException(ex);
}
switch (login_state) {
case USER_LOGIN_SUCCESS : {
System.out.println("登陆成功");
JOptionPane.showMessageDialog(null, "登陆成功");
server.show_all_user_info(name);
ui.login_frame.setVisible(false);
}
break;
case USER_LOGIN_USERNAME_NOT_EXIST : {
System.out.println("用户不存在");
JOptionPane.showMessageDialog(null, "用户不存在");
}
break;
case USER_LOGIN_PASSWORD_ERROR : {
System.out.println("密码错误");
JOptionPane.showMessageDialog(null, "密码错误");
}
break;
case USER_LOGIN_USER_ONLINE : {
System.out.println("该用户已在线");
JOptionPane.showMessageDialog(null, "该用户已在线");
}
break;
case USER_LOGIN_UNKNOWN_ERROR : {
System.out.println("登陆出错");
JOptionPane.showMessageDialog(null, "登陆错误,请稍后再试");
}
break;
}
}
}
同理,Register_Listener是监听注册界面按钮的,道理相同,这里直接给出代码
@Override
public void actionPerformed(ActionEvent e) {
String action = e.getActionCommand();
if (action.equals("注册")) {
String name = ui.register_info.get(0).getText();
String pwd = ui.register_info.get(1).getText();
String repwd = ui.register_info.get(2).getText();
String id = ui.register_info.get(3).getText();
System.out.println(name + " " + pwd + " " + id);
int register_state = 0;
try {
register_state = server.register(name, pwd, repwd,id);
} catch (SQLException ex) {
throw new RuntimeException(ex);
}
switch (register_state) {
case USER_REGISTER_SUCCESS : {
System.out.println("注册成功");
JOptionPane.showMessageDialog(null, "注册成功");
ui.register_frame.setVisible(false);
ui.login_frame.setVisible(true);
}
break;
case USER_REGISTER_USERNAME_EXIST : {
System.out.println("用户已存在");
JOptionPane.showMessageDialog(null, "用户已存在");
}
break;
case USER_REGISTER_FAIL_CONTAIN_SPACE : {
System.out.println("用户名含有空格");
JOptionPane.showMessageDialog(null, "用户名含有空格");
}
break;
case USER_REGISTER_FAIL_WRONG_NAME_LENGTH : {
System.out.println("用户名长度不满足条件");
JOptionPane.showMessageDialog(null, "用户名长度不满足条件");
}
break;
case USER_REGISTER_FAIL_WRONG_PWD_LENGTH : {
System.out.println("密码不满足条件");
JOptionPane.showMessageDialog(null, "密码不满足条件");
}
break;
case USER_REGISTER_FAIL_WRONG_REPWD : {
System.out.println("两次密码不一致");
JOptionPane.showMessageDialog(null, "两次密码不一致");
}
break;
}
}
}
有了监听器,就可以创建相关按键并且将监听器加入了,这里先看登录界面的创建
首先要设置登录界面的窗体的一些属性
login_frame.setSize(400,300);
login_frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
login_frame.setResizable(false);
login_frame.setLocationRelativeTo(null);
login_frame.setLayout(new FlowLayout());
然后创建需要的两个按键:登录、注册,并添加登录监听器
JButton button_login = new JButton ("登录");
button_login.addActionListener(login_listener);
JButton button_register = new JButton ("注册");
button_register.addActionListener(login_listener);
创建两个文本输入框
JTextField name_in = new JTextField ();
JPasswordField pwd_in = new JPasswordField ();
设置好信息提示的文本以及大小
JLabel label_name = new JLabel ("账号:");
JLabel label_pwd = new JLabel ("密码:");
name_in.setPreferredSize(new Dimension(300,35));
pwd_in.setPreferredSize(new Dimension(300,35));
接下来组装登录界面即可
login_frame.add(label_name);
login_frame.add(name_in);
login_frame.add(label_pwd);
login_frame.add(pwd_in);
login_frame.add(button_login);
login_frame.add(button_register);
除此之外,登录信息需要传递!因此UI类提供了login_info用来装用户名和密码
JTextField[] login_info = new JTextField[2];
login_info[0] = name_in;
login_info[1] = pwd_in;
不仅如此,还要传递一下UI类和Server类给两个监听器
login_listener.ui= this;
login_listener.server=this.server;
最后设置登录界面窗体可视
login_frame.setVisible(true);
注册界面是完全一样的道理,这里直接附上代码
public void create_register_ui(){
register_frame.setSize(400,300);
register_frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
register_frame.setResizable(false);
register_frame.setLocationRelativeTo(null);
register_frame.setLayout(new FlowLayout());
//注册界面信息框
JLabel label_name = new JLabel ("账号:");
JLabel label_pwd = new JLabel ("密码:");
JLabel label_repwd = new JLabel("确认:");
JLabel label_id = new JLabel("学号:");
//注册界面输入框
JTextField name_in = new JTextField ();
JTextField pwd_in = new JTextField ();
JTextField repwd_in = new JTextField ();
JTextField id_in = new JTextField ();
name_in.setPreferredSize(new Dimension(300,35));
pwd_in.setPreferredSize(new Dimension(300,35));
repwd_in.setPreferredSize(new Dimension(300,35));
id_in.setPreferredSize(new Dimension(300,35));
//注册界面按钮
JButton button_confirm = new JButton("注册");
JButton button_cancel = new JButton("取消");
button_confirm.addActionListener(register_listener);
button_cancel.addActionListener(register_listener);
//组装注册界面
register_frame.add(label_name);
register_frame.add(name_in);
register_frame.add(label_pwd);
register_frame.add(pwd_in);
register_frame.add(label_repwd);
register_frame.add(repwd_in);
register_frame.add(label_id);
register_frame.add(id_in);
register_frame.add(button_confirm);
register_frame.add(button_cancel);
register_frame.setVisible(false);
//保存注册信息到register_info
register_info.add(name_in);
register_info.add(pwd_in);
register_info.add(repwd_in);
register_info.add(id_in);
Register_Listener.ui =this;
Register_Listener.server =this.server;
}
至此客户端和服务端的内容就编写完了,还需要进行数据库的创建,这里不作展示了