顾老师新作“带着ChatGPT玩转软件开发”将于今年底明年初出版
4.1.2 ChatGPT生成后端代码
1.申请找回密码后端代码
提示词
使用JSP+JavaBean方式实现找回密码功能。该功能分为手机短信找回和Email找回,处理逻辑如下。
1.在VeriCodePage.jsp的id="contact"文本框中输入手机号或Email,提交给VeriCodePage.jsp处理。
2.根据输入的手机号或Email,查询数据库中的用户表(user)以判断该字段是否存在。
3.如果数据库中不存在该手机号或Email,返回提示信息:“您输入的手机号或Email不存在,请重新输入!”信息,并在id="VeriCodeError"的<div>中显示。
4.如果输入的是手机号,则向该手机号发送验证码;如果输入的是Email,则向该Email发送验证码。
5.发送完成后,将验证码存储在验证码表(code)中。
6.用户在RecoverPage.jsp的id="identifyingCode"中输入验证码,id="newPassword"中输入新密码,id="confirmPassword"中输入新密码的确认码,提交给RecoverPage.jsp处理。
7.如果验证码不正确,返回提示信息:“验证码错误”,并在id="recoverError"的<div>中显示。
8.如果新密码与确认密码不一致,返回提示信息:“新密码与新密码确认码不一致”,并在id="recoverError"的<div>中显示。
9.如果新密码不满足强度要求,返回提示信息:“密码必须包含大小写字母、数字和特殊字符,长度在5-30之间”,并在id="recoverError"的<div>中显示。
10.如果新密码与之前使用过,返回提示信息:“新密码以前使用过,请设置其他密码”,并在id="recoverError"的<div>中显示。
11.否则,将旧密码添加至密码历史表(password)。
12.将新密码存储至用户表(user)中。
13.删除验证码表(code)中的临时验证码记录。
14.设置成功后,重定向至登录页面(LoginPage.jsp)。
流程如图4-3所示。

图4-3找回密码流程图
后端验证格式代码ValidateUser.java。
代码4-8 ValidateUser.java
package ebusiness;
import java.util.regex.Pattern;
public class ValidateUser{
public boolean isHash(String str){
//判断是否是 SHA256 哈希值
if(str!=null&&str.length()==64&&str.matches("[a-fA-F0-9]{64}")){
return true;//可能是 SHA256 哈希值
}
return false;
}
public String validate(User user){
String usernamePattern="^[a-zA-Z0-9]{5,20}$";
String phonePattern="^1[3-9]\\d{9}$";
String emailPattern="^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$";
if(!Pattern.matches(usernamePattern,user.getUsername())){
return "账号必须是5-20位字母或数字";
}
if(!Pattern.matches(phonePattern,user.getPhone())){
return "手机号必须符合中国手机号码格式";
}
if(!Pattern.matches(emailPattern,user.getEmail())){
return "Email格式不正确";
}
if(!isHash(user.getPassword())){
return "密码应该哈希进行存储";
}
return "验证成功";
}
}为了满足新密码以前没有使用过的需求,可以将密码每重置一次存入在表password中,格式如下。

user表格式如下。

code表格式如下。

数据库名为ebusiness,表为user、password和code(password存储当前用户设置过的密码,code存储当前用户正在使用的验证码)。请给出password和code的建表SQL语句。另外尽可能在JSP中减少逻辑代码,对于逻辑代码请发在JavaBean中。
2.找回密码后端代码回复
ChatGPT回答
为了实现您所描述的找回密码功能,我们将分为几个部分来处理,包括数据库表的设计、JavaBean的逻辑代码、JSP页面的处理以及表单的验证。以下是详细的实现步骤。
1)数据库表结构
首先,我们需要设计存储用户历史密码的表password和暂时存储验证码的表code。以下是创建该表的SQL语句。
CREATE TABLE password(
id INT AUTO_INCREMENT PRIMARY KEY,
uid INT NOT NULL,
password VARCHAR(100)NOT NULL,
FOREIGN KEY(uid)REFERENCES user(id)
);
CREATE TABLE code(
id INT AUTO_INCREMENT PRIMARY KEY,
uid INT NOT NULL,
code CHAR(6)NOT NULL,
FOREIGN KEY(uid)REFERENCES user(id)
);2)JavaBean逻辑代码
我们将创建一个PasswordRecovery.java来处理找回密码的逻辑。代码示例如下。
代码4-9 PasswordRecovery.java
package ebusiness;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.regex.Pattern;
public class PasswordRecovery{
//先验证手机号或邮箱格式,然后验证在用户数据库中是否存在。
public String checkContact(String contact){
String phonePattern="^1[3-9]\\d{9}$";
String emailPattern="^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$";
//验证格式
if(!Pattern.matches(phonePattern,contact)&&!Pattern.matches(emailPattern,contact)){
return "您输入的手机号或Email格式不正确!";
}
//查询用户是否存在
String query="SELECT * FROM user WHERE phone=? OR email=?";
try(Connection conn=dataSource.getConnection();
PreparedStatement stmt=conn.prepareStatement(query)){
stmt.setString(1,contact);
stmt.setString(2,contact);
ResultSet rs=stmt.executeQuery()
if(!rs.next()){
return "您输入的手机号或Email不存在,请重新输入!";
}
//这里可以调用发送短信或邮件的服务
//例如:sendVerificationCode(contact);
return "验证码已发送到您的手机或邮箱";
}catch(SQLException e){
logger.error(e.getMessage());
return "数据库错误,请稍后再试";
}
}
//重置新密码。
public String recoverPassword(String contact,String identifyingCode,String newPassword,String confirmPassword){
//验证验证码
//这里需要实现验证码验证逻辑
boolean isCodeValid=true;//假设验证码验证逻辑
if(!isCodeValid){
return "验证码错误";
}
//验证新密码是否与以前相同
int userId=getUserIdByContact(contact);
if(isPasswordUsed(userId,newPassword)){
return "新密码以前使用过,请设置其他密码";
}
//SHA256散列处理
String hashedPassword=hashPassword(newPassword);
//更新密码
updatePassword(userId,hashedPassword);
return "密码重置成功";
}
//根据手机号或邮箱获取用户ID。
private int getUserIdByContact(String contact){
//根据手机号或邮箱获取用户ID的逻辑
//这里需要实现根据联系信息查询用户ID
return 0;//返回用户ID
}
//检查新密码是否以前被设置过?
private boolean isPasswordUsed(int userId,String password){
//检查密码是否已使用的逻辑
return false;//返回是否已使用
}
//更新密码。
private void updatePassword(int userId,String hashedPassword){
String query="UPDATE user SET password=? WHERE id=?";
try(Connection conn=dataSource.getConnection();
PreparedStatement stmt=conn.prepareStatement(query)){
conn.setAutoCommit(false);
stmt.setString(1,hashedPassword);
stmt.setInt(2,userId);
stmt.executeUpdate();
}catch(SQLException e){
logger.error(e.getMessage());
}
//保存新密码到 password 表
String insertQuery="INSERT INTO password(uid,password)VALUES(?,?)";
try(Connection conn=dataSource.getConnection();
PreparedStatement stmt=conn.prepareStatement(insertQuery)){
conn.setAutoCommit(false);
insertStmt.setInt(1,userId);
insertStmt.setString(2,hashedPassword);
insertStmt.executeUpdate();
}catch(SQLException e){
logger.error(e.getMessage());
}
}
}这次生成的后端代码,许多函数的代码仅给出一个框架,需要后续进行完善。
3)JSP页面处理
接下来,我们需要在VeriCodePage.jsp和RecoverPage.jsp中处理表单提交。以下是示例代码。
代码4-10 VeriCodePage.jsp
<%@page import="ebusiness.PasswordRecovery"%>
<%@page import="java.sql.*"%>
<%
String contact=request.getParameter("contact");
String message="";
PasswordRecovery recovery=new PasswordRecovery();
message=recovery.checkContact(contact);
request.setAttribute("contactError",message);
RequestDispatcher dispatcher=request.getRequestDispatcher("RecoverPage.jsp");
dispatcher.forward(request,response);
%>代码4-11 RecoverPage.jsp
<%@page import="ebusiness.PasswordRecovery"%>
<%@page import="java.sql.*"%>
<%
String contact=request.getParameter("contact");
String identifyingCode=request.getParameter("identifyingCode");
String newPassword=request.getParameter("newPassword");
String confirmPassword=request.getParameter("confirmPassword");
String message="";
PasswordRecovery recovery=new PasswordRecovery();
message=recovery.recoverPassword(contact,identifyingCode,newPassword,confirmPassword);
request.setAttribute("recoverError",message);
RequestDispatcher dispatcher=request.getRequestDispatcher("RecoverPage.jsp");
dispatcher.forward(request,response);
%>总结
以上代码实现了找回密码的功能,包括对手机号和邮箱的验证、验证码的发送、密码的格式检查以及密码的更新。JSP页面主要用于接收和显示信息,逻辑处理则放在了JavaBean中,符合您的要求。请根据实际情况调整数据库连接和验证码发送的具体实现。
3.对找回密码后端代码的改造
由于产生的后端代码仅给出了一个框架,需要对各个部分进行优化。
1)改造VeriCodePage.jsp
代码4-12 VeriCodePage.jsp
<%@page contentType="text/html;charset=utf-8"%>
<%@page import="ebusiness.Util,ebusiness.CookiesManager,ebusiness.recover"%>
…
<%
Util util=new Util();
String csrftoken=util.generateRandomString(100,"strInt");
%>
…
<div id="VeriCodeError" class="error">
<%
CookiesManager ct=new CookiesManager("csrftoken",csrftoken,response,request);
String method=request.getMethod();
if("get".equalsIgnoreCase(method)){
String error=request.getParameter("error");
if(error==null){
error="";
}
ct.setCookie();
out.println(error);
}else if("post".equalsIgnoreCase(method)){
String csrftoken_post=request.getParameter("csrftoken");
String csrftoken_cookies=ct.getCookie();
if((csrftoken_cookies.equals(csrftoken_post))&&(csrftoken_cookies!=null)&&(csrftoken_cookies.length()>0)){
ct.setCookie();
recover Recover=new recover(response,request);
%>
<%=Recover.sendVerificationCode()%>
<%
}
}
%>
</div>
</div>
</body>
</html>在VeriCodePage.jsp中引入JavaBean的逻辑处理。
<%=Recover.sendVerificationCode()%>若sendVerificationCode()方法出现错误,错误信息将在
<div id="VeriCodeError" class="error">
…
<div>位置显示。
2)改造RecoverPage.jsp
代码4-13 RecoverPage.jsp
<%@page contentType="text/html;charset=utf-8"%>
<%@page import="ebusiness.Util,ebusiness.CookiesManager,ebusiness.recover"%>
…
<div id="recoverError" class="error">
<%
Util util=new Util();
String csrftoken=util.generateRandomString(100,"strInt");
%>
…
<%
CookiesManager ct=new CookiesManager("csrftoken",csrftoken,response,request);
String method=request.getMethod();
if("get".equalsIgnoreCase(method)){
String error=request.getParameter("error");
if(error==null){
error="";
}
ct.setCookie();
out.println(error);
}else if("post".equalsIgnoreCase(method)){
String csrftoken_post=request.getParameter("csrftoken");
String csrftoken_cookies=ct.getCookie();
if((csrftoken_cookies.equals(csrftoken_post))&&(csrftoken_cookies!=null)&&(csrftoken_cookies.length()>0)){
ct.setCookie();
recover Recover=new recover(response,request);
%>
<%=Recover.RecoverPassword()%>
<%
}
}
%>
</div>
</div>
</body>
</html>在RecoverPage.jsp
<%=Recover.RecoverPassword()%>中同样引入JavaBean的逻辑处理。如果RecoverPassword()方法出现错误,错误信息将在
<div id="recoverError" class="error">
…
<div>显示。
3)创建PasswordRecoveryController.java
PasswordRecoveryController.java处理与VeriCodePage.jsp和RecoverPage.jsp两个JSP文件交互的文件。
① 头部
代码4-14 PasswordRecoveryController.java
package ebusiness;
import java.io.IOException;
import java.sql.SQLException;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.apache.log4j.Logger;
import com.zaxxer.hikari.HikariDataSource;
import java.net.URLEncoder;
public class PasswordRecoveryController{
private static final Logger logger=Logger.getLogger(PasswordRecoveryController.class);
private HttpServletResponse response;
private HttpServletRequest request;
//构造函数。
public PasswordRecoveryController(HttpServletResponse response,HttpServletRequest request){
this.response=response;
this.request=request;
}② 与发送验证码相关代码
代码4-15 PasswordRecoveryController.java
//发送验证码
public String sendVerificationCode()throws SQLException,IOException{
//获取手机号码或Email
String contact=request.getParameter("contact");
//建立密码找回类变量
PasswordRecovery PR=new PasswordRecovery();
try{
//发送验证码
String info=PR.sendVerificationCode(contact);
if(info.matches("-?\\d+(\\.\\d+)?")){
//建立VeriCode cookie
CookiesManager myusername=new CookiesManager("uid",info,response,request);
myusername.setCookie();
//交给RecoverPage.jsp处理
response.sendRedirect("RecoverPage.jsp");
}else if(info.equals("-1")){
return "系统发生错误,请查看日志!";
}else{
String encodedMessage=URLEncoder.encode(info,"UTF-8");
String url="VeriCodePage.jsp?error="+encodedMessage;
response.sendRedirect(url);
}
}catch(Exception e){
logger.error(e.getMessage());
logger.error("发送验证码的时候发生错误:"+e.getMessage());
}
return "0";
}③ 与重置密码相关代码
代码4-16 PasswordRecoveryController.java
//重置密码
public String recoverPassword()throws SQLException,IOException{
//获取验证码和密码
String identifyingCode=request.getParameter("identifyingCode");
String newPassword=request.getParameter("newPassword");
//获取uid
CookiesManager userId=new CookiesManager("uid",response,request);
String uid=userId.getCookie();
if(uid==null||uid.isEmpty()){
return "请输入验证码后再进入";
}else{
int uidi=Integer.parseInt(uid);
UserRepository mysql=new UserRepository();
HikariDataSource dataSource=mysql.dataSource;
PasswordRecovery passwordrecovery=new PasswordRecovery();
//更新新密码
String info=passwordrecovery.recoverPassword(identifyingCode,newPassword,mysql,uidi);
if(info.equals("0")){
userId.clearCookie();
response.sendRedirect("LoginPage.jsp");
}else{
String encodedMessage=URLEncoder.encode(info,"UTF-8");
String url="RecoverPage.jsp?error="+encodedMessage;
response.sendRedirect(url);
}
Util util=new Util();
util.closePool(dataSource);
}
return "0";
} 4)创建PasswordRecovery.java
PasswordRecovery.java是处理重置密码具体事务的文件。
① 头部
代码4-17 PasswordRecovery.java
package ebusiness;
import java.sql.SQLException;
import java.util.regex.Pattern;
import org.apache.log4j.Logger;
import com.zaxxer.hikari.HikariDataSource;
import demo.SendSms;
public class PasswordRecovery{
private static final Logger logger=Logger.getLogger(PasswordRecovery.class);
Util util=new Util();② 与发送验证码相关代码
代码4-18 PasswordRecovery.java
//发送验证码。
public String sendVerificationCode(String contact)throws Exception{
String contactType=getContactType(contact);
if(contactType==null){
return "您输入的手机号或Email格式不正确!";
}
UserRepository mysql=new UserRepository();
HikariDataSource dataSource=mysql.dataSource;
String uid=getUserIdByContact(contact,mysql);
if(!uid.matches("\\d+")){
return uid;
}
int uidi=Integer.parseInt(uid);
String code=util.generateRandomString(6,"int");
if(contactType.equals("phone")){
return sendCodeForSMS(contact,uidi,code,mysql,uid);
}else if(contactType.equals("email")){
return sendCodeForEmial(contact,code,uidi,uid,mysql);
}
util.closePool(dataSource);
return contactType;
}
//发送短信验证码
private String sendCodeForSMS(String contact,int uidi,String code,UserRepository mysql,String uid)throws Exception{
return sendCode(contact,mysql,uidi,uid,code,"SMS");
}
//发送Email验证码
private String sendCodeForEmial(String contact,String code,int uidi,String uid,UserRepository mysql)throws Exception{
return sendCode(contact,mysql,uidi,uid,code,"Email");
}
//发送验证码,然后删除发送好的验证码,最后将旧密码放到历史密码表(password)中
private String sendCode(String contact,UserRepository mysql,int uidi,String uid,String code,String sign)throws Exception{
boolean judge=false;
if(sign.equals("SMS")){
judge=sendSmsVerificationCode(contact,uidi,code,mysql).equals("验证码已发送至手机");
}else if(sign.equals("Email")){
judge=sendEmailVerificationCode(contact,code).equals("验证码已发送至邮箱");
}
if(judge){
int info=mysql.deleteCodeInDB(uidi);
if(info==1 ||info==0){
//将新的验证码记录放入password
mysql.putCodeInDB(code,uidi);
return uid;
}else{
logger.error("从code删除数据失败");
return "-1";
}
}else{
if(sign.equals("SMS")){
logger.error("短信发送失败!");
}else if(sign.equals("Email")){
logger.error("邮件发送失败!");
}
return "-1";
}提示词
如何使用Java发送邮件?
ChatGPT回答
在Java中发送邮件通常使用JavaMail API。以下是一个基本的示例,演示如何使用JavaMail发送电子邮件。
步骤1:添加JavaMail依赖。
如果您使用Maven,可以在pom.xml中添加以下依赖。
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
<version>1.6.2</version>
</dependency>如果您不使用Maven,可以从Java Mail GitHub页面下载JAR文件,并将其添加到项目的类路径中。
步骤2:编写发送邮件的代码。
以下是一个简单的Java程序,用于发送电子邮件。
import java.util.Properties;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
public class EmailSender{
public static void main(String[] args){
//SMTP 服务器信息
final String username="your_EMAIL@example.com";//发件人邮箱
final String password="your_PASSWORD";//发件人邮箱密码
//设置 SMTP 服务器的属性
Properties props=new Properties();
props.put("mail.smtp.auth","true");
props.put("mail.smtp.starttls.enable","true");//使用 TLS
props.put("mail.smtp.host","smtp.example.com");//SMTP 服务器地址
props.put("mail.smtp.port","587");//SMTP 端口号
//创建会话
Session session=Session.getInstance(props,
new javax.mail.Authenticator(){
protected PasswordAuthentication getPasswordAuthentication(){
return new PasswordAuthentication(username,password);
}
});
try{
//创建邮件
Message message=new MimeMessage(session);
message.setFrom(new InternetAddress("from@example.com"));//发件人
message.setRecipients(Message.RecipientType.TO,InternetAddress.parse("to@example.com"));//收件人
message.setSubject("Test Subject");//邮件主题
message.setText("This is a test email.");//邮件内容
//发送邮件
Transport.send(message);
logger.info("Email sent successfully!");
}catch(MessagingException e){
logger.error(e.getMessage());
}
}
}步骤3:配置SMTP服务器。
在代码中,您需要替换以下信息。
•your_email@example.com:您的发件人邮箱地址。
•your_password:您的发件人邮箱密码。
•smtp.example.com:您的SMTP服务器地址(例如,Gmail的SMTP服务器是smtp.gmail.com)。
•587:SMTP端口号,通常为587(TLS)或465(SSL)。
步骤4:注意事项。
•安全性:如果您使用Gmail作为SMTP服务器,您可能需要启用“允许不够安全的应用”或使用OAuth2.0进行身份验证。
•防火墙和网络设置:确保您的网络允许通过SMTP端口发送邮件。
通过以上步骤,您应该能够在Java中成功发送电子邮件。如果有任何问题,请随时询问!
提示词
如何使用Java发送短信?
ChatGPT回答
在Java中发送短信,通常有两种常见的方法:使用“第三方短信平台API”或者“通过短信网关”。大多数短信平台提供了简单的HTTP API接口,您可以通过发送HTTP请求来发送短信。
以下是使用Twilio、阿里云短信服务和腾讯云短信服务这三种流行的短信平台的基本实现方法。
1.使用Twilio发送短信
Twilio是一个提供全球短信、语音和视频通信的第三方平台。要使用Twilio发送短信,您需要注册一个Twilio账号,并获得一个Account SID和Auth Token。
步骤1:添加Twilio依赖(如果使用Maven)在pom.xml中添加Twilio依赖。
<dependency>
<groupId>com.twilio</groupId>
<artifactId>twilio</artifactId>
<version>8.30.0</version><!--请检查最新版本-->
</dependency>步骤2:编写Java代码发送短信。
import com.twilio.Twilio;
import com.twilio.rest.api.v2010.account.Message;
import com.twilio.type.PhoneNumber;
public class SmsSender{
//替换为您的Twilio账号SID和Auth Token
public static final String ACCOUNT_SID="your_account_sid";
public static final String AUTH_TOKEN="your_auth_token";
public static void main(String[] args){
//初始化Twilio客户端
Twilio.init(ACCOUNT_SID,AUTH_TOKEN);
//发送短信
Message message=Message.creator(
new PhoneNumber("+1234567890"),//收件人号码
new PhoneNumber("+0987654321"),//发件人号码(您的Twilio手机号码)
"Hello,this is a test message from Java!").create();
logger.info("Message SID:"+message.getSid());
}
}2.使用阿里云短信服务
阿里云短信服务提供了高效的短信发送接口。您需要在阿里云官网注册并获得Access Key ID和Access Key Secret,并配置短信模板
步骤1:添加阿里云SDK依赖。在pom.xml中添加阿里云短信SDK的依赖。
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>dysmsapi20170525</artifactId>
<version>3.1.2</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>tea</artifactId>
<version>1.1.14</version><!--请替换为实际版本号-->
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>4.5.16</version>
</dependency>步骤2:编写Java代码发送短信。
…
步骤3:…
注解。
•在步骤1添加
步骤0:请访问阿里云短信服务控制台https://dysms.console.aliyun.com/domestic/text,完成下面操作。
Ø资质认证。
Ø签名管理(签名管理需要7-10个工作日)。
Ø模版管理。
•步骤2和步骤3由于代码经常变更,之前提供的代码已失效。在此不提供旧版本代码,下面把提供最新版本。
步骤2:建立sms.properties。
AccessKeyId=yourAccessKeyId
AccessKeySecret=your AccessKeySecret建立SendSms.java。
代码4-19 SendSms.java
package demo;
import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
import java.util.Properties;
import com.aliyun.dysmsapi20170525.Client;
import com.aliyun.teaopenapi.models.Config;
import com.aliyun.tea.*;
import com.google.gson.Gson;
import ebusiness.Util;
public class SendSms{
public static Client createClient()throws Exception{
//工程代码泄露可能会导致 AccessKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考。
//建议使用更安全的 STS 方式,更多鉴权访问方式请参见:https://help.aliyun.com/document_detail/378657.html。
Util util=new Util();
Properties prop=util.getPropertiesInfo("sms.properties");
StringAccessKeyId=prop.getProperty("AccessKeyId");
StringAccessKeySecret=prop.getProperty("AccessKeySecret");
Config config=new Config()
//必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID。
.setAccessKeyId(AccessKeyId)
//必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_SECRET。
.setAccessKeySecret(AccessKeySecret);
//Endpoint 请参考 https://api.aliyun.com/product/Dysmsapi
config.endpoint="dysmsapi.aliyuncs.com";
return new Client(config);
}
public Boolean SendSMS(String phone,String code)throws Exception{
com.aliyun.dysmsapi20170525.Client client=SendSms.createClient();
com.aliyun.dysmsapi20170525.models.SendSmsRequest sendSmsRequest=new com.aliyun.dysmsapi20170525.models.SendSmsRequest()
.setPhoneNumbers("18917546507")
.setTemplateCode("SMS_482935054")
.setSignName("啄木鸟软件")
.setTemplateParam("{\"code\":\"123456\"}");
com.aliyun.teautil.models.RuntimeOptions runtime=new com.aliyun.teautil.models.RuntimeOptions();
try{
//复制代码运行请自行打印 API的返回值
SendSmsResponse response=client.sendSms(sendSmsRequest);
if("OK".equals(response.getBody().getCode())){//假设"OK"表示成功
return true;
}else{
return false;
}
}catch(TeaException error){
//此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
//错误 message
com.aliyun.teautil.Common.assertAsString(error.message);
return false;
}catch(Exception _error){
TeaException error=new TeaException(_error.getMessage(),_error);
//此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
com.aliyun.teautil.Common.assertAsString(error.message);
return false;
}
}
}步骤3:等待标签名认证完成后,调用Boolean SendSMS(String phone,String code)方法向指定的phone发送验证码code。
3.使用腾讯云短信服务
腾讯云提供了短信API,可以通过该API发送短信。首先,您需要在腾讯云官网创建一个账号,并获取SecretId和SecretKey。
步骤1:添加腾讯云SDK依赖(如果使用Maven)
在pom.xml中添加腾讯云SDK的依赖。
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java</artifactId>
<version>3.0.414</version>
</dependency>步骤2:编写Java代码发送短信。
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.sms.v20210111.SmsClient;
import com.tencentcloudapi.sms.v20210111.models.SendSmsRequest;
import com.tencentcloudapi.sms.v20210111.models.SendSmsResponse;
public class SmsSender{
public static void main(String[] args){
//替换为您的SecretId和SecretKey
String secretId="your_secret_id";
String secretKey="your_secret_key";
//创建Credentials对象
Credential cred=new Credential(secretId,secretKey);
//创建HttpProfile对象
HttpProfile httpProfile=new HttpProfile();
httpProfile.setEndpoint("sms.tencentcloudapi.com");
//创建ClientProfile对象
ClientProfile clientProfile=new ClientProfile();
clientProfile.setHttpProfile(httpProfile);
//创建SmsClient实例
SmsClient client=new SmsClient(cred,clientProfile);
//创建发送短信的请求对象
SendSmsRequest req=new SendSmsRequest();
req.setPhoneNumberSet(new String[]{"+8613800000000"});//收件人手机号码
req.setSmsSdkAppId("your_sms_sdk_app_id");//短信应用 SDK ID
req.setSignName("YourSMSsignName");//短信签名
req.setTemplateId("your_template_id");//短信模板ID
req.setTemplateParamSet(new String[]{"123456"});//模板参数
try{
//发送短信
SendSmsResponse resp=client.SendSms(req);
}catch(Exception e){
logger.error(e.getMessage());
}
}
}4.总结
上述是三种常见的短信发送平台(Twilio、阿里云、腾讯云)通过Java实现发送短信的方式。无论您选择哪种平台,都需要首先创建账号并获得相应的API密钥或凭证。接着,通过HTTP请求或者SDK调用相应的接口来发送短信。
如果您有其他问题或需要更多帮助,欢迎继续咨询!
注意:有时候ChatGPT给出的代码比较老旧,需要的时候可以在网上选择最新版本的代码(或者在提示词里给出具体的版本信息)。 |
|---|
注解:按照下面的办法改造代码。
第一项:关于Email的实现。
第1步:在pom.xml中添加相关依赖。
代码4-20 pom.xml
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
<version>1.6.2</version>
</dependency>第2步:创建包com.jerry,并在该包下建立EmailSender.java。
代码4-21 EmailSender.java
package com.jerry;
import java.io.File;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.mail.AuthenticationFailedException;
import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
/**
*实现邮件发送功能
*@author Administrator
*
*/
public class EmailSender{
private static final Logger logger=LogManager.getLogger(EmailSender.class);
private String host;//服务器地
private String from;//发件人
private String to;//收件人 多个收件人以,分隔
private String title;//主题
private String content;//内容
private List<File>attachmentlist ;//附件集
private String username;//账号
private String password;//密码
/*发件人员工编号*/
private String sendEmployeeId;
public String getSendEmployeeId(){
return sendEmployeeId;
}
public void setSendEmployeeId(String sendEmployeeId){
this.sendEmployeeId=sendEmployeeId;
}
public String getHost(){
return host;
}
public void setHost(String host){
this.host=host;
}
public String getFrom(){
return from;
}
public void setFrom(String from){
this.from=from;
}
public String getTo(){
return to;
}
public void setTo(String to){
this.to=to;
}
public String getTitle(){
return title;
}
public void setTitle(String title){
this.title=title;
}
public String getContent(){
return content;
}
public void setContent(String content){
this.content=content;
}
public List<File>getAttachmentlist(){
return attachmentlist;
}
public void setAttachmentlist(List<File>attachmentlist){
this.attachmentlist=attachmentlist;
}
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 getPort(){
return port;
}
public void setPort(String port){
this.port=port;
}
private String port;
public EmailSender(String host,String from,String to,String title,
String content,List attachmentlist,String username,String password,String port){
this.host=host;
this.from=from;
this.to=to;
this.title=title;
this.content=content;
this.attachmentlist=attachmentlist;
this.username=username;
this.password=password;
this.port=port;
}
public EmailSender(String to,String title,String content,List attachmentlist){
this.to=to;
this.title=title;
this.content=content;
this.attachmentlist=attachmentlist;
}
/**
* 发送邮件
* @return 发送状态信息 index0:状态 0成功 1失败;index1:描述错误信息
*/
public String[] sendMail(){
String[] result=new String[2];
Session session=null;
Properties props=System.getProperties();
props.put("mail.smtp.host",host);
props.put("mail.smtp.sendpartial","true");
props.put("mail.smtp.port",port);
if(StringUtils.isBlank(username)){//不需要验证账号密码
session=Session.getDefaultInstance(props,null);
}else{
props.put("mail.smtp.auth","true");
EmailAuthenticator auth=new EmailAuthenticator(username,password);
session=Session.getInstance(props,auth);
}
//设置邮件发送信息
try{
//创建邮件
MimeMessage message=new MimeMessage(session);
//设置发件人地址
message.setFrom(new InternetAddress(from));
//设置收件人地址(多个邮件地址)
InternetAddress[] toAddr=InternetAddress.parse(to);
message.addRecipients(Message.RecipientType.TO,toAddr);
//设置邮件主题
message.setSubject(title);
//设置发送时间
message.setSentDate(new Date());
//设置发送内容
Multipart multipart=new MimeMultipart();
MimeBodyPart contentPart=new MimeBodyPart();
contentPart.setText(content);
multipart.addBodyPart(contentPart);
//设置附件
if(attachmentlist!=null&&attachmentlist.size()>0){
for(int i=0 ;i<attachmentlist.size();i++){
MimeBodyPart attachmentPart=new MimeBodyPart();
FileDataSource source=new FileDataSource(attachmentlist.get(i));
attachmentPart.setDataHandler(new DataHandler(source));
attachmentPart.setFileName(MimeUtility.encodeWord(attachmentlist.get(i).getName(),"gb2312",null));
multipart.addBodyPart(attachmentPart);
}
}
message.setContent(multipart);
//登录SMTP服务器
if(StringUtils.isBlank(username)){
//不需验证
Transport.send(message);
}else{
//需要验证
Transport transport=session.getTransport("smtp");
transport.connect();
transport.sendMessage(message,message.getAllRecipients());
transport.close();
}
result[0]="0";
result[1]="发送成功";
logger.info("邮件发送成功!发送人:"+from);
}catch(MessagingException mex){
result[0]="1";
result[1]="邮件服务器发生错误";
if(mex instanceof AuthenticationFailedException){
result[1]="账号或密码错误";
}
}catch(Exception e){
result[0]="1";
result[1]="系统异常";
}
return result;
}
}
/**
*class MyAuthenticator用于邮件服务器认证 构造器需要账号、密码作参数
*/
class EmailAuthenticator extends Authenticator{
private String username=null;
private String password=null;
public EmailAuthenticator(String username,String password){
this.username=username;
this.password=password;
}
public PasswordAuthentication getPasswordAuthentication(){
return new PasswordAuthentication(username,password);
}
}第3步:建立配置文件config.properties。
代码4-22 config.properties
SMTPServer=SMTPServer名称
from=发送者Email
title=标题
username=账号
password=密码
port=SNMP端口号注:请替换您的邮件信息。
第4步:在Util.java中添加String SendMail(String contact,String message)方法。
代码4-23 Util.java
public String SendMail(String contact,String message){
try{
Util util=new Util();
Properties prop=util.getPropertiesInfo("config.properties");
//获取数据
String SNMPTServer=prop.getProperty("SMTPServer");
String from=prop.getProperty("from");
String to=contact;
String title=prop.getProperty("title");
String content=message;
String username=prop.getProperty("username");
String password=prop.getProperty("password");
String port=prop.getProperty("port");
List list=new ArrayList();
//发送Emial验证码
EmailSender sender=new EmailSender(SNMPTServer,from,to,title,content,list,username,password,port);
String [] result=sender.sendMail();
return result[1];
}catch(IOException ex){
logger.error("发Email验证码信息发生错误"+ex.getMessage());
}
return "0";
}在PasswordRecovery.java的sendEmailVerificationCode()方法中调用Util.SendMail()。
第二项:关于短信的实现
第1步:在pom.xml中添加相关依赖。
代码4-24 pom.xml
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>dysmsapi20170525</artifactId>
<version>3.1.2</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>tea</artifactId>
<version>1.1.14</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>4.5.16</version>
</dependency>第2步:创建配置文件sms.properties。
代码4-25 sms.properties
AccessKeyId=yourAccessKeyId
AccessKeySecret=yourAccessKeySecret注:替换您注册的AccessKeyId和AccessKeySecret。
第3步:创建包demo,并在该包下建立SendSms.java,如前面2.使用阿里云短信服务中的步骤3:编写Java代码发送短信所示。
第4步:在PasswordRecovery.java的sendSmsVerificationCode()方法中调用SMSSender.SendSMS()。
③ 与重置密码相关代码
代码4-26 PasswordRecovery.java
//重置用户密码。
public String recoverPassword(String identifyingCode,String newPassword,UserRepository mysql,int uid)throws SQLException{
//验证验证码
//判断验证码格式是否正确
ValidateUser validateuser=new ValidateUser();
String info_s=validateuser.valiidentifyingCode(identifyingCode);
if(!info_s.equals("验证成功")){
return info_s;
}
//判断验证码是否正确
int info_i=mysql.verifyCode(identifyingCode,uid);
if(info_i!=1){
return "验证码错误,请重新输入!";
}
//判断密码是否散列
if(!validateuser.isHash(newPassword)){
return "密码需要HASH散列";
}
//获取旧密码
String oldPassword=getOldPassword(mysql,uid);
if(!oldPassword.equals("0")){
//将旧密码放入Password表中
int info=storeOldPassword(oldPassword,mysql,uid);
if(info==-1){
return "-1";
}
//验证新密码是否使用过
boolean info_b=isPasswordUsed(newPassword,uid,mysql);
if(!info_b){
return "这个密码以前设置过,请用一个新密码!";
}
//重置密码
info_i=updatePassword(newPassword,oldPassword,mysql,uid);
if(info_i==-1){
return "-1";
}
}
return "0";
}
//根据手机或电子邮件获取用户ID。
public static String getUserIdByContact(String contact,UserRepository mysql)throws SQLException{
String result=mysql.queryForEmailOrPhone(contact);
return result;
}
//获得联系方法类型:Email还是手机
public String getContactType(String contact){
String phonePattern="^1[3-9]\\d{9}$";
String emailPattern="^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$";
if(Pattern.matches(phonePattern,contact)){
return "phone";
}else if(Pattern.matches(emailPattern,contact)){
return "email";
}else{
return null;
}
}
//通过手机短信发送验证码
private String sendSmsVerificationCode(String contact,int uidi,String code,UserRepository userRepository)throws Exception{
SendSms SMSSender=new SendSms();
boolean message=SMSSender.SendSMS(contact,code);
if(message){
if(userRepository.deleteCodeInDB(uidi)>=0){
userRepository.putCodeInDB(code,uidi);
return "验证码已发送至手机";
}else{
logger.error("从code表删除数据失败");
return "-1";
}
}else{
return "短信发送失败";
}
}
//通过邮箱发送验证码
public String sendEmailVerificationCode(String contact,String code){
String message="您刚请求的验证码是:"+code+",请妥善保存";
Util util=new Util();
String result=util.SendMail(contact,message);
if("发送成功".equals(result)){
return "验证码已发送至邮箱";
}else{
return "邮箱发送失败";
}
}
//检查新密码是否已被使用。
private boolean isPasswordUsed(String newPassword,int uid,UserRepository mysql){
//检查密码是否已使用的逻辑
int info=mysql.verifyPassword(newPassword,uid);
if(info==1){
return false;//返回使用过
}else{
return true;//返回没有使用过
}
}
//获取用户设置的旧密码。
private String getOldPassword(UserRepository mysql,int uid){
//获取旧密码
String oldPassword=mysql.getPassword(uid);
if((!oldPassword.equals("0"))&&(!oldPassword.equals("-1"))){
return oldPassword;
}else{
return "0";
}
}
//将旧密码存储到password表中。
public int storeOldPassword(String oldPassword,UserRepository mysql,int uid)throws SQLException{
int info=mysql.storeOldPassword(oldPassword,uid);
if(info!=1){
logger.error("系统在存储旧密码到password表的时候发生错误");
return-1;
}
return info;
}
//更新用户的新密码。
public int updatePassword(String newPassword,String oldPassword,UserRepository mysql,int uid)throws SQLException{
int info=mysql.updatePassword(newPassword,uid);
if(info!=1){
logger.error("系统在更新密码的时候发生错误");
return-1;
}
return info;
}5)在UserRepository.java中建立与数据库操作的方法
该模块包含以下八个方法,用于处理与数据库的交互。参见下面代码中每个函数前的注释。
代码4-27 UserRepository.java
//1.验证修改密码时提供的电子邮件或手机号码是否正确。
//查询到返回用户的ID,否则返回"您输入的手机号或Email不存在,请重新输入!"
public String queryForEmailOrPhone(String contact)throws SQLException{
String query="SELECT id FROM user WHERE phone=? OR email=?";
try(Connection conn=dataSource.getConnection();
PreparedStatement stmt=conn.prepareStatement(query)){
stmt.setString(1,contact);
stmt.setString(2,contact);
try(ResultSet rs=stmt.executeQuery()){
if(rs.next()){
logger.info("判断Email或phone是否存在成功");
return rs.getString("id");
}else{
logger.error("输入的手机号或Email不存在");
return "您输入的手机号或Email不存在,请重新输入!";
}
}
}catch(SQLException e){
logger.error("queryForEmailOrPhone(String contact)发生数据库错误:"+e.getMessage());
}
return "-1";
}
//2.将验证码存入code表。
//存入成功,返回操作记录的个数,通常为1,否则返回0
public int putCodeInDB(String code,int uid)throws SQLException{
String query="INSERT INTO code(uid,code)VALUES(?,?)";
//声明连接对象
try(Connection conn=dataSource.getConnection()){
//关闭自动提交模式,手动管理事务
conn.setAutoCommit(false);
try(PreparedStatement stmt=conn.prepareStatement(query)){
stmt.setInt(1,uid);
stmt.setString(2,code);
int affectedRows=stmt.executeUpdate();
if(affectedRows >0){
conn.commit();//提交事务
logger.info("把验证码放入code表中成功");
}else{
logger.error("把验证码放入code表中失败,请检查user ID是否存在");
}
return affectedRows;
}catch(SQLException e){
if(conn!=null){
try{
conn.rollback();//回滚事务
}catch(SQLException rollbackEx){
logger.error("putCodeInDB rollback 发生错误:"+rollbackEx.getMessage());
}
}
}
}
return-1;
}
//3.从code表中删除旧的验证码
//删除成功,返回操作记录的个数,通常为1,否则返回0
public int deleteCodeInDB(int uid)throws SQLException{
String query="DELETE FROM code WHERE uid=?";
try(Connection conn=dataSource.getConnection()){
try(PreparedStatement stmt=conn.prepareStatement(query)){
conn.setAutoCommit(false);//开启事务
stmt.setInt(1,uid);
int affectedRows=stmt.executeUpdate();
if(affectedRows >0){
conn.commit();//提交事务
logger.info("从code表中删除旧的验证码成功");
}else{
logger.info("code表中没有数据");
}
return affectedRows;
}catch(SQLException e){
logger.error("deleteCodeInDB发生数据库错误:"+e.getMessage());
return-1;//发生错误
}
}
}
//4.验证重置密码时输入的验证码是否正确。
//查询成功返回1,否者返回0
public int verifyCode(String code,int uid){
String query="SELECT * FROM code WHERE code=? AND uid=?";
try(Connection conn=dataSource.getConnection();
PreparedStatement stmt=conn.prepareStatement(query)){
stmt.setString(1,code);
stmt.setInt(2,uid);
try(ResultSet rs=stmt.executeQuery()){
if(!rs.next()){
logger.error("输入的验证码不正确");
return 0;//不存在
}else{
logger.info("输入的验证码正确");
return 1;//存在
}
}
}catch(SQLException e){
logger.error("verifyCode发生数据库错误:"+e.getMessage());
return-1;//发生错误
}
}
//5.检查重置的密码是否曾被使用过。
//新密码以前没有使用过,返回0,否者返回1
public int verifyPassword(String newPassword,int uid){
String query="SELECT * FROM password WHERE password=? AND uid=?";
try(Connection conn=dataSource.getConnection();
PreparedStatement stmt=conn.prepareStatement(query)){
stmt.setString(1,newPassword);
stmt.setInt(2,uid);
try(ResultSet rs=stmt.executeQuery()){
if(!rs.next()){
logger.info("新密码以前没有使用过");
return 0;//不存在
}else{
logger.error("新密码以前使用过");
return 1;//存在
}
}
}catch(SQLException e){
logger.error("verifyPassword发生数据库错误:"+e.getMessage());
return-1;//发生错误
}
}
//6.根据用户ID获取用户的密码
//获取成功,返回密码,否者返回0
public String getPassword(int uid){
String password=null;
String query="SELECT password FROM user WHERE id=?";
try(Connection conn=dataSource.getConnection();
PreparedStatement stmt=conn.prepareStatement(query)){
stmt.setInt(1,uid);
try(ResultSet rs=stmt.executeQuery()){
if(!rs.next()){
logger.error("通过id获得user表的密码失败");
return "0";//不存在
}else{
password=rs.getString("password");//存在
logger.info("通过id获得user表的密码成功");
}
}
}catch(SQLException e){
logger.error("getPassword 发生数据库错误:"+e.getMessage());
}
return password!=null?password:"-1";//返回密码或-1
}
//7.将旧密码保存到password表。
//存入成功,返回操作记录的个数,通常为1,否则返回0
public int storeOldPassword(String oldPassword,int uid)throws SQLException{
String insertQuery="INSERT INTO password(uid,password)VALUES(?,?)";
try(Connection conn=dataSource.getConnection()){
//关闭自动提交模式,手动管理事务
conn.setAutoCommit(false);
try(PreparedStatement stmt=conn.prepareStatement(insertQuery)){
stmt.setInt(1,uid);
stmt.setString(2,oldPassword);
int affectedRows=stmt.executeUpdate();
if(affectedRows >0){
conn.commit();//提交事务
logger.info("保存旧密码到 password 表成功");
return affectedRows;//正常返回受影响的行数
}else{
logger.error("保存旧密码到 password 表失败,请检查 user ID 是否存在");
return 0;//表示没有行被影响
}
}catch(SQLException e){
if(conn!=null){
try{
conn.rollback();//回滚事务
}catch(SQLException rollbackEx){
logger.error("storeOldPassword rollback 发生错误:"+rollbackEx.getMessage());
}
}
}
}
return-1;//如果发生异常则返回-1
}
//8.更新用户的新密码。
//更新成功,返回操作记录的个数,通常为1,否则返回0
public int updatePassword(String newPassword,int uid)throws SQLException{
String query="UPDATE user SET password=? WHERE id=?";
//从数据源获取连接
try(Connection conn=dataSource.getConnection()){
//关闭自动提交模式,手动管理事务
conn.setAutoCommit(false);
try(PreparedStatement stmt=conn.prepareStatement(query)){
stmt.setString(1,newPassword);
stmt.setInt(2,uid);
int affectedRows=stmt.executeUpdate();
if(affectedRows >0){
conn.commit();//提交事务
logger.info("密码更新成功");
}else{
logger.error("密码更新失败,请检查 user ID 是否存在");
return 0;//如果没有行受影响,返回0
}
return affectedRows;//返回受影响的行数
}catch(SQLException e){
if(conn!=null){
try{
conn.rollback();//回滚事务
}catch(SQLException rollbackEx){
logger.error("updatePassword rollback 发生错误:"+rollbackEx.getMessage());
}
}
}
}
return-1;//如果发生异常,则返回-1
}4.改造JSP文件
在本次改造中,将所有的GET请求用于与用户的交互,而POST请求则专注于业务逻辑处理。具体改造的页面包括RegisterPage.jsp、LoginPage.jsp、RecoverPage.jsp和VeriCodePage.jsp。相应的Java代码为:RegisterController.java、LoginController.java和PasswordRecoveryController.java。以下以RegisterPage.jsp和RegisterController.java为例进行说明。
1)改写RegisterPage.jsp
代码4-28 RegisterPage.jsp
if("get".equalsIgnoreCase(method)){
String error=request.getParameter("error");
if(error==null){
error="";
}
ct.setCookie();
out.println(util.repleatHTML(error));在处理GET请求时,首先检查error是否为null,如果是,则将其设置为空字符串。接着,设置cookie以防范CSRF攻击,最后显示错误信息(为了防止XSS攻击,需要对error中的HTML特除字符通过String util.repleatHTML(String)进行转义)。
2)改写RegisterController.java
代码4-29 RegisterController.java
if(info.equals("1")){
//成功转向下一页面
util.sendRedirect(response,request.getContextPath()+"/jsp/LoginPage.jsp");
}else{
//否者在原页面显示错误信息
util.sendRedirect(response,request.getContextPath()+"/jsp/RegisterPage.jsp","error",info);
}3)改写其他JSP文件
LoginPage.jsp、RecoverPage.jsp和VeriCodePage.jsp的改造方法与上述相似,在此不再赘述。
顾翔凡言:人工智能未来的发展瓶颈在于对知识的更新。唯一不变的是变化,知识发生了变化,人工智能软件能否及时跟进变化,可能阻碍人工智能的使用。