项目点滴:简单搞搞系统授权

本文介绍了应用系统如何通过客户端标识符和动态生成的授权码来确保系统的安全性,包括客户端标识的唯一性、授权码的生成与校验过程,以及如何利用SM2,SM3,SM4国密算法进行加密。同时提到在不同环境下的部署问题和优化建议。
摘要由CSDN通过智能技术生成

我们开发的应用系统,卖给企事业使用,为了保证不被二次售卖使用,以及后期的持续收入,我们需要加入系统授权。(简单描述,其实好处挺多的)

这就要求每一份独立部署的材料都至少具备:

①客户端标识符:唯一标识部署机器,自动生成,不能被更改,传播无效。用于申请系统授权码。

②授权码使用期限:系统需要授权码才能使用,可一定程度保障系统安全。

以上至少具备的两点是基于一下思路:

用户使用 “客户端标识符”向“我们”申请“系统授权码文件”,并在系统中导入“授权码文件”,然后正常使用。当在使用的“授权码文件”即将过期或过期后,需要申请新的授权码文件并导入,继续正常使用。

当然,可以提前申请新授权码文件并导入,避免正在使用的系统出现业务暂停。(这都是用户体验方面,不展开)

综上,涉及两个系统,每个系统大致功能如下

应用系统:授权码文件上传、授权码文件解析、授权码文件存储、授权码信息查询、授权码校验

授权管理系统:授权码申请

考虑点:授权码生成算法最好是动态的,涉及的密钥最好也是动态的。当然,这是我的个人想法,目前我们做的不是。。。

=========================================================================

依赖:主要功能依赖是这个,涉及的其它工具依赖如hutool,guava等不多说了。

<!-- 获取系统信息 -->
<dependency>
    <groupId>com.github.oshi</groupId>
    <artifactId>oshi-core</artifactId>
    <version>6.2.2</version>
</dependency>

做法:

客户端标识符:保证唯一,使用客户端机器序列号和硬件UUID

public static String getClientUuid() {
    String serialNumber = OshiUtil.getSystem().getSerialNumber();
    String hardwareUUID = OshiUtil.getSystem().getHardwareUUID();
    return SecureUtil.sha1(serialNumber + hardwareUUID);
}

授权码生成:算法分三步,涉及对称加密和非对称加密,均为国密算法:sm2, sm3, sm4

 

public static String genLicense(String clientUuid) {
    Date current = new Date();

    AuthLicense authLicense = new AuthLicense();
    authLicense.setSubject("授权许可");
    authLicense.setIssuer("夏日温华");
    authLicense.setIssuedAt(current.getTime());
    authLicense.setNotBefore(current.getTime());
    authLicense.setNotAfter(DateUtil.endOfDay(DateUtil.offsetMonth(current, TERM_MONTH)).getTime());

    // 1. SM4加密clientUuid并得到base64编码字符串
    String token1 = SmUtil.sm4(SecureUtil.md5().digest("这里是密钥,静态的,提前约定好的,最好做成动态的")).encryptBase64(clientUuid);

    // 2. SM4加密authLicenseInfo并得到base64编码字符串,密钥为"clientUuid"
    String licenseBaseStr = Base64.encode(JSON.toJSONString(authLicense));
    String token2 = SmUtil.sm4(SecureUtil.md5().digest(clientUuid)).encryptBase64(licenseBaseStr);

    // 3. 数字签名
    String token3 = Base64.encode(Sm2Util.encrypt(SmUtil.sm3(token1 + "." + token2), "公钥", "私钥"));

    // 授权许可证
    return StrUtil.join(".", token1, token2, token3);
}

授权码校验:就是生成算法的逆向解析,做成动态的也好,思想一样

public static AuthLicense checkLicense() {
    String license = readLicense(); // 读取授权码,自己实现
    return checkLicense(license);
}

public static AuthLicense checkLicense(String license) {
    String clientUuid = getClientUuid();
    if (StrUtil.isBlank(license)) {
        throw new ServiceException("不正确的授权许可文件");
    }
    String[] tokens = license.split("\\.");
    if (tokens.length != 3) {
        throw new ServiceException("不正确的授权许可文件");
    }
    // 1. 校验clientUuid
    String token1 = StrUtil.str(SmUtil.sm4(SecureUtil.md5().digest("这里是密钥,静态的,提前约定好的,最好做成动态的")).decrypt(tokens[0]), StandardCharsets.UTF_8);
    Assert.isTrue(clientUuid.equals(token1), "不正确的授权许可文件");
    // 2. 验签
    String sign = Sm2Util.decrypt(Base64.decodeStr(tokens[2]), "公钥", "私钥");
    String reSign = SmUtil.sm3(tokens[0] + "." + tokens[1]);
    Assert.isTrue(ObjectUtil.equals(sign, reSign), "不正确的授权许可文件");
    // 3. 验证授权信息
    String token2 = StrUtil.str(SmUtil.sm4(SecureUtil.md5().digest(clientUuid)).decrypt(tokens[1]), StandardCharsets.UTF_8);
    AuthLicense authLicense = JSON.parseObject(Base64.decodeStr(token2), AuthLicense.class);
    checkAuthLicense(authLicense);
    authLicense.setClientUuid(clientUuid);
    return authLicense;
}

private static void checkAuthLicense(AuthLicense authLicense) {
    long current = DateUtil.current();
    if (authLicense.getNotAfter() == null || !ISSUER.equals(authLicense.getIssuer())) {
        throw new ServiceException("无效的授权许可证");
    }
    if (current > authLicense.getNotAfter()) {
        throw new ServiceException("您的授权许可证到期,请重新申请并上传新的授权许可证文件");
    }
}

 系统授权码上传、信息查询等API因系统而异,不加了。

哈哈哈哈哈,简单吧,想说啥?忍住.

目前我只是把系统的一些做个简单介绍,不涉及个人优化操作,所以当前做法可能会有一些问题,如果您在参考,可以自己优化下,

问题:用docker容器部署时,如果基础镜像一样,在不同的机器上部署,也可能会导致有重复的客户端标识。

  • 20
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值