签名验签

很多场景下,我们的服务会需要对外部提供数据接口,如果是使用账户鉴权,我们上一节已经看到了,只需要登录,然后带着令牌进行请求就可以了。更多的情况,我们为了防伪造防抵赖,会使用签名验签的方式,concrete在这方面也做了设计,本小节我们看看怎么做。

体验

定义一个需要签名的服务

package org.coodex.concrete.demo.api.excepted;

import org.coodex.concrete.api.ConcreteService;
import org.coodex.concrete.api.Signable;
import org.coodex.concrete.demo.pojo.ExamplePojo;

@Signable // 声明这个服务需要签名,也可以作用在服务接口上
@ConcreteService
public interface DemoSignatureService {

    void doSomeThing(Integer a, String kkk, ExamplePojo pojo);
}

pojo

package org.coodex.concrete.demo.pojo;

import org.coodex.concrete.api.pojo.StrID;

import java.util.ArrayList;
import java.util.List;

public class ExamplePojo {
    private String x1 = "随便什么吧";

    private List<StrID<String>> example = new ArrayList<StrID<String>>(){{
        for(int i =0;i < 3; i ++){
            StrID<String> stringStrID = new StrID<>();
            stringStrID.setId("id" + i);
            stringStrID.setPojo("我是POJO_" + i);
            add(stringStrID);
        }
    }};

    public String getX1() {
        return x1;
    }

    public void setX1(String x1) {
        this.x1 = x1;
    }

    public List<StrID<String>> getExample() {
        return example;
    }

    public void setExample(List<StrID<String>> example) {
        this.example = example;
    }
}

实现。演示如何获取签名方信息

package org.coodex.concrete.demo.impl;

import org.coodex.concrete.core.signature.SignUtil;
import org.coodex.concrete.demo.api.excepted.DemoSignatureService;
import org.coodex.concrete.demo.pojo.ExamplePojo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Named;

@Named
public class DemoSignatureServiceImpl implements DemoSignatureService {

    private final static Logger log = LoggerFactory.getLogger(DemoSignatureServiceImpl.class);

    @Override
    public void doSomeThing(Integer a, String kkk, ExamplePojo pojo) {
        log.info("client keyId: {}, alg: {}, noise: {}, sign: {}",
                SignUtil.getKeyId(),
                SignUtil.getAlgorithm(),
                SignUtil.getNoise(),
                SignUtil.getSign());
    }
}

调用端

package org.coodex.concrete.demo.client;

import org.coodex.concrete.Client;
import org.coodex.concrete.demo.api.excepted.DemoSignatureService;
import org.coodex.concrete.demo.pojo.ExamplePojo;

public class SignatureDemoClient {

    public static void main(String[] args) {
        DemoSignatureService demoSignatureService = Client.getInstance(DemoSignatureService.class, "jaxrs");
        demoSignatureService.doSomeThing(1, "kkk", new ExamplePojo());
    }
}

开启签名拦截器,因为我们的调用端和服务端在一个工程里,并且java client没有在DI容器里,所以我们用两种方式分别定义拦截器

服务端:

interceptors:
  signature: true # 开启签名验签拦截器

客户端:在test作用域中

META-INF/services/org.coodex.concrete.core.intercept.ConcreteInterceptor

org.coodex.concrete.core.intercept.SignatureInterceptor

调用一下

Exception in thread "main" org.coodex.concrete.common.ConcreteException: 未知错误: no privateKey..

因为这个接口需要签名,没有配置签名相关信息,所以无法成功调用。

我们配置一下签名验签的信息,默认使用Profile

client.jaxrs.yml , 这里简单一说,客户端调用需要签名接口时,为了隔离不同目标,会优先到当前模块找签名所需信息,如果使用Destination的方式,会按照Destination的Identify属性查找

signature:
  keyId: app00001
  algorithm: HmacSHA256
  hmacKey: Davidoff is handsome # 默认的HmacKeyStore方案

服务端signature.yml

hmacKey:
  app00001: Davidoff is handsome # 默认的HmacKeyStore方案
2019-08-08 08:45:02.383  INFO 5776 --- [vice.executor-1] o.c.c.d.impl.DemoSignatureServiceImpl    : client keyId: app00001, alg: HmacSHA256, noise: 1096304856, sign: uJgM+I2ILJm22Zz1EsCIYXZb2B8VHpH9i/eeS+yr8xk=
08:44:59.577 [main] DEBUG org.coodex.concrete.core.intercept.AbstractSignatureInterceptor - signature for[ doSomeThing ]: 
    noise: 1096304856
    algorithm: HmacSHA256
    keyId: app00001
    sign: uJgM+I2ILJm22Zz1EsCIYXZb2B8VHpH9i/eeS+yr8xk=
    body: a=1&algorithm=HmacSHA256&keyId=app00001&kkk=kkk&noise=1096304856&pojo=example%3Did%253Did0%2526pojo%253D%2525E6%252588%252591%2525E6%252598%2525AFPOJO_0%26example%3Did%253Did1%2526pojo%253D%2525E6%252588%252591%2525E6%252598%2525AFPOJO_1%26example%3Did%253Did2%2526pojo%253D%2525E6%252588%252591%2525E6%252598%2525AFPOJO_2%26x1%3D%25E9%259A%258F%25E4%25BE%25BF%25E4%25BB%2580%25E4%25B9%2588%25E5%2590%25A7

你可以试着自己调整传输的数据以及签名设置试试。

扩展

签名验签部分的扩展点不少,IronPen, KeyStore, SignatureSerializer, 返回值签验,签名属性重载,NoiseGenerator,NoiseValidator 等等,在这里先做一个说明,后续在concrete工具链中详细说明

  • IronPen:
    • 用来签名的铁笔,命名源自《国家宝藏1》,线索指向独立宣言那一段,concrete-core提供了Hmac和RSA的签验方式
  • KeyStore:
    • 签名密钥读取,根据算法来
    • HMAC_KeyStore,默认提供的是Configuration获取
      • 服务端,signature命名空间下,优先级 hmacKey.paperName.keyId 高于 hmacKey.paperName 高于 hmacKey.keyId 高于 hmacKey
      • 客户端,client/模块命名空间下,优先级 signature.hmacKey.paperName.keyId 高于 signature.hmacKey.paperName 高于 signature.hmacKey.keyId 高于 signature.hmacKey
    • RSA_KeyStore,优先级
      • 私钥:Config 高于 pem文件
        • Config中 rsa.privateKey.paperName.keyId > rsa.privateKey.paperName > rsa.privateKey.keyId > rsa.privateKey
        • pem文件 paperName.keyId.pem > keyId.pem > paperName.pem
      • 公钥:Config 高于 crt文件
        • Config中 rsa.publicKey.paperName.keyId > rsa.publicKey.paperName > rsa.publicKey.keyId > rsa.publicKey
        • crt文件 paperName.keyId.crt > keyId.crt > paperName.crt
  • SignatureSerializer:需要签名的内容组织与序列化,默认提供的键值对encode、键字典排序使用&链接的方式,空值不参与签名
  • 返回值签名:返回值类型继承org.coodex.concrete.api.pojo.Signature即可
  • 签名属性重载:concrete-core默认的签名相关域包括algorithm、sign、keyId、noise;
    • 服务端可以通过配置signature命名空间下的相关Configuration项(signature.property.*)进行重载,
    • 客户端可以通过配置client/模块命名空间下的相关Configuration项(signature.property.*)进行重载,
  • NoiseGenerator,NoiseValidator,用于生成噪音和噪音验证,自行实现可以有效防止重放攻击,默认实现并没有包含安全相关的设计,但是允许你可以扩展啊
(ɔ) coodex.org 2016 - 2020            修订时间: 2020-03-09 02:56:39

results matching ""

    No results matching ""