简介
在数字时代,信息安全的重要性不言而喻,尤其当涉及到个人隐私和账户安全时。每天,无数的用户登录各种在线服务,从社交媒体到银行账户,再到电子邮件和云存储服务。这些服务的背后,是复杂的系统架构,其中包含着用户最为敏感的数据——密码。
过去,简单的加密方法和弱密码策略导致了许多严重的数据泄露事件。例如,2013年雅虎(yahoo)遭遇的大规模数据泄露事件,影响了数十亿的用户账户,部分原因就是由于使用了不够安全的密码存储技术。再如2016年的linkedin数据泄露事件,尽管该公司使用了sha-1散列算法对密码进行了处理,但未加盐的密码散列最终还是被破解,暴露了用户的隐私。
这些事件引发了行业对于密码安全的深刻反思,促使开发者和安全专家寻找更安全的解决方案。bcrypt作为一种适应性强且经过时间考验的密码哈希算法,成为了现代密码安全的基石。它不仅能够有效抵御暴力破解和彩虹表攻击,还能通过增加工作因子来适应未来计算能力的增长。
在本文中,我们将深入探讨如何在spring项目中利用maven和bcrypt来实现一个安全的密码修改功能。这不仅仅是关于代码实现的问题,更是一次对密码安全重要性的重申,以及对如何在实际应用中践行这一原则的示范。我们将从环境搭建开始,逐步构建出一个既实用又安全的密码修改流程,确保即使在最恶劣的情况下,用户的密码也能得到妥善保护。
通过本文的学习,你将获得宝贵的实践经验,了解如何在自己的项目中实施类似的解决方案,从而提升应用的安全性,给用户提供更加安心的在线体验。在接下来的内容中,我们将一步步解析实现过程,从添加依赖到编写核心业务逻辑,直至完成完整的功能测试,确保每一步都遵循最佳的安全实践。
controller(usercontroller)
/** * 修改密码 */ @putmapping("/update/pwd/{id}") public result update(@pathvariable("id") long id, @requestbody changepasswordvo changepasswordvo) { try{ userservice.changepassword(changepasswordvo,id); return result.success("修改成功!"); }catch (exception e){ // 捕获异常,获取异常信息 string message = e.getmessage(); // 如果修改失败,返回失败的结果,并附带异常信息 return result.failed(message); } }
1.注解 @putmapping("/update/pwd/{id}")
@putmapping
是spring mvc中的一个注解,用于处理http put请求。"/update/pwd/{id}"
是该方法的url路径。其中{id}
是一个路径变量,用于接收用户id。
2.方法定义 public result update(...)
- 这是一个公开的方法,名为
update
,返回一个result
对象。 result
很可能是一个自定义的响应类,通常用于封装api的响应结果,包括状态码、消息和数据等。
3.方法参数
@pathvariable("id") long id
:这是从url路径中提取的id
变量。@pathvariable
注解告诉spring mvc从url中提取名为id
的变量值,并将其转换为long
类型。@requestbody changepasswordvo changepasswordvo
:@requestbody
注解用于将http请求体中的json数据转换为changepasswordvo
类型的对象。changepasswordvo
可能是一个包含旧密码和新密码等信息的dto(数据传输对象)。
4.方法体
- 首先,它尝试调用
userservice.changepassword(changepasswordvo,id);
。这里假设userservice
是一个已经注入的服务类,用于处理与用户相关的业务逻辑。changepassword
方法可能会根据提供的用户id和密码信息来更新用户的密码。 - 如果上述操作成功,方法将返回一个表示成功的
result
对象,并附带消息“修改成功!”。
如果在更新密码的过程中发生异常(如数据库错误、密码验证失败等),catch
块将捕获该异常,并获取其消息。然后,它返回一个表示失败的result
对象,并附带异常的消息。
entity(vo-changepasswordvo)
@data public class changepasswordvo implements serializable { /** * 旧的密码 */ private string oldpassword; /** * 新的密码 */ private string newpassword; }
1.注解
@data
: 这是一个lombok库提供的注解。当你添加@data
到类上时,lombok会自动为这个类生成getter、setter、equals、hashcode和tostring方法。这可以大大减少模板代码的数量,使代码更加简洁。implements serializable
: 这表示该类实现了serializable
接口。serializable
是一个标记接口,用于指示一个类的对象可以被序列化。序列化是将对象状态转换为字节流,以便可以将其写入文件或发送到网络上的另一个位置。如果该类或其成员类(如果它们不是基本类型或string
、数组
等)需要被序列化,则必须实现这个接口。
2.成员变量
private string oldpassword;
: 这是一个私有字符串类型的成员变量,用于存储旧的密码。但是,从java的命名约定来看,变量名应该使用驼峰命名法(camelcase),并且首字母小写。因此,更合适的命名可能是oldpassword
。private string newpassword;
: 同样,这是一个私有字符串类型的成员变量,用于存储新的密码。按照java的命名约定,它应该被命名为newpassword
。
3.注释
- 每个成员变量上方都有注释,描述了该变量的用途。
- 这是一个很好的做法,因为它增加了代码的可读性。
- 但是,请注意,这些注释是用中文写的,而在国际项目中,通常建议使用英文注释。
service
userservice
void changepassword(changepasswordvo changepasswordvo, long id);
1.返回类型 (void
): 方法前面有一个void
关键字,表示这个方法没有返回值。也就是说,当你调用这个方法时,它不会返回任何值或对象。
2.方法名 (changepassword
): 这是方法的名称,即changepassword
。当你想在代码的其他部分调用这个方法时,你会使用这个名字。
参数:
changepasswordvo changepasswordvo
: 这是方法的第一个参数。
changepasswordvo
: 这是参数的类型。它可能是一个数据传输对象(dto),用于封装与更改密码相关的数据,如旧密码、新密码等。changepasswordvo
: 这是参数的名称。在方法内部,你可以使用这个名称来引用传入的changepasswordvo
对象。
long id
: 这是方法的第二个参数。
long
: 这是参数的类型,表示这是一个长整型(64位整数)数据。id
: 这是参数的名称。在方法内部,你可以使用这个名称来引用传入的id
值。从参数名可以推测,这个id
可能表示用户的唯一标识符(如用户id)。
userserviceimpl
@override public void changepassword(changepasswordvo changepasswordvo, long id){ // 根据id查询用户信息 user user = usermapper.selectbyid(id); // 判断原密码是否正确 if(!bcrypt.checkpw(changepasswordvo.getoldpassword(),user.getpassword())){ throw new runtimeexception("原密码不正确"); } // 设置新密码 user.setpassword(bcrypt.hashpw(changepasswordvo.getnewpassword(), bcrypt.gensalt())); // 调用mapper的updatebyid方法更新用户信息 usermapper.updatebyid(user); }
1.@override:
- 这是一个java注解,它告诉编译器这个方法是从超类或接口中继承或实现的。
- 使用
@override
注解可以确保你正确地重写了父类或接口中的方法,如果没有正确重写(例如方法签名不匹配),编译器会报错。
2.public void changepassword(changepasswordvo changepasswordvo, long id): 这是方法的声明部分。
public
:表示这是一个公共方法,可以从任何其他类中被访问。void
:表示该方法没有返回值。changepassword
:是方法的名称。changepasswordvo changepasswordvo
和long id
:是方法的参数。changepasswordvo
可能是一个数据传输对象(dto),用于封装密码更改请求所需的信息(如旧密码和新密码)。id
则是用户的唯一标识符。
3.user user = usermapper.selectbyid(id);:
- 使用
usermapper
(可能是mybatis的mapper或类似的orm工具)的selectbyid
方法根据提供的id
从数据库中查询用户信息,并将查询到的用户信息存储在user
变量中。
4.if(!bcrypt.checkpw(changepasswordvo.getoldpassword(),user.getpassword())){:
- 使用
bcrypt
库(一个流行的密码哈希库)的checkpw
方法检查用户提供的旧密码(从changepasswordvo
中获取)是否与数据库中存储的哈希密码(从查询到的user
对象中获取)匹配。 - 如果不匹配(
!bcrypt.checkpw(...)
返回true
),则执行下面的throw
语句。
5.throw new runtimeexception("原密码不正确");:
- 如果旧密码不正确,则抛出一个
runtimeexception
,并带有消息“原密码不正确”。调用此方法的代码应该捕获此异常并适当地处理它(例如,向用户显示错误消息)。
6.user.setpassword(bcrypt.hashpw(changepasswordvo.getnewpassword(), bcrypt.gensalt()));:
- 如果旧密码正确,则使用
bcrypt
库的gensalt
方法生成一个新的随机盐值。 - 使用这个新盐值和用户提供的新密码(从
changepasswordvo
中获取)作为参数,调用bcrypt
的hashpw
方法生成新的哈希密码。 - 将这个新的哈希密码设置到
user
对象的password
字段中。
7.usermapper.updatebyid(user);:
- 使用
usermapper
的updatebyid
方法更新数据库中对应id
的用户的密码信息。 - 这里假设
usermapper
的updatebyid
方法会处理将user
对象中的更改保存到数据库中的逻辑。
测试
先新增一条数据
密码:111
进行修改密码;
id为你新增后的id号
输入字段为我之前定义的字段oldpassword与newpassword
可以看到密码已经被修改
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论