安卓APP登录忘记密码以及通过邮箱找回密码的实现

本文章主要实现的是APP端点击忘记密码之后,服务器将自动发送密码给APP。


前提:先阅读 这篇文章这篇文章,以下的代码有几个类和数据库是基于这篇文章的。
(1)需要一个发送邮件的jar包 : javax.mail .jar

(2)在qq邮箱开通IMAP/SMTP服务,并生成授权码
(3)编写邮件工具类
(4)编写Servlet
(5)编写安卓activity和xml

1.java发送邮件需要的jar包:

传送门

2.在qq邮箱开通IMAP/SMTP服务,并生成授权码

1.IMAP是什么?
IMAP,即Internet Message Access Protocol(互联网邮件访问协议),您可以通过这种协议从邮件服务器上获取邮件的信息、下载邮件等。IMAP与POP类似,都是一种邮件获取协议。

2.如何使用IMAP服务?
使用IMAP很简单,首先,您需要先在QQ邮箱中启用IMAP功能,然后,配置好客户端,就可以使用了。

3.如何开启IMAP服务?
请进入:邮箱设置->帐户,然后选中开启IMAP服务。

4.如何配置邮件客户端使用IMAP服务?
请先确定您的客户端支持IMAP协议,目前已经支持的有:
电子邮件客户端:
Foxmail
Outlook Express
Outlook 2003
Outlook 2007

我选择的是:Foxmail
那应该如何配置电子邮件客户端使用IMAP?传送门

3.编写邮件工具类

SendMail.java

/**
 *
 * @author Administrator
 */

import javax.mail.MessagingException;
import javax.mail.NoSuchProviderException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.util.Date;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

 
public class SendMail {
 
	// 发件人的邮箱账号如:xxx@163.com
	public static String sendEmailAccount = "这里填你的邮箱";
	// 发件人的邮箱的授权码(自己在邮箱服务器中开启并设置)
	public static String sendEmailPassword = "这里填你得到的授权码";
	// 发件人邮箱的SMTP服务器地址,如:smtp.163.com
	public static String sendEmailSMTPHost = "smtp.qq.com";
	// 收件人的邮箱账号
	public static String receiveMailAccount = "";
 
	// 把发送邮件封装为函数,参数为收件人的邮箱账号和要发送的内容
	public void sendMail(String receiveMailAccount, String mailContent) {
		// 创建用于连接邮件服务器的参数配置
		Properties props = new Properties();
		// 设置使用SMTP协议
		props.setProperty("mail.transport.protocol", "smtp");
		// 设置发件人的SMTP服务器地址
		props.setProperty("mail.smtp.host", sendEmailSMTPHost);
		// 设置需要验证
		props.setProperty("mail.smtp.auth", "true");
                final String smtpPort = "465";
                props.setProperty("mail.smtp.port", smtpPort);
                props.setProperty("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
                props.setProperty("mail.smtp.socketFactory.fallback", "false");
                props.setProperty("mail.smtp.socketFactory.port", smtpPort);
                // 根据配置创建会话对象, 用于和邮件服务器交互
                 Session session = Session.getDefaultInstance(props);
                 // 设置debug模式,便于查看发送过程所产生的日志
                  session.setDebug(true);  
	
 
		try {
			// 创建一封邮件
			MimeMessage message = createMimeMessage(session, sendEmailAccount, receiveMailAccount);
                        message.setContent(mailContent,"text/html;charset=UTF-8");
			// 根据 Session 获取邮件传输对象
			Transport transport = session.getTransport();
 
			transport.connect(sendEmailAccount, sendEmailPassword);
 
			// 发送邮件, 发到所有的收件地址, 通过message.getAllRecipients() 可以获取到在创建邮件对象时添加的所有收件人
			transport.sendMessage(message, message.getAllRecipients());
 
			// 关闭连接
			transport.close();
		} catch (NoSuchProviderException e) {
			// TODO Auto-generated catch block
			   Logger.getLogger(SendMail.class.getName()).log(Level.SEVERE, null, e);
		} catch (MessagingException e) {
			// TODO Auto-generated catch block
			 Logger.getLogger(SendMail.class.getName()).log(Level.SEVERE, null, e);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			 Logger.getLogger(SendMail.class.getName()).log(Level.SEVERE, null, e);
		}
	}
 
	/**
	 * 
	 * @param session
	 *            和服务器交互的会话
	 * @param sendMail
	 *            发件人邮箱
	 * @param receiveMail
	 *            收件人邮箱
	 * @return
	 * @throws Exception
	 */
	public static MimeMessage createMimeMessage(Session session, String sendMail, String receiveMail) throws Exception {
		// 创建一封邮件
		MimeMessage message = new MimeMessage(session);
               
		// 设置发件人姓名和编码格式
		message.setFrom(new InternetAddress(sendMail, "老米", "UTF-8"));
 
		// 收件人
		message.setRecipient(MimeMessage.RecipientType.TO, new InternetAddress(receiveMail, "尊敬的用户", "UTF-8"));
 
		// 设置邮件主题
		message.setSubject("找回密码提醒", "UTF-8");
 
		// 设置邮件正文
		//message.setContent( "这是您的密码:","text/html;charset=UTF-8");
 
		// 设置发件时间
		message.setSentDate(new Date());
 
		// 保存设置
		message.saveChanges();
 
		return message;
	}
 
}

4.编写Servlet

记得映射到web.xml

    <servlet>
        <servlet-name>ForgetPSWServlet</servlet-name>
        <servlet-class>你的包名.ForgetPSWServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>ForgetPSWServlet</servlet-name>
        <url-pattern>/ForgetPSWServlet</url-pattern>
    </servlet-mapping>

ForgetPSWServlet.java

import java.io.IOException;
import java.io.PrintWriter;
import java.security.interfaces.RSAPrivateKey;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.jw.MyFirstWebAPP.RSAutil.GenKeyFromString;
import net.jw.MyFirstWebAPP.RSAutil.MyConstant;
import net.jw.MyFirstWebAPP.RSAutil.RSAUtil;
import net.jw.MyFirstWebAPP.EmailUtil.SendMail;
import net.jw.MyFirstWebAPP.User;
import net.jw.MyFirstWebAPP.UserDAO;
import net.sf.json.JSONObject;
import org.bouncycastle.util.encoders.Base64;

/**
 *忘记密码
 * @author Administrator
 */
public class ForgetPSWServlet extends HttpServlet {

  
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        // 设置响应内容类型  
        response.setContentType("text/html;charset=utf-8");
        request.setCharacterEncoding("utf-8");
        response.setCharacterEncoding("utf-8");

        try (PrintWriter out = response.getWriter()) {

            //获得请求中传来的用户名和邮箱
            String accountNumber = request.getParameter("AccountNumber").trim();
            String email = request.getParameter("Email").trim();

            byte[] bytereuser = Base64.decode(accountNumber);//用户名解密BASE64
            byte[] bytereemail = Base64.decode(email);//密码解密BASE64
          //  System.out.println(byteres);
           // System.out.println("字符串转成byte数组:"+new String(byteres));
             // 获取私钥	
            RSAUtil rsa = new RSAUtil();
            RSAPrivateKey priKey = (RSAPrivateKey) GenKeyFromString
				.getPrivateKey(MyConstant.priKey1);
             // 拿着私钥解用户名
            byte[] encRsaByteuser = rsa.decrypt(priKey,
				bytereuser);
          // 拿着私钥解邮箱
             byte[] encRsaByteemail = rsa.decrypt(priKey,
				bytereemail);
             //邮箱验证结果
            int verifyResult = verifyForget(new String(encRsaByteuser),new String(encRsaByteemail));
                              
            Map<String, String> params = new HashMap<>();
            JSONObject jsonObject = new JSONObject();

            if (verifyResult == -1) {
                params.put("Result", "TheUserDoesNotExist");
            } 
            else if (verifyResult == 0){
                params.put("Result", "EmailError");
            }
            else if(verifyResult == 1){
                params.put("Result","CorrectEmail");//验证邮箱和用户成功
                SendMail mySendMail = new SendMail();    //发送邮箱          
                 User user=UserDAO.queryUserByEmail(new String(encRsaByteemail));
                 if(user!=null) {
                     
                    String psw=user.getPassword();
                    mySendMail.sendMail(new String(encRsaByteemail), "温馨提醒!!!您的密码为:"+psw+"\n阅读完请立即删除该邮件,登录成功后请尽快修改密码!!!以防密码被他人窃取。");
				   
			}
            }

            jsonObject.put("params", params);
            out.write(jsonObject.toString());
        } catch (Exception ex) {
            Logger.getLogger(ForgetPSWServlet.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doPost(request, response);
    }

    /**
     * 验证用户名邮箱是否正确
     *
     * @param userName
     * @param email
     */
   private int verifyForget(String userName, String email) {
        User user = UserDAO.queryEmail(userName);
        
           boolean hasUser = false;
           boolean rightEmail = false;
        //账户邮箱验证
         if(!UserDAO.checkUserName(userName)){
            hasUser = true;
            if(user.getEmail().equals(email)){
                rightEmail = true;
            }
        }
       if(!hasUser) return -1;//无该用户
        else if(!rightEmail) return 0;//有该用户,但是邮箱输入错误
        return 1;//有该用户,且邮箱输入正确
       
    }

}

如果以下导入包报错:

import net.jw.MyFirstWebAPP.RSAutil.GenKeyFromString;
import net.jw.MyFirstWebAPP.RSAutil.MyConstant;
import net.jw.MyFirstWebAPP.RSAutil.RSAUtil;
import net.jw.MyFirstWebAPP.User;
import net.jw.MyFirstWebAPP.UserDAO;

请去这篇文章阅读,里面都有以上类的!

5.安卓APP的实现

ForgetPswActivity.java

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.android.volley.AuthFailureError;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;
import com.example.administrator.loginclient.HttpsUtils.HTTPSTrustManager;
import com.example.administrator.loginclient.R;
import com.example.administrator.loginclient.RsaUtils.GenKeyFromString;
import com.example.administrator.loginclient.RsaUtils.MyConstant;
import com.example.administrator.loginclient.RsaUtils.RSAUtil;
import org.bouncycastle.util.encoders.Base64;
import org.json.JSONException;
import org.json.JSONObject;
import java.security.interfaces.RSAPublicKey;
import java.util.HashMap;
import java.util.Map;

/**
 * 找回密码
 */
public class ForgetPswActivity extends BaseActivity {
    public static RequestQueue queue;
    private static Context mContext;
    public  static boolean flag=false;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_forget_psw);
        queue = Volley.newRequestQueue(getApplicationContext());

        mContext = this;
        final EditText AccountNumber = (EditText) findViewById(R.id.forget_edit_account);
        final EditText Email = (EditText) findViewById(R.id.forget_edit_email);
        Button submit=(Button)findViewById(R.id.forget_btn_sudmit);
        Button to_register=(Button)findViewById(R.id.to_register);
        Button back_to_login=(Button)findViewById(R.id.back_to_login);
        submit.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                final String name = AccountNumber.getText().toString().trim();
                final String email = Email.getText().toString().trim();

                // 获取Rsa 工具类对象
                RSAUtil rsa = new RSAUtil();

                // 获取公钥
                RSAPublicKey pubKey = (RSAPublicKey) GenKeyFromString
                        .getPubKey(MyConstant.pubKey1);

                // 使用公钥加密 数据
                byte[] enRsaByte_email = new byte[0];
                byte[] enRsaBytes_user = new byte[0];
                try {
                    enRsaByte_email = rsa.encrypt(pubKey, email.getBytes());//邮箱加密
                    enRsaBytes_user = rsa.encrypt(pubKey, name.getBytes());//用户名加密
                } catch (Exception e) {
                    e.printStackTrace();
                }

                /**
                 * base64对byte数组进行编码,进过编码后得到String传输到对服务端解码得出byte数组。
                 */
                String enRsaStr_email = new String(Base64.encode(enRsaByte_email));//密码byte数组转成字符串
                String enRsaStr_user = new String(Base64.encode(enRsaBytes_user));//用户名byte数组转成字符串
                ForgetPSWRequest(enRsaStr_user,enRsaStr_email);
                Toast.makeText(mContext, "请稍等...", Toast.LENGTH_LONG).show();

              }

        });

        to_register.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
          //      Intent intent = new Intent(mContext, RegisterActivity.class);
            //    startActivity(intent);
            }
        });
        back_to_login.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
           //     Intent intent = new Intent(mContext, MainActivity.class);
            //    startActivity(intent);
            }
        });
    }
    public static void ForgetPSWRequest(final String accountNumber,final String email){
        //请求地址
        String url = "http://localhost:8083/MyFirstWebAPP/ForgetPSWServlet";    //注①
        String tag = "Forget";    //注②

        //取得请求队列
        RequestQueue requestQueue = queue;

        //防止重复请求,所以先取消tag标识的请求队列
        requestQueue.cancelAll(tag);
        HTTPSTrustManager.allowAllSSL();//允许所有https请求

        //创建StringRequest,定义字符串请求的请求方式为POST(省略第一个参数会默认为GET方式)
        final StringRequest request = new StringRequest(Request.Method.POST, url,
                new Response.Listener<String>() {
                    @Override
                    public void onResponse(String response) {
                        try {
                            JSONObject jsonObject = (JSONObject) new JSONObject(response).get("params");  //注③
                            String result = jsonObject.getString("Result");  //注④
                            if (result.equals("TheUserDoesNotExist")) {  //注⑤

                                Toast.makeText(mContext, "用户名不存在或邮箱错误", Toast.LENGTH_SHORT).show();
                            }
                            else if (result.equals("EmailError")){
                                //做自己的登录失败操作,如Toast提示
                                Toast.makeText(mContext, "用户不存在或邮箱错误", Toast.LENGTH_LONG).show();
                            }
                            else if(result.equals("CorrectEmail")){

                                Toast.makeText(mContext, "密码已发至您邮箱,请注意查收!", Toast.LENGTH_LONG).show();
                            }

                        } catch (JSONException e) {
                            //做自己的请求异常操作,如Toast提示(“无网络连接”等)
                            Log.e("TAG", e.getMessage(), e);
                            Toast.makeText(mContext, "无网络连接", Toast.LENGTH_LONG).show();
                        }
                    }
                }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                //做自己的响应错误操作,如Toast提示(“请稍后重试”等)
                Log.e("TAG", error.getMessage(), error);
                Toast.makeText(mContext, "请稍后重试", Toast.LENGTH_LONG).show();
            }
        }) {
            @Override
            protected Map<String, String> getParams() throws AuthFailureError {
                Map<String, String> params = new HashMap<>();
                params.put("AccountNumber", accountNumber);  //注⑥
                params.put("Email", email);  //注⑥

                return params;
            }
        };

        //设置Tag标签
        request.setTag(tag);

        //将请求添加到队列中
        requestQueue.add(request);

    }
}

activity_forget_psw.xml

<?xml version="1.0" encoding="utf-8"?>
<!--
忘记密码
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
   android:orientation="vertical"
    tools:context=".Activities.ForgetPswActivity">
    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="textPersonName"
        android:ems="10"
        android:hint="请输入用户名"
        android:textColorHint="#003399"
        android:id="@+id/forget_edit_account"

        android:textSize="20dp"
        android:textColor="#003399"
        android:layout_margin="10dp"/>
    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10"
        android:hint="注册时填的邮箱"
        android:textColorHint="#003399"
        android:id="@+id/forget_edit_email"

        android:textSize="20dp"
        android:textColor="#003399"
        android:layout_margin="10dp"/>
    <Button
        android:text="提交"
        android:textSize="20dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/forget_btn_sudmit"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:background="@color/lavender"/>

    <Button
        android:text="去注册"
        android:textSize="20dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/to_register"
        android:layout_marginTop="7dp"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:background="@color/lavender"/>
    <Button
        android:text="返回登录"
        android:textSize="20dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/back_to_login"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:layout_marginTop="7dp"
        android:background="@color/lavender"/>
</LinearLayout>

用到的依赖(外部的BASE64Decoder.jar包和java服务器的一样的):

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation 'androidx.core:core:1.3.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'com.android.volley:volley:1.1.1'
    implementation 'com.nineoldandroids:library:2.4.0'
    implementation files('libs/sun.misc.BASE64Decoder.jar')
    implementation files('libs/bcprov-jdk15-1.45.jar')
    //noinspection GradleDeprecated
    implementation 'com.google.android.gms:play-services-appindexing:9.8.0'
    implementation files('libs/xUtils-2.6.14.jar')
    androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation 'androidx.cardview:cardview:1.0.0'
    implementation 'androidx.recyclerview:recyclerview:1.1.0'
    implementation 'com.google.android.material:material:1.3.0-alpha01'
    implementation 'com.github.bumptech.glide:glide:4.10.0'
    implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"

}

app需要手动导入外部jar包有三个:百度网盘
提取码:jd3v
BaseActivity.java

import android.os.Bundle;

import androidx.appcompat.app.AppCompatActivity;

import com.example.administrator.loginclient.ActivityCollectors.ActivityCollector;

/**
 * 这个类主要用于销毁活动
 */
public class BaseActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityCollector.addActivity(this);//将活动添加到活动收集器
    }
    @Override
    protected void onDestroy(){
        super.onDestroy();
        ActivityCollector.removeActivity(this);//将活动移除活动收集器
    }



}

HTTPSTrustManager.java 是用来允许https请求的

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

/**
 * Created by Administrator on 2020/4/26.
 * 此类是volley加持https协议
 */


public class HTTPSTrustManager implements X509TrustManager {

    private static TrustManager[] trustManagers;
    private static final X509Certificate[] _AcceptedIssuers = new X509Certificate[] {};
    protected HttpURLConnection createConnection(URL url) throws IOException {
        if("https".equals(url.getProtocol().toLowerCase())){
            HTTPSTrustManager.allowAllSSL();
        }
        return (HttpURLConnection) url.openConnection();
    }

    @Override
    public void checkClientTrusted(
            java.security.cert.X509Certificate[] x509Certificates, String s)
            throws java.security.cert.CertificateException {
        // To change body of implemented methods use File | Settings | File
        // Templates.
    }

    @Override
    public void checkServerTrusted(
            java.security.cert.X509Certificate[] x509Certificates, String s)
            throws java.security.cert.CertificateException {
        // To change body of implemented methods use File | Settings | File
        // Templates.
    }

    public boolean isClientTrusted(X509Certificate[] chain) {
        return true;
    }

    public boolean isServerTrusted(X509Certificate[] chain) {
        return true;
    }

    @Override
    public X509Certificate[] getAcceptedIssuers() {
        return _AcceptedIssuers;
    }

    public static void allowAllSSL() {
        HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {

            @Override
            public boolean verify(String arg0, SSLSession arg1) {
                // TODO Auto-generated method stub
                return true;
            }

        });

        SSLContext context = null;
        if (trustManagers == null) {
            trustManagers = new TrustManager[] { new HTTPSTrustManager() };
        }

        try {
            context = SSLContext.getInstance("TLS");
            context.init(null, trustManagers, new SecureRandom());
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }

        HttpsURLConnection.setDefaultSSLSocketFactory(context
                .getSocketFactory());
    }

}

这三个导入包报错:

import com.example.administrator.loginclient.RsaUtils.GenKeyFromString;
import com.example.administrator.loginclient.RsaUtils.MyConstant;
import com.example.administrator.loginclient.RsaUtils.RSAUtil;

请去这篇文章阅读,里面都有的。

提示一下,本文章是基于这个项目而实现的!
参考文章:https://blog.csdn.net/Mr_Megamind/article/details/71404618

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一粒程序米

你的鼓励是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值