引言
在数字世界中,信息安全一直是大家所关注的焦点,而哈希算法在其中扮演着十分重要的角色。sha256 (Secure Hash Algorithm 256) 是 SHA-2 家族中的一员,因其高安全性和性能,在数据完整性校验、数据签名和密码存储等领域得到了广泛的应用。本文将详细介绍sha256算法特点、使用场景以及在集成sha256算法,不可逆转换数据入库的实践应用
特点
- 不可逆性:无法通过哈希值推导出原始数据
- 唯一性:对于不同的输入数据,生成的哈希值几乎不同
- 固定长度输出:无论输入数据的长度是多少,输出的哈希值长度始终为256位
使用场景
- 密码存储:利用其不可逆特点提高密码存储在数据库中的安全性
- 文件完整性校验:计算文件的哈希值,验证文件在传输或存储的过程中是否被篡改
- 数据签名:确保数据的真实性和完整性
实践应用
了解了sha256算法的特点和使用场景,你就会明白我上篇文章Java中的全局异常捕获与处理为什么会在用户密码入库选择它,Java提供了很多方式来计算sha256哈希值,其中最常用的是通过 java.security.MessageDigest,不需要引入额外的库,因为它是Java标准库的一部分
封装sha256工具类
在工具包中创建sha256工具类
编写sha256工具类
/**
* sha256算法工具类
* @author muze
*/
@Slf4j
public class Sha256Util {
/**
* 计算sha256哈希值
* @param str 需要计算sha256哈希值的字符串
* @return 计算出的sha256哈希值
*/
public static String calculateSha256(String str) {
try {
// 获取一个实现 SHA-256 算法的 MessageDigest 对象,用于计算sha256哈希值
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
// 将字符串转换为字节数组
byte[] strBytes = str.getBytes();
// 计算字节数组的 sha256 哈希值并以字节数组返回
byte[] sha256Bytes = messageDigest.digest(strBytes);
// 创建 StringBuilder 对象,用于构建最终的十六进制字符串
StringBuilder sha256 = new StringBuilder();
// 遍历 sha256 哈希值字节数组
for (byte sha256Byte : sha256Bytes) {
// 将 sha256 字节转换为十六进制字符串
String sha256HexString = Integer.toHexString(0xff & sha256Byte);
// 如果转换后的十六进制字符串是1位则在它前面添加一个0,确保每个字节都是两位十六进制数
if (sha256HexString.length() == 1) sha256.append('0');
sha256.append(sha256HexString);
}
return sha256.toString();
} catch (NoSuchAlgorithmException e) {
// 打印日志,快速定位问题
log.error("计算sha256哈希值时异常:", e);
// 抛出自定义异常
throw new CustomException("系统异常");
}
}
}
实现用户注册功能
用户注册请求实体
/**
* 用户注册请求实体
*/
@Data
public class UserRegisterDTO {
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
}
用户业务接口层
新增注册接口
/**
* 用户业务接口层
* @author muze
*/
public interface IUserService {
/**
* 登录
* @param userLoginDTO 用户登录请求实体
* @return 登录结果
*/
String login(UserLoginDTO userLoginDTO);
//************************ 新增注册功能 ************************//
/**
* 注册
* @param userRegisterDTO 用户注册请求实体
* @return 注册结果
*/
String register(UserRegisterDTO userRegisterDTO);
}
用户业务实现层
新增注册接口实现,修改登录接口实现
/**
* 用户业务实现层
* @author muze
*/
//************************ 新增日志打印 ************************//
@Slf4j
@Service
public class UserServiceImpl implements IUserService {
/**
* 注入用户数据层
*/
@Autowired
private UserMapper userMapper;
@Override
public String login(UserLoginDTO userLoginDTO) {
// 取出用户名和密码
String username = userLoginDTO.getUsername();
String password = userLoginDTO.getPassword();
// 构建查询条件
LambdaQueryWrapper<User> userLambdaQueryWrapper = new LambdaQueryWrapper<User>().eq(User::getUsername, username);
// 查询用户
User user = userMapper.selectOne(userLambdaQueryWrapper);
//************************ 新增密码解密 ************************//
String decryptPassword;
try {
decryptPassword = RSAUtil.decryptByPrivateKey(password);
} catch (Exception e) {
//************************ 打印异常信息 ************************//
log.error("使用私钥解密密码时异常:", e);
//************************ 抛出自定义异常 ************************//
throw new CustomException("用户名或密码错误");
}
String sha256Password = Sha256Util.calculateSha256(decryptPassword);
// 如果用户为空或者解密后的输入密码与用户密码不匹配则返回:用户名或密码错误
if (user == null || !sha256Password.equals(user.getPassword())) return "用户名或密码错误";
// 使用SaToken的工具类StpUtil调用登录方法login,入参:用户id
StpUtil.login(user.getId());
// 返回:登录成功
return "登录成功";
}
/**
* 注册
* @param userRegisterDTO 用户注册请求实体
* @return 注册结果
*/
@Override
public String register(UserRegisterDTO userRegisterDTO) {
// 提取用户名和密码
String username = userRegisterDTO.getUsername();
String password = userRegisterDTO.getPassword();
// 构建查询条件
LambdaQueryWrapper<User> userLambdaQueryWrapper = new LambdaQueryWrapper<User>().eq(User::getUsername, username);
// 查询用户
User dbUser = userMapper.selectOne(userLambdaQueryWrapper);
// 如果用户不为空,则抛出自定义异常"该用户已注册,请直接登录"
if (dbUser != null) throw new CustomException("该用户已注册,请直接登录");
// 计算密码sha256哈希值
String sha256Password = Sha256Util.calculateSha256(password);
// 构建并入库用户信息
User user = new User();
user.setUsername(username);
user.setPassword(sha256Password);
userMapper.insert(user);
// 返回:注册成功
return "注册成功";
}
}
用户控制层
新增注册请求入口
/**
* 用户控制层
* @author muze
*/
@RestController
@RequestMapping("/user")
public class User {
/**
* 注入用户业务接口层
*/
@Autowired
private IUserService userService;
/**
* 登录
* @param userLoginDTO 用户登录请求实体
* @return 响应码 + 响应消息 + 响应数据
*/
@PostMapping("/login")
public SaResult login(@RequestBody UserLoginDTO userLoginDTO) {
return SaResult.ok(userService.login(userLoginDTO));
}
//************************ 新增注册功能 ************************//
/**
* 注册
* @param userRegisterDTO 用户注册请求实体
* @return 注册结果
*/
@PostMapping("/register")
public SaResult register(@RequestBody UserRegisterDTO userRegisterDTO) {
return SaResult.ok(userService.register(userRegisterDTO));
}
}
拦截器
config包中WebConfig,将注册路径添加到拦截器不需要校验的路径中
/**
* Web配置
* @author muze
*/
@Configuration
public class WebConfig implements WebMvcConfigurer {
/**
* 添加资源共享映射
* @param registry 资源共享注册表
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // 允许跨域访问的路径
.allowedOrigins("*") // 允许跨域请求的 请求协议、ip和端口
.allowedMethods("GET", "POST", "PUT", "DELETE") // 允许的请求方法
.allowedHeaders("*") // 允许的请求头信息
.allowCredentials(false); // 允许携带cookie
}
/**
* 添加拦截器
* @param registry 拦截器注册表
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SaInterceptor(handler -> StpUtil.checkLogin())) // 添加SaToken拦截器,校验规则为检查登录
.addPathPatterns("/**") // 添加校验路径
.excludePathPatterns("/user/login", "/user/register"); // 不需要校验的路径
}
}
测试
启动项目,使用接口调试工具测试注册功能
接口调试工具
数据库
调用登录接口测试
到这里我们就一起完成了在Java中使用sha256算法对数据进行不可逆转换入库,相信你已经掌握了,赶快去试试吧,希望这篇文章能够对你有所帮助!
彩蛋:相信细心的你也发现了,日常开发中我们不仅仅是进行sha256哈希值转换入库,为了提高安全性,我们还需要用到"盐值",没错就是屏幕前你那英俊美丽的"颜值",敬请期待!