在线登录注册功能(android客户端+javaweb服务端+腾讯云服务器+腾讯云数据库)
完整的项目已上传github仓库,链接在文章最下面
注:笔者在安卓客户端部分写了kotlin语言和java语言两种,编译运行以java的为准,kotlin是笔者练习所写
开发工具
Android Studio 3.5
Eclipse 2020
tomcat 9.0
jdk 13.0
腾讯云服务器 Window Server 2012 R2
腾讯云数据库 Mysql 5.7
java web项目部署在腾讯云服务器
https://blog.csdn.net/zhangjin2024/article/details/101173520?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param
腾讯云数据库配置
首先要购买一个腾讯云数据库,我买的是学生活动价36一年的。
进入控制台,可以看到我们的实例:
点击实例ID,将外网地址打开,记下外网地址和外网端口,java web连接时要用:
然后登录数据库实例:
新建自己app要用到的数据库,并建立项目用到的表:
客户端(Android Studio)
功能页
app启动页
登录页面
注册页面
忘记密码页面
重置密码页面
app启动页
app启动时默认是白屏,为了好看,我们加上app的特色启动页面,先看效果图:
实现这个非常容易,只需要在styles.xml中添加如下:
然后在mainfest文件的启动页面里添加:
登录页面
效果如下:
包含用户名和密码的输入,密码的可见或不可见切换,登录按钮,忘记密码文本点击以及注册新用户的文本点击,功能还包括对用户输入合法性的判断。使用忘记密码功能时,接收用户输入的用户名,提交至后台验证用户是否存在,若存在则跳转至找回密码界面。
登录时客户端接收到用户输入的用户名以及密码,提交至后端服务器,与数据库中的已存信息比较验证合法性,然后将判断结果传回客户端显示登录成功或失败。
与服务器建立连接:
其中132.232.81.77:8080是我腾讯云服务器的公网ip,后面是打包的javaweb项目war包名称以及用到的对应页面的servlet文件名。
登录界面完整代码:
package com.example.yiyu;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.text.method.HideReturnsTransformationMethod;
import android.text.method.PasswordTransformationMethod;
import android.view.View;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ToggleButton;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class Login2Activity extends AppCompatActivity implements View.OnClickListener{
Button login;
ToggleButton pswsee;
EditText name1;
EditText password1;
TextView textRegister;
TextView textForget;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login2);
login = this.findViewById(R.id.login);
pswsee = this.findViewById(R.id.pswsee);
name1 = this.findViewById(R.id.name);
password1 = this.findViewById(R.id.password);
textRegister = this.findViewById(R.id.newuser);
textForget = this.findViewById(R.id.forgetpsw);
login.setOnClickListener(this);
textRegister.setOnClickListener(this);
textForget.setOnClickListener(this);
pswsee.setOnCheckedChangeListener(new ToggleButtonClick());
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.login:
final String name = name1.getText().toString().trim();
final String password = password1.getText().toString().trim();
if (name.isEmpty()) {
Toast.makeText(this, "请输入用户名", Toast.LENGTH_SHORT).show();
} else if (password.isEmpty()) {
Toast.makeText(this, "请输入密码", Toast.LENGTH_SHORT).show();
} else {
//调用Java后台登录接口
new Thread() {
@Override
public void run() {
try {
String path = "http://132.232.81.77:8080/ForAndroid/loginServlet?name=" + name + "&password=" + password;
URL url = new URL(path);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.connect();
int responseCode = connection.getResponseCode();
if (responseCode == 200) {
InputStream is = connection.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = -1;
while ((len = is.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
final String result = baos.toString();
runOnUiThread(new Runnable() {
@Override
public void run() {
if (result.equals("1")) {
Intent intent =new Intent(Login2Activity.this, MainActivity.class);
Toast.makeText(Login2Activity.this, "登录成功", Toast.LENGTH_SHORT).show();
startActivity(intent);
} else {
Toast.makeText(Login2Activity.this, "登录失败", Toast.LENGTH_SHORT).show();
}
}
});
}
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
break;
case R.id.newuser:
startActivity(new Intent(Login2Activity.this, Register2Activity.class));
break;
case R.id.forgetpsw:
final String name2 = name1.getText().toString().trim();
if(name2.isEmpty()){
Toast.makeText(this,"请输入用户名",Toast.LENGTH_SHORT).show();
}else {
Intent intent=new Intent(Login2Activity.this, Forget2Activity.class);
intent.putExtra("name",name2);
startActivity(intent);
}
break;
}
}
//密码可见性按钮监听
private class ToggleButtonClick implements CompoundButton.OnCheckedChangeListener{
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
//5、判断事件源的选中状态
if (isChecked){
//显示密码
//etPassword.setInputType(InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);
password1.setTransformationMethod(HideReturnsTransformationMethod.getInstance());
}else {
// 隐藏密码
//etPassword.setInputType(InputType.TYPE_CLASS_TEXT|InputType.TYPE_TEXT_VARIATION_PASSWORD);
password1.setTransformationMethod(PasswordTransformationMethod.getInstance());
}
//6、每次显示或者关闭时,密码显示编辑的线不统一在最后,下面是为了统一
password1.setSelection(password1.length());
}
}
}
注册界面
效果如下:
注册包含昵称、密码、和手机,手机号用于用户忘记密码时重置密码。将用户输入的信息提交至后台服务器,插入云数据库中,完成注册。
对用户输入注册内容的合法性判断:
忘记密码界面
效果如下:
接收用户输入的手机号,提交至后台验证与用户注册时预留的手机号是否一致,一致则跳转至重置密码界面。
这里短信验证码服务采用MobTech平台的SMSSDK短信验证服务,附上平台链接:https://new.dashboard.mob.com/#/SMSSDK,按照教程在平台创建应用,会得到一个app key和app secret,如下:
在android studio完成相应的配置即可使用,附上配置教程:https://www.mob.com/wiki/detailed?wiki=SMSSDK_for_Android_kuaisujicheng&id=23
我的配置如下:
在project的build.gradle里:
在module的build.gradle里加上:
用户输入手机号,点击获取验证码,这时候会有弹窗向用户确认是否发送验证码(防止用户不小心输错手机号而把验证码发到别人的手机上),效果如下:
确认则下发验证码,同时按钮变为不可点击状态,并显示倒计时,效果如下:
用户点击提交按钮后,判断验证码是否正确,并后台验证手机号与用户注册时绑定的手机号是否一致
该界面的代码如下:
package com.example.yiyu;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.mob.MobSDK;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Timer;
import java.util.TimerTask;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import cn.smssdk.EventHandler;
import cn.smssdk.SMSSDK;
public class Forget2Activity extends AppCompatActivity implements View.OnClickListener{
EditText telenum;
EditText yzm;
Button getyzm;
Button submit;
TimerTask tt;
Timer tm;
int TIME=60;
String country="86";
int code_again=1;
String name;
String phone;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_forget2);
telenum=findViewById(R.id.phone);
yzm=findViewById(R.id.yzm);
getyzm=findViewById(R.id.getyzm);
submit=findViewById(R.id.submit);
getyzm.setOnClickListener((View.OnClickListener) this);
submit.setOnClickListener((View.OnClickListener) this);
name = getIntent().getStringExtra("name");
MobSDK.init(this);
SMSSDK.registerEventHandler(eh) ;//注册短信回调(记得销毁,避免泄露内存)
}
private void toast(final String str) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(Forget2Activity.this, str, Toast.LENGTH_SHORT).show();
}
});
}
public void forgetpsw(final String name,final String phone){
//调用Java后台登录接口
new Thread() {
@Override
public void run() {
try {
String path = "http://132.232.81.77:8080/ForAndroid/forgetPasswordServlet?name=" + name + "&phone=" + phone;
URL url = new URL(path);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.connect();
int responseCode = connection.getResponseCode();
if(responseCode == 200){
InputStream is = connection.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer= new byte[1024];
int len = -1;
while((len=is.read(buffer))!=-1){
baos.write(buffer,0,len);
}
final String result = baos.toString();
runOnUiThread(new Runnable() {
@Override
public void run() {
if(result.equals("1")){
Toast.makeText(Forget2Activity.this,"手机号与预留匹配",Toast.LENGTH_SHORT).show();
Intent intent =new Intent(Forget2Activity.this, ChangePassword2Activity.class);
intent.putExtra("name",name);
startActivity(intent);
}else{
Toast.makeText(Forget2Activity.this,"手机号与预留不匹配",Toast.LENGTH_LONG).show();
}
}
});
}
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
@Override
public void onClick(View view) {
switch(view.getId()){
case R.id.getyzm:
phone = telenum.getText().toString().trim();
if (!TextUtils.isEmpty(phone)) {
//定义需要匹配的正则表达式的规则
String REGEX_MOBILE_SIMPLE = "[1][3578]\\d{9}";
//把正则表达式的规则编译成模板
Pattern pattern = Pattern.compile(REGEX_MOBILE_SIMPLE);
//把需要匹配的字符给模板匹配,获得匹配器
Matcher matcher = pattern.matcher(phone);
// 通过匹配器查找是否有该字符,不可重复调用重复调用matcher.find()
if (matcher.find()) {//匹配手机号是否存在
alterWarning();
} else {
toast("手机号不正确 ");
}
} else {
toast("请输入手机号");
}
break;
case R.id.submit:
//获得用户输入的验证码
String code = yzm.getText().toString().replaceAll("/s","");
if (!TextUtils.isEmpty(code)) {//判断验证码是否为空
//验证
SMSSDK.submitVerificationCode( country, phone, code);
}else{//如果用户输入的内容为空,提醒用户
toast("请输入验证码");
}
break;
}
}
Handler hd = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what == code_again) {
getyzm.setEnabled(true);
submit.setEnabled(true);
tm.cancel();//取消任务
tt.cancel();//取消任务
TIME = 60;//时间重置
getyzm.setText("再次获取");
}else {
getyzm.setText(TIME + "秒后再次获取");
}
}
};
//回调
EventHandler eh=new EventHandler(){
@Override
public void afterEvent(int event, int result, Object data) {
if (result == SMSSDK.RESULT_COMPLETE) {
if (event == SMSSDK.EVENT_SUBMIT_VERIFICATION_CODE) {
toast("验证码正确!");
phone = telenum.getText().toString().trim();
forgetpsw(name,phone);
}else if (event == SMSSDK.EVENT_GET_VERIFICATION_CODE){ //获取验证码成功
toast("获取验证码成功");
}else if (event ==SMSSDK.EVENT_GET_SUPPORTED_COUNTRIES){//如果你调用了获取国家区号类表会在这里回调
//返回支持发送验证码的国家列表
}
}else{//错误等在这里(包括验证失败)
//错误码请参照http://wiki.mob.com/android-api-错误码参考/这里我就不再继续写了
((Throwable)data).printStackTrace();
String str = data.toString();
//toast(str);
toast("验证码错误!");
}
}
};
//弹窗确认下发
private void alterWarning() {
//构造器
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("提示"); //设置标题
builder.setMessage(phone + " 将会收到验证码"); //设置内容
// builder.setIcon(R.mipmap.ic_launcher);//设置图标,图片id即可
builder.setPositiveButton("好的", new DialogInterface.OnClickListener() {
//设置确定按钮
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss(); //关闭dialog
//通过sdk发送短信验证(请求获取短信验证码,在监听(eh)中返回)
SMSSDK.getVerificationCode(country, phone);
//做倒计时操作
Toast.makeText(Forget2Activity.this, "已发送", Toast.LENGTH_SHORT).show();
getyzm.setEnabled(false);
submit.setEnabled(true);
tm = new Timer();
tt = new TimerTask() {
@Override
public void run() {
hd.sendEmptyMessage(TIME--);
}
};
tm.schedule(tt,0,1000);
}
});
builder.setNegativeButton("取消", new DialogInterface.OnClickListener() { //设置取消按钮
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
Toast.makeText(Forget2Activity.this, "已取消" , Toast.LENGTH_SHORT).show();
}
});
//参数都设置完成了,创建并显示出来
builder.create().show();
}
//销毁短信注册
@Override
protected void onDestroy() {
super.onDestroy();
// 注销回调接口registerEventHandler必须和unregisterEventHandler配套使用,否则可能造成内存泄漏。
SMSSDK.unregisterEventHandler(eh);
}
}
重置密码界面
效果如下:
这里判断密码合法的话,就提交至后台服务器,在数据库修改用户密码
代码如下:
package com.example.yiyu;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class ChangePassword2Activity extends AppCompatActivity implements View.OnClickListener{
EditText password1;
EditText password2;
Button submit;
String name;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_change_password2);
password1=this.findViewById(R.id.password1);
password2=this.findViewById(R.id.password2);
submit=this.findViewById(R.id.submit);
name = getIntent().getStringExtra("name");
submit.setOnClickListener((View.OnClickListener) this);
}
public boolean check() {
boolean isok =false;
String psw=password1.getText().toString();
if(psw.isEmpty()){
Toast.makeText(this,"密码不能为空",Toast.LENGTH_LONG).show();
return isok;
}
if(psw.length()>16){
Toast.makeText(this,"密码不能超过16位",Toast.LENGTH_LONG).show();
return isok;
}
String psw2=password2.getText().toString();
if(!psw2.equals(psw)){
Toast.makeText(this,"两次密码不一致",Toast.LENGTH_LONG).show();
return isok;
}else{
isok=true;
return isok;
}
}
public void onClick(View view) {
switch (view.getId()) {
case R.id.submit:
final String password = password1.getText().toString();
//Toast.makeText(this,name+password,Toast.LENGTH_LONG).show();
if (check()) {
new Thread() {
@Override
public void run() {
try {
String path = "http://132.232.81.77:8080/ForAndroid/changePasswordServlet?name=" + name + "&password=" + password;
URL url = new URL(path);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.connect();
int responseCode = connection.getResponseCode();
if (responseCode == 200) {
InputStream is = connection.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = -1;
while ((len = is.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
final String result = baos.toString();
runOnUiThread(new Runnable() {
@Override
public void run() {
if (result.equals("1")) {
Toast.makeText(ChangePassword2Activity.this, "密码重置成功", Toast.LENGTH_LONG).show();
startActivity(new Intent(ChangePassword2Activity.this, Login2Activity.class));
} else {
Toast.makeText(ChangePassword2Activity.this, "密码重置失败", Toast.LENGTH_LONG).show();
}
}
});
}
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
break;
}
}
}
Java web服务端
主要框架:
bean:封装数据,是数据库中person的映射
dao:实现数据的持久化操作,如增删改查
db:连接数据库
service:业务逻辑的实现
servlet:实现客户端和服务端的通信
Select.jsp:查看后台数据库中的数据
下面主要说一下连接腾讯云数据库和servlet部分
ConnDB.java
package android.db;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class ConnDB {
/**
* 打开数据库
*/
public static Connection openConn() {
// 1.导驱动包mysql-connector-java-5.1.7-bin.jar
// 2.创建Connection连接对象
Connection conn = null;
try {
// 3.获取mysql驱动
Class.forName("com.mysql.jdbc.Driver");
// 4.获取url路径
String url = "jdbc:mysql://cdb-fmhwq8ww.cd.tencentcdb.com:10074/yiyu";
// 5.获取mysql连接账号
String mysql_name = "root";
String mysql_password = "b980227l";
// 6.进行连接
conn = DriverManager.getConnection(url, mysql_name, mysql_password);
} catch (Exception e) {
e.printStackTrace();
}
//
return conn;
}
/**
* 关闭数据库
*/
public static void closeConn(ResultSet rs, PreparedStatement ps, Connection conn) {
try {
if (rs != null) {
rs.close();
}
if (ps != null) {
ps.close();
}
if (conn != null) {
conn.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
这个部分就是上面说到的云数据库的外网地址的外网端口。
LoginServlet.java
package android.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import android.bean.Person;
import android.service.PersonService;
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
String name = request.getParameter("name");
String password = request.getParameter("password");
Person person =new Person();
person.setName(name);
person.setPassword(password);
PersonService personService = new PersonService();
int num = personService.loginPerson(person);
response.getWriter().write(num + "");
}
}
接收客户端传来的用户名和密码,与数据库中的数据比较,验证登录的合法性,并将结果传回给客户端做出相应的响应。调用PersonDao中的loginPerson函数实现验证,该函数如下:
其他的servlet类似,大家可以直接下载代码看
另外,提醒一点,大家可能会发现在安卓模拟器上一切正常,但是app安装到真机上就无法访问腾讯云数据库了。这是因为安卓9.0系统已经默认不支持http请求了,谷歌默认要求链接是加密链接了,解决方法如下:https://blog.csdn.net/qq_41117896/article/details/109011794
查看后台数据库中的数据
Select.jsp
浏览器输入http://132.232.81.77:8080/ForAndroid/Select.jsp
完整项目
github:https://github.com/guyuanjunxi/yiyu
csdn:https://download.csdn.net/download/qq_41117896/12918776