Juconcurrent 学而不思则罔,思而不学则殆。

如何使用ABtoken解决跨网安全性问题


前言

我们假设有一个混合云的场景,在这个场景中,存在两个环境 - 公网和专网。公网和专网都可以提供服务。公网可以访问专网获取非敏感数据,公网也可以访问专网获取敏感数据脱敏之后的呈现。由于考虑到安全因素,专网不可以访问公网。

在这种场景下,如何解决专网访问的安全性呢?

我们抽取出其中最关键的因素,也就是安全性。很明显,专网的安全性要求要高于公网。公网的访问,我们可以通过借用于JWT来做token校验的方式,来满足我们的安全性。同时,公网在访问的同时可延长token的存活时间,这样我们就可以让token一直可用。专网的访问,如果使用这种方式的话,那么可能出现token被劫持,从而导致敏感信息被泄露的风险。

如果我们给token加一个有效期,在有效期内,可以通过token正确地访问专网。使用过期的token访问专网,我们还可通过过滤器的方式对此进行拦截,并异常返回。这样的确能解决拿到了token就可以一直访问的风险。但是在token过期的临界时间点上,有可能出现正常的请求也被异常拦截的情况。

基于此,我们想到使用双token的方案。也就是说,可使用ABtoken的方案来解决。

名词解释

在陈述之前,我们有必要说明一下用到的几个关键名词。

名词 解释
ABtoken策略 该策略可有效防止客户端在token过期临界点,
并发访问token导致业务不可用的问题。
过期时间 token的过期时间,默认为30分钟
过期时间点 token将在此时间点过期
交替时间 指的是Atoken和Btoken并行存活的最大时间,5分钟

获取token时序图

获取token时序图

生成token流程图

生成token流程图

校验Token流程图

校验token流程图

签名算法

基于安全性考虑,专网token获取的时候,需要增加签名的校验,从而避免传入的参数被篡改。生成签名的算法,通常是通过服务商提供的appkey和secret,再加上时间戳和数据内容来动态生成md5码。为了增加破解难度,我们也可以内置一个固定值salt。

加签算法大概是下面这个样子:

public class SignGenerator {

    private String appKey;
    private String secret;
    private static final String SALT = "hrbiN34XTE30KUfp";

    public SignGenerator (String appKey, String secret) {
        this.appKey = appKey;
        this.secret = secret;
    }

    public String getSign(Object obj) {
        SortedMap<String, Object> map = new TreeMap<>();

        // 将所有字段值不为null的字段都纳入签名
        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            try {
                if (field.getAnnotationsByType(IgnoreSign.class).length < 1) {
                    Object value = field.get(obj);
                    if (value != null) {
                        map.put(field.getName(), value);
                    }
                }
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Get sign field value error", e);
            }
        }

        if (map.isEmpty()) {
            throw new RuntimeException("Sign fields can not be empty!");
        }

        map.put("salt", SALT);
        map.put("appKey", appKey);
        map.put("secret", secret);

        StringBuilder builder = new StringBuilder();
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            builder.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
        }
        builder.deleteCharAt(builder.length() - 1);

        return Md5Util.encode(builder.toString(), "utf-8");
    }
}

总结

由于公司隐私问题,关于ABtoken最核心的实现代码,楼主并不会开放出来。如果读者对此感兴趣,可以自行实现一遍,同时写一些测试用例来进一步测试。


下一篇 深入使用Xshell

Content