Hyperledger Fabric——balance transfer(二)注册用户
详细分析blance transfer示例的用户注册(register)与登录(enroll)功能。
源码分析
1.首先分析项目根目录的app.js文件中关于用户注册和登录的路由函数。注意这里的token很重要,在之后的请求中,只要在请求头中附上token,js的路由函数就能直接获取其中的参数:req.username,req.orgName
// 注册和登录用户
app.post('/users', async function(req, res) {
// 获取并检验参数
var username = req.body.username;
var orgName = req.body.orgName;
if (!username) {
res.json(getErrorMessage('\'username\''));
return;
}
if (!orgName) {
res.json(getErrorMessage('\'orgName\''));
return;
}
// 生成token,包含信息username, orgName
var token = jwt.sign({
exp: Math.floor(Date.now() / 1000) + parseInt(hfc.getConfigSetting('jwt_expiretime')),
username: username,
orgName: orgName
}, app.get('secret'));
// 注册成功则返回响应信息,并附上token
let response = await helper.getRegisteredUser(username, orgName, true);
if (response && typeof response !== 'string') {
response.token = token;
res.json(response);
// 注册失败则返回错误信息
} else {
res.json({success: false, message: response});
}
});
2.继续进入helper.js,找到函数getRegisteredUser()分析其具体实现:内部
需要注意的是用户的登陆(enroll)过程是在SDK中的setUserContext()函数内部实现的(登录admin也是),当传入的不是User对象时,而是一个含有username和password属性的object时,会调用_setUserFromConfig()方法,在内部进行登录操作,返回一个User对象。随后setUserContext()将该用户对象存储到到本地(persistence)。
由于SDK在创建该对象的时候没有设置_enrollmentSecret属性,所以在注册登录的返回信息和本地存储的键值对中都没有sectect值,所以可以对源码进行一些修改(不太优雅),或者可以直接修改SDK代码.
var getRegisteredUser = async function(username, userOrg, isJson) {
try {
// 获得一个客户端实例
var client = await getClientForOrg(userOrg);
// 通过SDK中的getUserContext()方法获取User对象(包含证书和私钥等信息)
// 本质上是通过KeyValueStore接口从本地存储加载User数据
var user = await client.getUserContext(username, true);
// 用户存在且已经登录
if (user && user.isEnrolled()) {
logger.info('Successfully loaded member from persistence');
}
// 用户未登录,需要使用admin来进行注册
else {
// 根据config.json配置文件中的信息,获得的admins结构如下:
// admins = [{username:"admin", secret:"adminpw"}]
var admins = hfc.getConfigSetting('admins');
let adminUserObj = await client.setUserContext({username: admins[0].username, password: admins[0].secret});
// 生成CA服务实现
let caClient = client.getCertificateAuthority();
// 通过CA服务和admin来注册该user,获得密码secret
let secret = await caClient.register({
enrollmentID: username,
affiliation: userOrg.toLowerCase() + '.department1'
}, adminUserObj);
// enroll成功,将用户上下文信息(证书,私钥)以及密钥对持久化地很存入本地存储中
user = await client.setUserContext({username:username, password:secret});
// 改动:添加属性后重新存入本地存储,以保证获取secret值
user._enrollmentSecret = secret;
await client.setUserContext(user);
}
if(user && user.isEnrolled) {
// 返回包含登录信息的json对象
if (isJson && isJson === true) {
var response = {
success: true,
secret: user._enrollmentSecret,
message: username + ' enrolled Successfully',
};
return response;
}
} else {
throw new Error('User was not enrolled ');
}
} catch(error) {
logger.error('Failed to get registered user: %s with error: %s', username, error.toString());
return 'failed '+error.toString();
}
};
3.再来看生成客户端的getClientForOrg()函数:
async function getClientForOrg (userorg, username) {
// 通过加载配置文件来创建客户端实例
// 根据config.js文件中的配置文件路径设置可知:
// 此步是加载 ./artifacts/network-config.yaml 网络配置文件
// 文件中包含 需要进行交互的目标 blockchain 网络的信息
let config = '-connection-profile-path';
let client = hfc.loadFromConfig(hfc.getConfigSetting('network'+config));
// 此步是加载 ./artifacts/userorg.yaml 其中userorg为org1/org2
// 文件中包含用户状态存储路径(用户键值对)以及公私钥的存储路径(一系列密钥对)
client.loadFromConfig(hfc.getConfigSetting(userorg+config));
// 初始化创建状态存储和加密(密钥)存储
await client.initCredentialStores();
// 如果参数中含有username,则在持久存储中查找该用户,如果查到则说明该用户已经注册并登录
// 并将该User对象分配给刚刚生成的clietn实例
if(username) {
let user = await client.getUserContext(username, true);
if(!user) {
throw new Error(util.format('User was not found :', username));
} else {
logger.debug('User %s was found to be registered and enrolled', username);
}
}
return client;
}
测试
运行网络
./runApp.sh在
Org1注册并登录用户Jim:ORG1_TOKEN=$(curl -s -X POST \
http://localhost:4000/users \
-H "content-type: application/x-www-form-urlencoded" \
-d "username=Jim&orgName=Org1")结果:
{
"success": true,
"secret": "ZVGfOMkmQmBH",
"message": "Jim enrolled Successfully",
"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MjMyODE0OTQsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Ik9yZzEiLCJpYXQiOjE1MjMyNDU0OTR9.61-rp1Nye2Rg8e-91WCQwrTAkGvSJ0Bw_Y0OHQ-hDcg"
}用户的状态信息存储在
./fabric-client-kv-org1中,其中包含用户私钥的ski标识符signingIdentity(16进制bytes格式)和证书certificate(pem格式)。用户的公私钥(pem格式)存储在/tmp/fabric-client-kvs_peerOrg1中,文件名为私钥的ski。
具体调用
regiseter:
CA客户端向CA server节点,发送注册请求,参数为enrollmentID:username 和 affiliation: org1.department1,其中affiliation 在 CA节点的yaml文件中指定,可修改。 注册返回secret以登录。
enroll:
- setUserContext({username:username, password:secret})
- _setUserFromConfig(),CA客户端调用enroll(),发送参数enrollmentID:username, enrollmentSecret:password,登录返回CA节点生成的私钥和证书
- createUser() 发送参数username, mspid, cryptoContent(私钥,证书)
构建User对象 :_signingIdentity, _mspId, _cryptoSuite, _identity, _enrollmentSecret(目前版本属性未赋值)
其中通过 CryptoSuit.importKey(private_key) 将私钥存于本地,文件名为 ski + ‘_priv’
通过setEnrollment() ——> importKey(certificate) 通过证书生成公钥 并存入本地,文件名为ski+’_pub’ - 最后再次调用 setUserContext(User object) ——> saveUserToStateStore() 将Use对象存本地,文件名为username, 内容为User对象的序列化(toString())
总结
- 生成jwt,该token值需要在之后的请求中使用
- 通过加载配置文件生成客户端实例(设置了目标blockchain网络信息 及 用户状态和公私钥的本地存储路径)
- 判断(getUserContext),如果用户未登录,则从配置文件获取信息(secret)登录admin
- 通过CA服务,admin作为注册员,对用户进行注册
- 登录用户并将用户状态信息和密钥对存入本地(setUserContext 一步完成)
Hyperledger Fabric——balance transfer(二)注册用户的更多相关文章
- Hyperledger Fabric——balance transfer(一)启动示例
Blacne transfer是Hyperledger fabric Node SDK的一个示例应用,主要使用了SDK中fabric-client 和 fabric-ca-client 模块中的API ...
- Hyperledger Fabric——balance transfer(六)查询
balance transfer 提供了很多查询接口,包括链码查询,根据区块号查询区块数据,根据交易ID查询交易信息,查询链上的区块数,查询已安装或已实例化的链码,查询通道. 源码解析 1.调用链码查 ...
- Hyperledger Fabric——balance transfer(四)安装和实例化chaincode
详细解析blance transfer示例的安装(install)和实例化(Instantiate)链码(chaincode)的过程.安装chaincode会根据本地的链码文件生成chaincode镜 ...
- Hyperledger Fabric——balance transfer(五)执行交易
链码安装和实例化之后就可以调用chaincode执行交易,下面分析简单的账户转账操作是如何完成的. 源码分析 1.首先看app.js的路由函数 app.post('/channels/:channel ...
- Hyperledger Fabric——balance transfer(三)创建和加入Channel
详细解析blance transfer示例的创建通道(Channel)和加入节点到通道的过程. 创建Channel 1.首先看app.js的路由函数 var createChannel = requi ...
- Hyperledger Fabric链码之二
上篇文章中我们介绍了链码的概念,本文中我们将介绍Fabric下链码的编写和测试.我们会通过一个简单例子的方式来阐述链码API的使用. 链码API 每一个链码程序都必须实现一个接口Chainco ...
- Hyperledger Fabric开发(二):创建网络
运行fabric-samples项目中的一个例子:first-network,创建第一个网络(Building Your First Network). 该网络共有4个peer节点,划分为2个组织(o ...
- Hyperledger Fabric 实战(十二): Fabric 源码本地调试
借助开发网络调试 fabric 源码本地调试 准备工作 IDE Goland Go 1.9.7 fabric-samples 模块 chaincode-docker-devmode fabric 源码 ...
- Hyperledger Fabric链码之三
在<Hyperledger Fabric链码之一>和<Hyperledger Fabric链码之二>中我们介绍了链码的定义,并通过dev网络测试了测试了自己编写的链码程序. 本 ...
随机推荐
- 二进制安装MySQL及破解密码
二进制安装MySQL及破解密码 1.确保系统中有依赖的libaio 软件,如果没有: yum -y install libaio 2.解压二进制MySQL软件包 tar xf mysql-5.7.24 ...
- 【linux题目】第三关
1. 解释下什么是GPL,GNU,自由软件? 2. 如何选择Linux操作系统版本? 3. 安装系统时如何给Linux操作系统分区? 4. 描述Linux系统的启动过程? 5. 简要说出20个Linu ...
- Bootstrap表格组件 Bootstrap Table
Bootstrap Table是Bootstrap的一个组件 Bootstrap Table Demo:http://issues.wenzhixin.net.cn/bootstrap-table/i ...
- 移动App性能测评与优化1.4.4 多进程应用
1.4.4 多进程应用 根据上一节中的描述,当一个进程结束后,它所占用的共享库内存将会被其他仍然使用该共享库的进程所分担,共享库消耗的物理内存并不会减少.实际上,对于所有共享使用了这个库的应用,Pss ...
- 手把手教你用Node.js爬虫爬取网站数据
个人网站 https://iiter.cn 程序员导航站 开业啦,欢迎各位观众姥爷赏脸参观,如有意见或建议希望能够不吝赐教! 开始之前请先确保自己安装了Node.js环境,还没有安装的的童鞋请自行百度 ...
- Soldiers Sortie
这部2006年上映的作品豆瓣评分9.3,时至今日仍然有人乐此不疲地讨论剧情和启发,我想称之为经典应该不算过分. 这部剧我至少完整看过3遍,随着阅历增加,对某些角色的体会也变得更深刻. 许三多 许三多是 ...
- 题目分享V
题意:现在两个人做游戏,每个人刚开始都是数字1,谁赢了就能乘以k^2,输的乘以k(k可以是任意整数,每次不一定相同)现在给你最终这两个人的得分,让你判断是否有这个可能,有可能的话Yes,否则No. 分 ...
- Java采用反射技术创建对象后对目标类的成员变量和成员方法进行访问
实现: package com.ljy; import java.lang.reflect.Field; import java.lang.reflect.Method; /** * * @Class ...
- 内存淘汰机制——LRU与LFU
内存淘汰机制之LRU与LFU LRU(Least Recently Used):淘汰 近期最不会访问的数据 LFU(Least Frequently Used):淘汰 最不经常使用(访问次数少) 所谓 ...
- java基础篇 之 异常丢失
我们看如下代码: @Slf4j public class Test { public static void main(String[] args) { try { try { test(); } f ...