Java中使用sha256算法对数据进行不可逆转换入库

引言

        在数字世界中,信息安全一直是大家所关注的焦点,而哈希算法在其中扮演着十分重要的角色。sha256 (Secure Hash Algorithm 256) 是 SHA-2 家族中的一员,因其高安全性和性能,在数据完整性校验、数据签名和密码存储等领域得到了广泛的应用。本文将详细介绍sha256算法特点、使用场景以及在集成sha256算法,不可逆转换数据入库的实践应用

特点

  1. 不可逆性:无法通过哈希值推导出原始数据
  2. 唯一性:对于不同的输入数据,生成的哈希值几乎不同
  3. 固定长度输出:无论输入数据的长度是多少,输出的哈希值长度始终为256位

使用场景

  1. 密码存储:利用其不可逆特点提高密码存储在数据库中的安全性
  2. 文件完整性校验:计算文件的哈希值,验证文件在传输或存储的过程中是否被篡改
  3. 数据签名:确保数据的真实性和完整性

实践应用

        了解了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哈希值转换入库,为了提高安全性,我们还需要用到"盐值",没错就是屏幕前你那英俊美丽的"颜值",敬请期待!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值