首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >带着ChatGPT玩转软件开发-连载22

带着ChatGPT玩转软件开发-连载22

作者头像
顾翔
发布2025-12-29 11:18:08
发布2025-12-29 11:18:08
1190
举报

顾老师新作“带着ChatGPT玩转软件开发”将于今年底明年初出版

4.4使用ChatGPT关于找回密码的代码评审

到目前为止,存在以下12个文件需要代码评审。

前端代码

Ø提炼出的HTML文件

◆VeriCodePage.jsp。

◆RecoverPage.jsp。

ØJavaScript文件

◆index.js。

后端代码

ØJSP文件

◆VeriCodePage.jsp。

◆RecoverPage.jsp。

ØJava文件

◆UserRepository.java。

◆PasswordRecoveryTest.java。

◆RecoverTest.java。

◆LoginController.java。

◆RegisterController.java。

测试文件

ØAPI测试代码:Test_Recover.py。

ØGUI测试代码:Test_Recover_GUI.py。

4.4.1前端找回密码代码评审

1.申请前端找回密码代码评审

提示词

请对下面前端代码进行代码评审

1)提炼出的HTML文件

①VeriCodePage.jsp

参看代码4-56 VeriCodePage.jsp

②RecoverPage.jsp

参看代码4-56 RecoverPage.jsp

2)index.js

参看代码4-7 index.js

2.前端找回密码代码评审回复

ChatGPT回答

对您提供的前端代码进行评审时,我将从代码结构、可读性、性能和安全性等方面进行分析。

1)提炼出的HTML文件

①VeriCodePage.jsp

1.优点

结构清晰:HTML结构简洁明了,包含必要的表单元素和标签。

使用了CSRF令牌:通过<input type="hidden">传递CSRF令牌,有助于提高。

2.改进建议

表单验证:建议在<form>标签中添加novalidate属性,以便使用JavaScript自定义验证,而不是浏览器的默认验证。

错误信息的可访问性:可以考虑为错误信息添加aria-live属性,以便屏幕阅读器能够及时通知用户。

CSS和JavaScript文件的相对路径:建议使用绝对路径或基于根的路径,以避免在不同目录中访问时出现问题。

注解。

暂不考虑在<form>标签中添加novalidate属性。

考虑到旧版本浏览器的兼容性,暂不添加aria-live属性。

如前所述,使用相对路径便于移植。

②RecoverPage.jsp

1.优点

表单设计:设计合理,包含必要的输入字段和错误提示区域。

重用代码:与VeriCodePage.jsp使用相同的CSS和JavaScript文件,避免重复代码。

2.改进建议

表单验证:同样可以考虑在<form>标签中添加novalidate属性。

错误信息的可访问性:同样建议为错误信息添加aria-live属性。

注解。

暂不考虑在<form>标签中添加novalidate属性。

考虑到旧版本浏览器的兼容性,暂不添加aria-live属性。

2)index.js

1.优点

模块化函数:将验证逻辑分散到多个函数中,使得代码更易于维护和理解。

正则表达式使用:使用正则表达式进行输入验证是一个良好的实践。

2.改进建议

全局变量:在JavaScript中,避免使用全局变量(如phone),应使用let或const声明变量以确保其作用域。

函数命名:函数命名可以更具描述性,例如CheckField()可以改为validateField()以提高可读性。

错误处理:在hash256()和其他函数中,建议增加错误处理逻辑,以便捕获和处理可能的异常。

性能优化:在check和checkRecover函数中,重复调用。

document.getElementById可以通过将其结果存储在变量中来优化性能。

代码注释:增加代码注释,特别是在复杂的逻辑部分,以便其他开发者更容易理解代码的意图。

注解。

修改

代码4-58index.js

代码语言:javascript
复制
phone=CheckFieldByRecover(phonePattern,contact,isValid);
email=CheckFieldByRecover(emailPattern,contact,isValid);

代码语言:javascript
复制
let phone=CheckFieldByRecover(phonePattern,contact,isValid);
let email=CheckFieldByRecover(emailPattern,contact,isValid);

将CheckField()方法改为validateField()。

下一个版本将考虑在hash256()和其他函数中增加错误处理逻辑。

暂时不考虑在check和checkRecover函数中,重复调用。

下一个版本将考虑通过将document.getElementById的结果存储在变量中来优化性能。

下一个版本将考虑优化代码注释。

3.安全性

密码哈希:在hash256()函数中,密码在客户端进行哈希处理,虽然可以增加一定的安全性,但仍然建议在服务器端进行密码哈希,以确保安全性。

输入验证:确保所有用户输入在服务器端也进行验证,以防止恶意输入。

HTTPS:确保所有数据传输通过HTTPS进行,以保护用户数据的安全。

注解。

正如前面提到的,客户端和服务器端都对密码进行了哈希处理,因此不进行修改。

正如前面提到的,客户端和服务器端都对用户输入数据进行了验证。

下一版本将考虑确保所有数据传输通过HTTPS进行。

4.总结

整体而言,您的代码结构清晰,功能实现合理,但在可读性、性能和安全性方面还有进一步优化的空间。通过遵循上述建议,您可以提高代码的可维护性和安全性。如果您有任何具体问题或需要进一步的帮助,请随时告知!

4.4.2找回密码后端代码评审

1.JSP文件代码评审

1)申请JSP文件进行代码评审

提示词

请对以下Java后端代码进行评审。

VeriCodePage.jsp

参看代码4-56 VeriCodePage.jsp

RecoverPage.jsp

参看代码4-56 RecoverPage.jsp

2)JSP文件代码评审回复

ChatGPT回复

代码评审-VeriCodePage.jsp和RecoverPage.jsp

1.优点

CSRF Token机制:两段代码均实现了CSRF Token的生成与验证,增强了安全性。

表单验证:通过JavaScript的onsubmit()事件,增加了客户端的表单验证。

结构清晰:代码结构相对清晰,易于理解和维护。

2.改进建议。

CSRF Token生成与处理。

当前的CSRF Token是在JSP页面中生成的,建议将其生成逻辑封装到后端控制器中,以确保更高的安全性和一致性。

在CookiesManager类中,建议提供一个方法用于生成Token,并在构造函数中仅传递必要的参数。

注解。

下一版本将在JavaBean中生成CSRF Token。

下一版本将提供一个方法用于生成Token。

3.Cookie设置。

CookiesManager的构造函数中传入csrftoken,但实际设置Cookie的逻辑在setCookie()方法中。建议将Cookie的设置逻辑集中在CookiesManager中,避免在JSP中重复代码。

setSecure(true)应根据实际部署环境动态设置,确保Cookie只在HTTPS环境下使用。

注解。

下一版本将把Cookie的设置逻辑集中到CookiesManager中。

下一版本将根据实际部署环境动态设置setSecure(true),并使用HTTPS。

4.错误处理。

目前的错误处理仅通过out.println输出错误信息,建议使用更健壮的错误处理机制,例如抛出异常并在用户界面上显示友好的错误信息。

使用日志框架(如SLF4J或Log4J)记录错误信息,而不是直接在页面输出。

注解。

下一版本改进错误处理机制。

下一版本考虑在JSP页面采用Log4J日志框架。

5.SQL注入和XSS防护。

在处理用户输入(如request.getParameter())时,应进行适当的验证和清理,以防止SQL注入和XSS攻击。

例如,对于用户输入的contact和identifyingCode,应使用合适的验证逻辑。

注解:前端和后端均已对用户输入进行了验证,以防止SQL注入和XSS攻击。

6.代码重复。

VeriCodePage.jsp和RecoverPage.jsp中有大量重复代码(如CSRF Token处理),建议将其提取到一个公共的JSP文件或方法中,以减少代码重复,提高可维护性。

注解:下一版本将提取VeriCodePage.jsp和RecoverPage.jsp中的重复代码到一个公共JSP文件或JavaBean方法中。

7.JavaScript代码的安全性。

在客户端进行表单验证时,确保JavaScript代码的安全性,避免潜在的XSS攻击。

可以考虑使用CSP(内容安全策略)来进一步保护页面。

注解。

JavaScript中已进行验证,以避免XSS攻击。

下一版本将改进使用内容安全策略(CSP)。

8.页面编码。

在输出用户输入时,建议使用out.print()或out.write()的HTML编码,以防止XSS攻击。例如,可以使用StringEscapeUtils.escapeHtml4()。

注解:下一版本将使用StringEscapeUtils.escapeHtml4()。

9.使用JSP EL 和 JSTL。

考虑使用JSP EL和JSTL来简化JSP页面中的Java代码,使页面更清晰,逻辑更分离。

注解:下一版本将使用JSP EL和JSTL。

10示例改进(部分)。

代码语言:javascript
复制
<%@page contentType="text/html;charset=utf-8"%>
<%@page import="ebusiness.Util,ebusiness.CookiesManager,ebusiness.PasswordRecoveryController"%>
<%@page import="org.apache.commons.text.StringEscapeUtils"%>
<%
  CookiesManager ct=new CookiesManager(response,request);
  String csrftoken=ct.generateCsrfToken();//假设在 CookiesManager 中实现
  ct.setCookie(csrftoken);
%>
<div id="recoverError" class="error">
<%
  String method=request.getMethod();
  if("post".equalsIgnoreCase(method)){
    String csrftoken_post=request.getParameter("csrftoken");
    String csrftoken_cookies=ct.getCookie();
  if(csrftoken_cookies!=null&&csrftoken_cookies.equals(csrftoken_post)){
    PasswordRecoveryController recover=new PasswordRecoveryController(response,request);
    recover.recoverPassword();
    }else{
    out.println("可能存在CSRF攻击风险");
    }
  }
%>
</div>

总结

代码实现了基本的CSRF防护机制,但在安全性和可维护性方面存在改进空间。

通过提取公共逻辑、增强错误处理和输入验证、动态设置Cookie 属性等方式,可以有效提升整体安全性和代码质量。

2.Java文件代码评审

1)申请Java文件进行代码评审

评审前先做如下改动。

第1步:UserRepository.java的Connection connect()方法中改为。

代码4-59 UserRepository.java

代码语言:javascript
复制
…
Util util=new Util();
Properties prop=util.getPropertiesInfo("jdbc.properties");
String driver=prop.getProperty("driver");
String url=prop.getProperty("url");
String user=prop.getProperty("user");
String password=prop.getProperty("password");
Class.forName(driver);
Connection conn=DriverManager.getConnection(url,user,password);
…

第2步:Util.java的SendMail(String contact,String message)方法中改为。

代码4-60 Util.java

代码语言:javascript
复制
public boolean SendMail(String contact,String message){
try{
Properties prop=util.getPropertiesInfo("config.properties");
//获取数据
String SNMPTServer=prop.getProperty("SNMPTServer");
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(SMTPServer,from,to,title,content,list,username,password,port);
String [] result=sender.sendMail();
if (result[1].equals("发送成功")){
return true;
		}else{
return false;
		}	
	}catch (IOException ex){
		logger.error("发Email验证码信息发生错误" + ex.getMessage());
	}
return false;
}

第3步:使用try-with-resources格式修改所有SQL语句,比如。

代码语言:javascript
复制
String insertString="INSERT INTO user(username,password,phone,email)VALUES(?,?,?,?)";
try (Connection conn=dataSource.getConnection(); 
	PreparedStatement stmt=conn.prepareStatement(insertString)){
…
try (ResultSet rs=stmt.executeQuery()){
…

提示词

请对以下Java后端代码进行评审。

① UserRepository.java

参看代码4-59 UserRepository.java

② PasswordRecovery.java

参看代码4-26 PasswordRecovery.java

③ PasswordRecoveryController.java

参看代码4-32 PasswordRecoveryController.java

④ LoginController.java

参看代码3-58 LoginController.java

⑤ RegisterController.java

参看代码3-54 RegisterController.java

2)Java文件代码评审回复

ChatGPT回答

对您提供的Java后端代码进行评审时,我将从代码结构、可读性、性能、安全性等方面进行分析。

①UserRepository.java

这段代码实现了一个用户存储库类UserRepository,用于处理用户的各种操作,包括注册、登录、密码管理等。以下是针对该代码的一些评审和建议,涵盖了可读性、健壮性、安全性和性能等方面。

1.可读性和结构

方法命名:方法名称清晰地表达了其意图,例如insert,userExists,login等,使代码可读性较强。

日志记录:日志记录很详细,能帮助开发者追踪问题,尤其是SQL操作方面的错误。

2.异常处理

目前的异常处理可以提高。尽量不要捕捉SQLException后继续返回错误代码,考虑利用自定义异常来处理错误,这样能更好地向调用者传达错误信息,同时也能避免隐藏错误。

3.事务管理

事务的使用有些不一致。例如在执行queryForEmailOrPhone()和deleteCodeInDB()等方法时,如果开启了setAutoCommit(false),但在try-with-resources的上下文中。实际上,许多操作可以在不控制事务的情况下进行,因为它们是单一的查询或插入操作。

注解。

下一版本考虑利用自定义异常来处理错误。

改造String getPassword(int uid)。

代码4-61 UserRepository.java

代码语言:javascript
复制
//通过User ID获得user表的密码
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
	}

4.SQL注入安全

尽管使用了PreparedStatement,这已经很好地防止了SQL注入,但在某些地方,依然存在对用户输入的的不必要的信任(如在judgeInfo()里使用field直接拼接成SQL语句),需要严密检查field参数的合法性,避免SQL注入的风险。

注解:在

代码语言:javascript
复制
String queryString="SELECT * FROM user WHERE "+field+"=?";

前面加入判断。

代码语言:javascript
复制
  if(!field.equals("username")||!field.equals("phone")||!field.equals("email")
{
	logger.info("user表"+field+"字段不存在"+value+"值");
return "0";
  }else{
String queryString="SELECT * FROM user WHERE "+field+"=?";
  }

5.字段唯一性验证

userExists()方法会多次调用judgeInfo(),可以考虑使用一个方法来接收一个字段列表以及对应的值,来减少数据库的访问,提高性能。

注解:下一版本考虑使用一个方法来接收一个字段列表以及对应的值。

6.密码管理

登录过程中,密码是以明文存储和验证的,这是一个潜在的安全隐患。建议使用加密算法(例如BCrypt、PBKDF2等)来securely storage密码,确保用户密码的安全性。

注解:正如前述,数据库中密码使用SHA256存储。

7.返回值设计

方法返回值的设计也可以做一定改善。例如,返回字符串表示的结果并不直观,尤其是多个方法对于成功和失败的返回值会造成调用者理解的难度,可以考虑使用自定义的响应对象,例如Result或者ServiceResponse。

注解:下一版本考虑方法返回值的设计优化。

8.代码复用

某些逻辑(例如查询操作)可以提取为通用方法,以减少代码重复。例如,重复的连接获取和PreparedStatemen的创建,可以考虑封装成一个方法。

注解:下一版本考虑提取为通用方法。

9.关闭资源

使用try-with-resources保证PreparedStatement和ResultSet的关闭很重要。但是在某些捕捉块外仍需确认连接被正确关闭。

注解:确认后不存在连接没有被正确关闭。

10.代码注释

尽量保持注释的更新,确保注释与代码逻辑保持一致。同时,尽量减少注释的使用,通过清晰的代码结构和命名提升代码自解释性。

注解:下一版本考虑代码注释的优化。

11.Magic Numbers

代码中使用了一些数值,比如最大连接数、超时时间等,建议使用常量定义。

注解:下一版本考虑解决Magic Numbers。

12.改进后的示例

以下是部分改进示例。

代码4-62 UserRepository.java

代码语言:javascript
复制
public String userExists(String username,String email,String phone){
//页面的逻辑可能更易于理解
String result=judgeInfo("username",username);
if(!"0".equals(result))return "注册用户的账号必须唯一";
	result=judgeInfo("phone",phone);
if(!"0".equals(result))return "注册用户的手机必须唯一";
	result=judgeInfo("email",email);
if(!"0".equals(result))return "注册用户的邮箱必须唯一";
return "0";
}

注解:按上述方法修改,测试通过。

13.总结

这个UserRepository类实现了基本的用户管理功能,整体结构清晰,但在安全性、健壮性及性能方面还有机会改进。重点关注异常处理、事务管理,确保用户数据安全,加密存储敏感信息,以及提高代码复用率来优化代码结构。

②PasswordRecovery.java

以下是对你提供的Java后端代码的评审以及一些改进建议。

1.代码结构和可读性

清晰的命名

Ø此代码中变量名和方法名基本上是自解释的,但某些地方如info_i,info_s可以改为更具描述性的名称(如verificationResult,validationResult),被引用的地方也需要更改。

注解。

第1步:将

代码4-63 PasswordRecovery.java

代码语言:javascript
复制
//判断验证码是否正确
int info_i=mysql.verifyCode(identifyingCode,uid);
if(info_i!=1){
return "验证码错误,请重新输入!";
}

修改为。

代码语言:javascript
复制
//重置密码
info_i=updatePassword(newPassword,oldPassword,mysql,uid);
if(info_i==-1){
return "-1";
}

第2步:将

代码语言:javascript
复制
//重置密码
info_i=updatePassword(newPassword,oldPassword,mysql,uid);
if(info_i==-1){
  return "-1";
}

修改为。

代码语言:javascript
复制
//重置密码
updatePasswordInfo=updatePassword(newPassword,oldPassword,mysql,uid);
if(updatePasswordInfo==-1){
  return "-1";
}

第3步:将

代码语言:javascript
复制
//验证新密码是否使用过
boolean info_b=isPasswordUsed(newPassword,uid,mysql);
if(!info_b){
  return "这个密码以前设置过,请用一个新密码!";
}

修改为。

代码语言:javascript
复制
//验证新密码是否使用过
boolean isPasswordUsedInfo=isPasswordUsed(newPassword,uid,mysql);
if(!isPasswordUsedInfo){
  return "这个密码以前设置过,请用一个新密码!";
}

第4步:将

代码语言:javascript
复制
int info=mysql.deleteCodeInDB(uidi);
if(info==1 ||info==0){ 
  //将新的验证码记录放入password

修改为。

代码语言:javascript
复制
int deleteCodeInDBInfo=mysql.deleteCodeInDB(uidi);
if(deleteCodeInDBInfo==1||deleteCodeInDBInfo==0){
  //将新的验证码记录放入password

2.提取方法

sendVerificationCode()方法较长且具有多个关注点。建议将其拆分成多个小方法,例如。

代码语言:javascript
复制
handleSmsVerification(String contact,int uid,String code)
handleEmailVerification(String contact,int uid,String code)

这样可以提高代码的可读性和可重用性。

注解:改写sendVerificationCode()。

代码4-63 PasswordRecovery.java

代码语言:javascript
复制
//通过手机号或Email发送验证码
public String sendVerificationCode(String contact)throws Exception{
		String contactType=getContactType(contact);
if(contactType==null){
return "您输入的手机号或Email格式不正确!";
		}
		UserRepository userRepository=new UserRepository();
		String uid=getUserIdByContact(contact,userRepository);
if(!uid.matches("\\d+")){
return uid;
		}
		int uidi=Integer.parseInt(uid);
		String code=util.generateRandomString(6,"int");
		UserRepository mysql=new UserRepository();
		HikariDataSource dataSource=mysql.dataSource;
if(contactType.equals("phone")){
return sendCodeForSMS(contact,uidi,code,userRepository,mysql,uid);
		}else if(contactType.equals("email")){
return sendCodeForEmail(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{
if(sendSmsVerificationCode(contact,uidi,code,mysql).equals("验证码已发送至手机")){
//清除以前code表中的验证码记录
			int info=mysql.deleteCodeinDB(uidi);
if(info==1 ||info==0){
//将新的验证码记录放入code表
				mysql.putCodeInDB(code,uidi);
return uid;
			}else{
				logger.error("从code删除数据失败");
return "-1";
			}
		}else{
			logger.error("短信发送失败!");
return "-1";
		}
	}
//通过Email发送验证码
private String sendCodeForEmial(String contact,String code,int uidi,String uid,UserRepository mysql)throws Exception{
if(sendEmailVerificationCode(contact,code).equals("验证码已发送至邮箱")){
			int info=mysql.deleteCodeinDB(uidi);
if(info==1 ||info==0){
//将新的验证码记录放入password
				mysql.putCodeInDB(code,uidi);
return uid;
			}else{
				logger.error("从code删除数据失败");
return "-1";
			}
		}else{
			logger.error("邮件发送失败!");
return "-1";
		}
	}

拆分为

代码语言:javascript
复制
sendCodeForSMS(String contact,int uidi,String code,UserRepository mysql,String uid)

代码语言:javascript
复制
sendCodeForEmail(String contact,String code,int uidi,String uid,UserRepository mysql)

提取sendCodeForEmail()和sendCodeForSMS()公共部分。

代码4-64 PasswordRecovery.java

代码语言:javascript
复制
private String sendCodeForSMS(String contact,int uidi,String code,UserRepository mysql,String uid)throws Exception{
return sendCode(contact,mysql,uidi,uid,code,"SMS");	
	}
private String sendCodeForEmail(String contact,String code,int uidi,String uid,UserRepository mysql)throws Exception{
return sendCode(contact,mysql,uidi,uid,code,"Email");
	}
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";
}
}

3.更好的异常处理

throws Exception不明确,建议使用具体的异常类型(如SQLException,RuntimeException)或进行更细致的异常处理。

使用try-catch语句进行异常处理,并在发生异常时记录日志,避免程序因为未捕获的异常而崩溃。

注解。

下一版本将优化异常处理机制。

将sendVerificationCode(String contact)修改为。

代码4-65 PasswordRecovery.java

代码语言:javascript
复制
public String sendVerificationCode(String contact)throws Exception{
String contactType=getContactType(contact);
if(contactType==null){
return "您输入的手机号或Email格式不正确!";
	}		
UserRepository mysql=new UserRepository();
try(HikariDataSource dataSource=mysql.dataSource){
String uid=getUserIdByContact(contact,mysql);
		…
}

4.数据库连接池管理

资源管理

Ø数据库连接池在多个地方被关闭,这看似是为了防止泄漏,但过于频繁地开关连接相对较浪费资源。考虑在类的构造函数或 sendVerificationCode()方法之外进行连接池的初始化和关闭。

使用try-with-resources

Ø优化资源管理,可以使用try-with-resources来确保即使出现异常也能正确关闭数据库连接。

注解。

下一版本将解决数据库连接池多次关闭的问题。

下一版本将采用try-with-resources语法优化这段代码。

5.逻辑和业务验证

重复代码

Ømysql.deleteCodeInDB(uidi)和之后的逻辑在两种发送方式(短信与邮件)中均出现,建议提取成一个单独方法以减少重复。

返回值一致性

Ø方法返回多种不同格式(字符串、整数),不应使用类似"-1"或"0"这样的魔法值。建议使用标准的返回结构(如一个封装类)来提供更清晰的错误消息和状态。

注解。

下一版本将合并多处出现的mysql.deleteCodeInDB(uidi)调用。

下一版本将魔法值替换为具有实际意义的常量。

6.验证码生成

在验证码生成方面,generateRandomString()接收的参数为"int",这种方式有些不清晰,可以考虑使用枚举或更具体的方法名以提高可读性。

注解:generateRandomString()的参数为"int",指示随机字符串的长度,暂不作优化。

7.安全性

密码处理

Ø在当前输出中并未看到散列操作,当检查密码时是否确保已安全地散列存储。确保密码不可逆散列,并在验证密码时同步处理散列。

Ø在代码中增加对密码复杂度的检查,以避免简单或易猜测的密码。

注解:如前所述,密码在传输和存储过程中均进行了哈希处理。

8.测试

单元测试

Ø建议为每个公共方法编写单元测试,以确保其功能在未来的修改中不被意外破坏。

注解:已为所有公共方法编写单元测试脚本,且均已通过测试。

9.总结

代码的逻辑和功能相对清晰,但在可维护性、异常处理和资源管理方面还有改进空间。通过对代码结构及逻辑进行重构可以提升代码的可读性和安全性。同时,确保编写单元测试以确保功能的一致性和可靠性是非常重要的。

③PasswordRecoveryController.java

这段Java后端代码是一种典型的控制器实现,用于处理用户的密码恢复请求。在进行代码评审时,以下是一些比较重要的关注点和建议。

1.代码结构和可读性

类命名与职责:类名PasswordRecoveryController明确指出了控制器的目的,符合SRP原则,有助于理解类的功能。

方法命名:方法sendVerificationCode()和recoverPassword()名称能清晰表明其用途,但可以考虑使用动词"send"和"recover"前缀以提高一致性,比如sendVerificationCode()和resetPassword()。

注解:将暂时保留recoverPassword()方法名称。

2.异常处理

异常捕获:sendVerificationCode()和recoverPassword()内都仅捕获通用Exception,考虑具体化捕获异常类型并分别处理逻辑,比如SQLException、IOException,以便更好地理解和调试。

异常记录:在捕捉异常后,仅调用e.printStackTrace(),建议增加对此异常的记录或者采用统一的错误响应格式返回,这样可以提高系统的信息化和可维护性。

注解。

下一版本将改善异常处理机制。

去掉

代码4-66 PasswordRecoveryController.java

代码语言:javascript
复制
logger.error("发送验证码的时候发生错误:"+e.getMessage());

保留

代码语言:javascript
复制
logger.error("XXXX的时候发生错误:"+e.getMessage());

3.资源管理

数据库连接池的关闭:在recoverPassword()方法中,调用util.closePool(dataSource);可能引发问题,尤其是在方法执行过程中出现异常的情况下,连接池未必能够被正确关闭。应考虑使用try-with-resources语句或在finally代码块中确保连接池关闭。

注解:修改

代码4-67 PasswordRecoveryController.java

代码语言:javascript
复制
//重置密码
public String recoverPassword()throws SQLException,IOException{
	…
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);

修改为。

代码语言:javascript
复制
//重置密码
public String recoverPassword()throws SQLException,IOException{
	…
//更新新密码
try(HikariDataSource dataSource=mysql.dataSource){
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);
			}
		}catch(Exception e){
			logger.error("userInfoIsInviald()数据库发生错误:"+e.getMessage());
		}
	}
return "0";
}

这样当语句结束dataSource被自动关闭。

4.安全性

敏感信息处理:在记录密码重置信息时,确定不记录敏感信息,如密码明文等。

验证码的安全性:若用于验证码的用户标识(uid)使用cookie,考虑对cookie设置HttpOnly和Secure标志,以增加安全性。

输入验证:没有对参数contact、identifyingCode和newPassword进行充分的输入验证和过滤。考虑添加输入验证以防止出现SQL注入、跨站脚本等安全问题。

注解。

敏感信息不被记录。

当前不考虑使用cookie存储验证码的用户标识(uid)。

前后端均已对输入参数进行验证,确保不发生SQL注入和跨站脚本等安全问题。

5.逻辑处理

重定向处理:每次sendRedirect()调用后,方法就会返回,可能导致部分代码未被执行。确保逻辑简洁,避免多余逻辑执行。

引入常量:在代码中使用的字符串,如"uid""error"和不同的页面URL等,建议定义成常量,以便于维护和避免硬编码字符串错误。

注解。

下一版本将处理重定向相关的问题。

暂时不考虑将"uid""error"和不同页面URL定义为常量。

6.依赖管理

依赖注入:考虑依赖注入的方式来传递HttpServletResponse和HttpServletRequest,这可以增强可测试性和灵活性。

分页逻辑:在调用数据库相关的方法前,考虑提前实例化数据库访问组件,而不是在每个请求中都实例化。

注解。

暂时不考虑通过依赖注入方式传递HttpServletResponse和HttpServletRequest。

目前不考虑实现分页逻辑。

7.代码重复

URLEncoder.encode(info,"UTF-8")和response.sendRedirect(url)的这种重复逻辑可以考虑提取为单独的方法,以避免代码重复。

注解:下一版本将解决URLEncoder.encode(info,"UTF-8")和response.sendRedirect(url)重复逻辑的问题。

8.总结

该代码实现了基本的密码恢复功能,结构清晰,但在资源管理、安全性、异常处理和性能优化等方面可以进一步增强。如果能整合上述建议,将使代码更具可读性、安全性和维护性

④LoginController.java

这段Java后端代码实现了用户登录功能,整体结构清晰,但仍存在一些可改进之处。以下是对代码的评审及建议。

1.代码结构与可读性

方法命名:方法名performLogin()清晰表明其功能,但可以考虑更简化的命名,比如login(),让它更加简洁。

变量命名:变量validationResult和info的命名比较模糊,可以考虑使用更加具描述性的名字,比如validationMessage和errorMessage,以提高可读性。

注解。

保留performLogin()方法名。

将validationResult修改为validationMessage;将info修改为errorMessage。

2.异常处理

异常捕获:在performLogin()方法中,捕获了所有异常并只记录错误信息。应考虑细化异常类型(如SQLException或与登录逻辑相关的具体异常),并相应地处理以提高系统的健壮性。

异常响应:不仅记录异常,同时可以通过返回给用户友好的错误消息来提升用户体验。

注解。

在下一版本中添加异常捕获。

在下一版本中完善异常响应处理。

3.资源管理

数据库连接管理:在UserRepository类的login()方法中进行数据库操作时,应确保连接在使用后适当地关闭,避免连接泄漏。当前代码可通过try-with-resources语句增加代码的安全性。

注解:优化一下代码,将performLogin()拆分为void validatePass(User user,String errorMessage,String username)和void validatefail(String errorMessage)。

代码4-68 LoginController.java

代码语言:javascript
复制
public String performLogin()throws SQLException,IOException{
String username=request.getParameter("username");
String password=request.getParameter("password");	
User user=new User(username,password);
ValidateUser ValidateUser=new ValidateUser();
String validationMessage=ValidateUser.validate(user);
String errorMessage="";
if(validationMessage.equals("验证成功")){
validatePass(user,errorMessage,username);
	}else{
		errorMessage=validationMessage;
validatefail(errorMessage);
	}
return "0";
}
private void validatePass(User user,String errorMessage,String username)throws SQLException,IOException{
Util util=new Util();
UserRepository mysql=new UserRepository();
HikariDataSource dataSource=mysql.dataSource;
try{
if(mysql.login(user).equals("0")){
String url="/jsp/LoginPage.jsp";
			errorMessage="账号或密码错误";
util.sendRedirect(response,request.getContextPath()+url,"error",errorMessage);
		}else{
CookiesManager myusername=new CookiesManager("username",username,response,request);
			myusername.setCookie();
String url="/jsp/WelcomePage.jsp";
			util.sendRedirect(response,request.getContextPath()+url);
		}
	}catch(Exception e){
		logger.error("validatePass()发生错误:"+e.getMessage());
	}finally{
			util.closePool(dataSource);
	}
}
private void validatefail(String errorMessage)throws IOException{
Util util=new Util();
String url="/jsp/LoginPage.jsp";
	util.sendRedirect(response,request.getContextPath()+url,"error",errorMessage);
}

将void validatePass(User user,String errorMessage,String username)修改为。

代码4-69 LoginController.java

代码语言:javascript
复制
private void validatePass(User user,String errorMessage,String username)throws SQLException,IOException{
Util util=new Util();
UserRepository mysql=new UserRepository();
try(HikariDataSource dataSource=mysql.dataSource){
if(mysql.login(user).equals("0")){
			…
	}catch(Exception e){
		logger.error("validatePass()发生错误:"+e.getMessage());
	}
}

4.输入验证与安全性

输入验证:虽然注释提到前后端都有输入验证,但在此代码段中没有执行任何输入验证。建议至少检查账号和密码是否为空,以避免不必要的数据库调用。

敏感信息处理:在错误处理时,输出的错误信息(如“账号或密码错误”)可能会泄露系统信息。考虑将其模糊化,例如,只提示“登录失败”。

注解。

有专门的模块处理前后端验证输入问题。

前端。

代码4-70 index.js

代码语言:javascript
复制
function check(username,password,confirmPassword,phone,email){
…
}
function checkLogin(username,password){
…
}
function checkVeriCode(contact){
…
}

后端。

代码4-71 ValidateUser.java

代码语言:javascript
复制
public boolean isHash(String str){
  …
}

 public String validate(User user){
  …
}

在下一版本中考虑细化提示信息。

5.逻辑处理

重定向处理:调用response.sendRedirect()后,方法可以提前返回,以防止后续的代码逻辑被执行。建议在重定向后直接返回,以保持逻辑简洁。

response.sendRedirect(url);

return;

注解:方法的返回类型设为void,无需使用return。

6.代码重复

重复的URLEncoder编码:在多个地方使用了URLEncoder.encode(info,"UTF-8"),可以考虑提取为一个独立的私有方法来提高可维护性。

字符串比较:使用equals方法时应考虑使用StringUtils等工具类,保证安全与简洁。

注解。

在下一版本中考虑对重复的URLEncoder编码进行优化。

暂不进行使用StringUtils等工具类处理字符串比较。

7.依赖管理

限制直接实例化:在performLogin()方法中直接创建UserRepository和ValidateUser的实例,可考虑通过依赖注入来增强代码的灵活性和可测试性。

8.Cookies管理

Cookie设置:在CookiesManager的使用中,未看到对Cookie的有效期设置,建议明确设置有效期、路径和其他必要的属性,以确保Cookie能有效地在需求范围内保持。

注解:在CookiesManager类中定义有效期、路径及其他必要属性。

代码4-72 CookiesManager.java

代码语言:javascript
复制
//设置cookies的方法
public void setCookie(){
if(this.cookies_value!=null&&!this.cookies_value.isEmpty()){
try{
//创建一个 Cookie
		Cookie cookie=new Cookie(this.cookies_name,this.cookies_value);	
//设置 Cookie 的路径
		cookie.setPath(request.getContextPath());//确保在应用的上下文中可用 
//cookie.setDomain("192.168.31.184");//注意:这可能在某些浏览器中不被支持
//可选:设置 Cookie 的过期时间(单位:秒)
		cookie.setMaxAge(60*60*24);//1天 
//可选:设置 Cookie 的 HttpOnly 属性
		cookie.setHttpOnly(true);
//将 Cookie 添加到响应中
		response.addCookie(cookie);
	}catch(Exception e){
		logger.error("setCookie()发生错误:"+e.getMessage());
	}
	}else{
//处理cookies为空的情况
	logger.info("cookie 为空.");
}
}

9.总结

这段代码实现了基本的用户登录功能,结构清晰,但在异常处理、安全性、资源管理等方面还有提升空间。如果能考虑上述建议,将使代码更具可读性、安全性和可维护性。

⑤RegisterController.java

下面是对提供的Java后端代码RegisterController进行评审的结果,包括潜在问题、改进建议和最佳实践。

1.代码结构和可读性

注释:代码缺乏适当的注释,为关键部分和算法提供注释会提高可读性和可维护性。

方法长度:performRegister()方法较长,考虑将其拆分成多个小方法以提高可读性。

注解。

在下一版本中优化注释。

将performRegister()方法拆分出两个方法:User getUser()和void userInfoIsInviald(User user),具体实现如下。

代码4-73 RegisterController.java

代码语言:javascript
复制
public String performRegister()throws SQLException,IOException{
User user=getUser();
ValidateUser ValidateUser=new ValidateUser();
String validationResult=ValidateUser.validate(user);
Util util=new Util();
if(!validationResult.equals("验证成功")){
	util.sendRedirect(response,request.getContextPath()+"/jsp/RegisterPage.jsp","error",validationResult);
}else{
userInfoIsInviald(user);
}
return "0";
}
private User getUser(){
String username=this.request.getParameter("username");
String password=this.request.getParameter("password");
String phone=this.request.getParameter("phone");
String email=this.request.getParameter("email");
User user=new User(username,password,phone,email);
return user;
}
private void userInfoIsInviald(User user)throws SQLException,IOException{
Util util=new Util();
UserRepository mysql=new UserRepository();
HikariDataSource dataSource=mysql.dataSource;
try{
String info=mysql.insert(user);
if(info.equals("1")){
		util.sendRedirect(response,request.getContextPath()+"/jsp/LoginPage.jsp");
	}else{
util.sendRedirect(response,request.getContextPath()+"/jsp/RegisterPage.jsp","error",info);
	}
	}catch(Exception e){
		logger.error("userInfoIsInviald()数据库发生错误:"+e.getMessage());
	}finally{
		util.closePool(dataSource);
	}
}

2.错误处理

异常捕获:目前代码只记录了SQLException和IOException,并未给出具体的用户反馈。建议将异常信息返回给用户,或在日志中详细记录。

具体异常捕获:不建议使用通用的Exception捕获,应该更具体化,例如使用SQLException。如果有必要,使用多个catch块来处理不同类型的异常。

注解。

下一版本细化异常信息。

下一版本优化具体的异常捕获。

3.表单数据处理

参数验证:代码中没有对用户输入的有效性进行全面验证(例如空检查、格式检查等)。考虑使用更全面的验证逻辑。

敏感信息处理:密码需要特别处理,比如加密存储而不是明文存储。

注解:如LoginController.java代码审核反馈,前后端都对用户输入进行了校验。

4.URL编码和重定向

URL编码:URLEncoder.encode应该在try语句中单独处理以捕获可能的异常。

重定向的最佳实践:使用HttpServletResponse的sendRedirect()方法时,建议确保URL不会引导用户到预期外的页面。

注解。

重构代码,不用URLEncoder.encode。

代码4-74 RegisterController.java

代码语言:javascript
复制
package ebusiness;
import java.io.IOException;
import java.sql.SQLException;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import com.zaxxer.hikari.HikariDataSource;
import javax.servlet.http.HttpServletRequest;
public class RegisterController{
private static final Logger logger=Logger.getLogger(RegisterController.class);
private HttpServletResponse response;
private HttpServletRequest request;
public RegisterController(HttpServletResponse response,HttpServletRequest request){
this.response=response;
this.request=request;
	}
public String performRegister()throws SQLException,IOException{
User user=getUser();
ValidateUser ValidateUser=new ValidateUser();
String validationResult=ValidateUser.validate(user);
Util util=new Util();
if(!validationResult.equals("验证成功")){
			util.sendRedirect(response,request.getContextPath()+"/jsp/RegisterPage.jsp","error",validationResult);
		}else{
userInfoIsInviald(user);
		}
return "0";
	}
private User getUser(){
String username=this.request.getParameter("username");
String password=this.request.getParameter("password");
String phone=this.request.getParameter("phone");
String email=this.request.getParameter("email");
User user=new User(username,password,phone,email);
return user;
	}
private void userInfoIsInviald(User user)throws SQLException,IOException{
Util util=new Util();
UserRepository mysql=new UserRepository();
HikariDataSource dataSource=mysql.dataSource;
try{
String info=mysql.insert(user);
if(info.equals("1")){
				util.sendRedirect(response,request.getContextPath()+"/jsp/LoginPage.jsp");
			}else{
				util.sendRedirect(response,request.getContextPath()+"/jsp/RegisterPage.jsp","error",info);
			}
		}catch(Exception e){
			logger.error("userInfoIsInviald()数据库发生错误:"+e.getMessage());
		}finally{
			util.closePool(dataSource);
		}
	}
}

确定重定向URL不会引导用户到预期外的页面。

5.资源管理

数据库连接的管理:当前使用Util.closePool(dataSource)的实现方式不够清晰,应该清楚地管理数据库连接的生命周期。考虑使用try-with-resources 来自动关闭资源。

注解:在下一版本中考虑在这段代码中使用try-with-resources语句。

6.魔法字符串

避免硬编码字符串:"验证成功"和其他字符串直接使用在代码中,建议使用常量或枚举来代替,以防止由于错别字带来的逻辑错误。

注解:在下一版本中优化魔法字符串。

7.代码示例

以下是改进建议的示范。

代码语言:javascript
复制
public String performRegister()throws SQLException,IOException{
String username=this.request.getParameter("username");
String password=this.request.getParameter("password");
String phone=this.request.getParameter("phone");
String email=this.request.getParameter("email");
//Validate user input
String validationMessage=validateInput(username,password,phone,email);
if(validationMessage!=null){
return validationMessage;//Return validation message
	}
User user=new User(username,password,phone,email);
ValidateUser validateUser=new ValidateUser();
String validationResult=validateUser.validate(user);
if(!validationResult.equals("验证成功")){
return validationResult;
	}
UserRepository mysql=new UserRepository();
try(HikariDataSource dataSource=mysql.getDataSource()){//Use try-with-resources
String info=mysql.insert(user);
if("1".equals(info)){
this.response.sendRedirect("LoginPage.jsp");
		}else{
String encodedMessage=URLEncoder.encode(info,"UTF-8");
String url="RegisterPage.jsp?error="+encodedMessage;
this.response.sendRedirect(url);
		}
	}catch(SQLException e){
		logger.error("performRegister()数据库发生错误:"+e.getMessage(),e);
//Optionally return a user-friendly message
	}
return "0";//Consider returning meaningful status or message
}
private String validateInput(String username,String password,String phone,String email){
//Add input validation logic here
//Return null if valid,or return a validation message if invalid
}

注解:修改void userInfoIsInviald(User user)方法为。

代码语言:javascript
复制
private void userInfoIsInviald(User user)throws SQLException,IOException{
Util util=new Util();
UserRepository mysql=new UserRepository();
try(HikariDataSource dataSource=mysql.dataSource){
String info=mysql.insert(user);
		…
	}catch(Exception e){
		logger.error("userInfoIsInviald()数据库发生错误:"+e.getMessage());
	}
}

最后删除Util.java中的void closePool(HikariDataSource dataSource)方法。

8.总结

此代码的基本逻辑可行,但在错误处理、输入验证、资源管理等方面有改进空间。通过引入异常处理、注释和清晰的结构,您可以提升代码的可维护性和可读性。

4.4.3找回密码测试代码评审

1.申请找回密码测试代码评审

提示词

请对下面测试代码进行评审。

1)globals.py

代码4-75 globals.py

代码语言:javascript
复制
class Config:
REGISTER_URL="http://127.0.0.1:8080/CharGPTEbusiness/jsp/RegisterPage.jsp"
LOGIN_URL="http://127.0.0.1:8080/CharGPTEbusiness/jsp/LoginPage.jsp"
WELCOME_URL="http://127.0.0.1:8080/CharGPTEbusiness/jsp/WelcomePage.jsp"
VERICODE_URL="http://127.0.0.1:8080/CharGPTEbusiness/jsp/VeriCodePage.jsp"
RECOVER_URL="http://127.0.0.1:8080/CharGPTEbusiness/jsp/RecoverPage.jsp"
VALID_USERNAME="validUser123"
VALID_PASSWORD="Valid123!"
INVALID_USERNAME="validUser321"
INVALID_PASSWORD="Valid321!"
SHORT_USERNAME="usr2"
LONG_USERNAME="user12345678901234567"
SHORT_PLUS1_USERNAME="usr12"
LONG_REDUCE1_USERNAME="user1234567890123456"
OTHER_USERNAME="validUser456"
XSS_USERNAME="<script>alert('XSS')</script>"
SQL_USERNAME="admin'--"
DIFF_PASSWORD="Abc@123"
SHORT_PASSWORD="abc"
LONG_PASSWORD="iLongpassword12@Longpassword12@"
SIMPLE_PASSWORD="12345"
VALID_PHONE="13812345677"
OTHER_PHONE="13812345688"
INVALID_PHONE="12345"
CONTRACT_PHONE="13681732596"
VALID_EMAIL="valid@example.com"
INVALID_EMAIL="invalid@example"
OTHER_EMAIL="valid1@example.com"
CONTRACT_EMAIL="xianggu625@126.com"
CODE="123456"
ZERO_SIZE_STRING=""

2)Test_Recover.py

参看代码4-52Test_Recover.py

3)Test_Recover_GUI.py

参看代码4-57Test_Recover_GUI.py

4)Util.py

参看代码2-36 Util.py

2.找回密码测试代码评审回复

ChatGPT回答

以下是对您提供的代码(globals.py、Test_Recover.py、Test_Recover_GUI.py和Util.py)的详细评审和改进建议。主要关注代码的可读性、可维护性、安全性和性能。

1)globals.py

1.优点

全局变量集中管理,便于维护和修改。

变量命名清晰,易于理解其用途。

2.改进建议

数据安全

Ø敏感信息处理:如果有敏感信息(如数据库连接信息),应避免直接在代码中硬编码。

注解:数据库连接信息未在globals.py中体现,因此不存在敏感信息。

2)Test_Recover.py

1.优点

使用unittest框架,结构清晰。

使用parameterized进行多场景测试,减少重复。

2.改进建议

异常处理

Ø异常信息格式化:在捕获异常时,使用f-string格式化字符串,确保异常信息能正确显示。

Ø请求失败处理:在捕获请求异常时,建议在失败时记录更多信息(如请求的URL和数据)。

逻辑优化

Ø重复代码:在多个地方重复请求和响应处理逻辑,建议封装为一个方法,以减少代码重复。

测试用例描述

Ø测试用例注释:可以使用更简洁的方式描述测试用例,例如使用pytest的@pytest.mark注释。

3.改进示例

代码语言:javascript
复制
class Test TestRecoverAPI(unittest.TestCase):
def setUp(self):
self.session=requests.Session()
		response=self.session.get(Config.VERICODE_URL)
self.csrf=CSRF.get_csrf(response.text)
self.token={"csrftoken":self.csrf}
self.db=ClassDB()
self.db.init_login(username="",password="")
def tearDown(self):
self.db.init_db()
def _post_request(self,url,data):
try:
		response=requests.post(url,data=data,cookies=self.token)
		response.raise_for_status()
return response
except requests.exceptions.RequestException as e:
self.fail(f"请求失败:{e},URL:{url},数据:{data}")
	@parameterized.expand([

                #参数化用例
	])
def test_valid_contact(self,sig,contact,message):
"""验证有效和无效的联系方式"""
		uid=str(self.db.getId(Config.CONTRACT_EMAIL))
		data1={'csrftoken':self.csrf,'contact':contact}
self.token['uid']=uid
		response=self._post_request(Config.VERICODE_URL,data1)
if sig==0:
self.assertEqual(response.status_code,200)
self.assertIn(message,response.text)
else:
self.assertEqual(response.status_code,200)
self.assertIn("找回密码",response.text)
                #处理验证码逻辑
#...

注解:修改Test_Recover.py。

代码4-76 Test_Recover.py

代码语言:javascript
复制
#-*-coding:utf-8-*-
import requests
import unittest
from Util import ClassDB
from Util import CSRF
from Util import HASH
from parameterized import parameterized
from globals import Config
MESSAGES={
"OK":"登录",
"NON_EXISTER_EMAIL_OR_PHONE":"您输入的手机号或Email不存在,请重新输入!",
"INCORRECT_VERIFICATION":"验证码错误,请重新输入!",
"HAVEN_USING_PASSWORD":"这个密码以前设置过,请用一个新密码!",
"PASSWORD_NEED_HASH":"密码需要HASH散列",
}
class TestRecoverAPI(unittest.TestCase):
def setUp(self):
self.session=requests.Session()
		response=self.session.get(Config.VERICODE_URL)
self.csrf=CSRF.get_csrf(response.text)
self.token={"csrftoken":self.csrf}
self.db=ClassDB()
self.db.init_login(username="",password="")
def tearDown(self):
self.db.init_db()
def _post_request(self,url,data):
try:
		response=requests.post(url,data=data,cookies=self.token)
		response.raise_for_status()
return response
except requests.exceptions.RequestException as e:
self.fail(f"请求失败:{e},URL:{url},数据:{data}")
	@parameterized.expand([
	(1,Config.CONTRACT_PHONE,MESSAGES["OK"]),
	(1,Config.CONTRACT_EMAIL,MESSAGES["OK"],),
	(0,Config.OTHER_EMAIL,MESSAGES["NON_EXISTER_EMAIL_OR_PHONE"]),
	(0,Config.OTHER_PHONE,MESSAGES["NON_EXISTER_EMAIL_OR_PHONE"]),
	(1,Config.CONTRACT_EMAIL,MESSAGES["INCORRECT_VERIFICATION"]),
	(1,Config.CONTRACT_EMAIL,MESSAGES["HAVEN_USING_PASSWORD"]),
	])
def test_valid_contact(self,sig,contact,message):
"""TC-001-输入有效的注册手机号进行找回密码"""
"""TC-002-输入有效的注册邮箱进行找回密码"""
"""TC-003-输入未注册的邮箱"""
"""TC-004-输入未注册的手机"""
"""TC-005-验证码输入错误"""
"""TC-009-新密码已使用过"""
		uid=str(self.db.getId(Config.CONTRACT_EMAIL))
		data1={'csrftoken':self.csrf,'contact':contact}
self.token['uid']=uid
		response=self._post_request(Config.VERICODE_URL,data1)
if sig==0:
self.assertEqual(response.status_code,200)
self.assertIn(message,response.text)
else:
self.assertEqual(response.status_code,200)
self.assertIn("找回密码",response.text)
if message=="验证码错误,请重新输入!":
			vricode=Config.INVALID_CODE
else:
			vricode=self.db.geveriCode(uid)
if message=="这个密码以前设置过,请用一个新密码!":
			newpassword=Config.VALID_PASSWORD
else:
			newpassword=Config.DIFF_PASSWORD
		data2={
'csrftoken':self.csrf,
'identifyingCode':vricode,
'newPassword':HASH.hash_password(newpassword),
'confirmPassword':HASH.hash_password(newpassword)
		}

                #进入输入新密码页面
self.csrf=CSRF.get_csrf(response.text)
		response=self._post_request(Config.RECOVER_URL,data2)
self.assertEqual(response.status_code,200)
self.assertIn(message,response.text)
	@parameterized.expand([
	(Config.VALID_PASSWORD),
	(Config.ZERO_SIZE_STRING),
	])
def test_recover_password_by_password_format_no_hash256(self,newpassword):
"""TC-010-密码正确使用SHA256散列传输"""
"""TC-011-空字段提交"""

                #进入输入手机或Email页面
		uid=str(self.db.getId(Config.CONTRACT_EMAIL))
		data1={
'csrftoken':self.csrf,
'contact':Config.CONTRACT_EMAIL
	}

                #获取Uid
self.token['uid']=uid
		response=self._post_request(Config.VERICODE_URL,data1)
self.assertEqual(response.status_code,200)
self.assertIn("找回密码",response.text)

                #获取验证码
		vricode=self.db.geveriCode(uid)
		data2={
'csrftoken':self.csrf,
'identifyingCode':vricode,
'newPassword':newpassword,
'confirmPassword':newpassword
	}

                #进入输入新密码页面
self.csrf=CSRF.get_csrf(response.text)
		response=self._post_request(Config.RECOVER_URL,data2)
self.assertEqual(response.status_code,200)
self.assertIn(MESSAGES["PASSWORD_NEED_HASH"],response.text)
if __name__=="__main__":
	unittest.main()

3)Test_Recover_GUI.py

1.优点

使用pytest框架,支持参数化和fixture,结构更灵活。

GUI测试使用playwright,能够模拟用户交互。

2.改进建议

结构优化

Ø使用pytest的fixture:可以在setup_class()中进行数据库初始化,确保每个测试用例前后的状态一致。

错误处理

Ø页面加载失败处理:在页面加载失败时,建议使用pytest.fail()记录详细信息。

测试用例描述

Ø参数化注释:可以在参数化注释中加入用例编号,便于追踪。

3.改进示例

代码语言:javascript
复制
class TestRecover:
	@pytest.fixture(autouse=True)
def setup_and_teardown(self):
self.db=ClassDB()
self.db.init_login(username="",password="")
yield
self.db.init_db()
def _fill_new_password_form(self,page:Page,code:str,password:str,repassword:str):

                #填充新密码表单
		page.get_by_placeholder("输入验证码").fill(code)
		page.get_by_placeholder("输入新密码").fill(password)
		page.get_by_placeholder("确认新密码").fill(repassword)
		page.get_by_role("button",name="确定").click()
	@pytest.mark.parametrize("code,password,repassword,expected_message",[

                #参数化用例
	])
def test_reset_password(self,page:Page,code,password,repassword,expected_message):
try:
		page.goto(Config.RECOVER_URL)
except TimeoutError:
		pytest.fail(f"加载页面失败:{Config.RECOVER_URL}")
		page.reload()
self._fill_new_password_form(page,code,password,repassword)
		error_message=page.inner_text("#confirmPasswordError" if expected_message=="密码确认不一致!" else "#newPasswordError")
assert expected_message in error_message

修改Test_Recover_GUI.py。

代码4-77 Test_Recover_GUI.py

代码语言:javascript
复制
import pytest
from playwright.sync_APIimport Page
from Util import ClassDB
import time
from globals import Config
class TestRecover:
	@pytest.fixture(autouse=True)
def setup_and_teardown(self):
self.db=ClassDB()
self.db.init_login(username="",password="")
yield
self.db.init_db()
def _fill_new_password_form(self,page:Page,code:str,password:str,repassword:str):

                #填充新密码表单
		page.get_by_placeholder("输入验证码").fill(code)
		page.get_by_placeholder("输入新密码").fill(password)
		page.get_by_placeholder("确认新密码").fill(repassword)
		page.get_by_role("button",name="确定").click()
def _fill_phone_mail_form(self,page:Page,contact:str):
		page.get_by_placeholder("输入手机号码或邮箱").fill(contact)
		page.get_by_role("button",name="发送验证码").click()
	@pytest.mark.parametrize("code,password,repassword,expected_message",[
	(Config.CODE,Config.VALID_PASSWORD,Config.DIFF_PASSWORD,"密码确认不一致!"),#TC-006:新密码和确认密码不一致
	(Config.CODE,Config.SIMPLE_PASSWORD,Config.SIMPLE_PASSWORD,"密码必须包含大小写字母、数字和特殊字符,长度在5-30之间"),#TC-007:新密码不符合复杂度要求
	(Config.CODE,Config.SHORT_PASSWORD,Config.SHORT_PASSWORD,"密码必须包含大小写字母、数字和特殊字符,长度在5-30之间"),#TC-008:新密码长度不符合要求
	])
def test_reset_password(self,page:Page,code,password,repassword,expected_message):
try:
		page.goto(Config.RECOVER_URL)
except TimeoutError:
		pytest.fail(f"加载页面失败:{Config.RECOVER_URL}")
		page.reload()
self._fill_new_password_form(page,code,password,repassword)
		error_message=page.inner_text("#confirmPasswordError" if expected_message=="密码确认不一致!" else "#newPasswordError")
assert expected_message in error_message
	@pytest.mark.parametrize("contact,expected_message",[
		(Config.INVALID_PHONE,"请输入有效的中国手机号或Email"),#TC-012:用户输入的邮箱格式不正确
		(Config.INVALID_EMAIL,"请输入有效的中国手机号或Email"),#TC-013:用户输入的邮箱格式不正确
		])
def test_invalid_phone_format(self,page:Page,contact,expected_message):
try:
		page.goto(Config.VERICODE_URL)
except TimeoutError:
		pytest.fail("加载页面失败:{"+Config.VERICODE_URL+"}")
		page.reload()
self._fill_phone_mail_form(page,contact)

                #检查错误提示
		error_message=page.inner_text("#contactError")
assert expected_message in error_message
if __name__=='__main__':
	pytest.main(["-sv","Test_Recover_GUI.py"])

4)Util.py

1.优点

ClassDB和CSRF的实现结构清晰,功能分明。

使用pymysql进行数据库操作,支持多种功能。

2.改进建议

数据库操作

ØSQL注入风险:在SQL语句中直接拼接用户输入,存在SQL注入风险。建议使用参数化查询。

Ø连接管理:在initDB()方法中,建议使用上下文管理器(with)来自动管理连接的打开和关闭。

代码重复

Ø重复代码:init_db()和init_login()中有重复的数据库连接和关闭逻辑,建议提取为一个通用方法。

3.改进示例

代码语言:javascript
复制
class ClassDB:
def initDB(self):
		host='localhost'
		user='root'
		password='123456'
		database='ebusiness'
return pymysql.connect(host=host,user=user,password=password,database=database,charset='utf8')
def execute_query(self,query,params=None):
		connection=self.initDB()
try:
with connection.cursor()as cursor:
			cursor.execute(query,params)
			connection.commit()
return cursor.fetchall()
finally:
		connection.close()
def init_db(self):
self.execute_query("DELETE FROM code")
self.execute_query("DELETE FROM password")
self.execute_query("DELETE FROM user")
def init_login(self,username,password):
		username=username or "validUser"
		password=HASH.hash_password(password or "ValidPassword123!")
self.execute_query("INSERT INTO user(username,password,phone,email)VALUES(%s,%s,%s,%s)",
					(username,password,"13681732596","xianggu625@126.com"))

修改Util.py前半部分为。

代码4-78 Util.py

代码语言:javascript
复制
#-*-coding:utf-8-*-
import pymysql
import re
import hashlib
from globals import Config
class ClassDB:
def initDB(self):
		host='localhost'
		user='root'
		password='123456'
		database='ebusiness'
return pymysql.connect(host=host,user=user,password=password,database=database,charset='utf8')
def execute_query(self,query,params=None):
		connection=self.initDB()
try:
with connection.cursor()as cursor:
			cursor.execute(query,params)
			connection.commit()
return cursor.fetchall()
finally:
		connection.close()
def init_db(self):
self.execute_query("DELETE FROM code")
self.execute_query("DELETE FROM password")
self.execute_query("DELETE FROM user")
def init_login(self,username,password):
		username=username or Config.VALID_USERNAME
		password=HASH.hash_password(password or Config.VALID_PASSWORD)
self.execute_query("INSERT INTO user(username,password,phone,email)VALUES(%s,%s,%s,%s)",
					(username,password,"13681732596","xianggu625@126.com"))

        #关闭数据库连接
def closedb(self,connection):
try:
		connection.close()
except MySQLError as e:
print(f"数据库关闭失败:{e}")

4.总结

通过以上评审和建议,您可以提升代码的可读性、可维护性和安全性。关键点包括。

采用更安全的数据库操作方式,避免SQL注入。

使用类和方法封装全局变量,提升代码组织性。

通过异常处理和日志记录提高代码的健壮性。

如果您有任何进一步的问题或需要更具体的帮助,请随时告诉我。

顾翔凡言:人工智能未来的发展瓶颈在于对知识的更新。唯一不变的是变化,知识发生了变化,人工智能软件能否及时跟进变化,可能阻碍人工智能的使用。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-12-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档