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网络测试了测试了自己编写的链码程序. 本 ...
随机推荐
- Vue集成tinymce富文本编辑器并实现本地化指南(2019.11.21最新)
tinymce是一款综合口碑特别好.功能异常强大的富文本编辑器,在某些网站,甚至享有"宇宙最强富文本编辑器"的称号.那么,在Vue项目中如何集成呢?这并不困难,只需要参照官方教程 ...
- 在okhttp的callback回调中加Toast出现Cant create handler inside hread that has not called Looper.prepare()...
2019独角兽企业重金招聘Python工程师标准>>> 分析:callback中回调的response方法中还是在子线程中运行的,所以要调取Toast必须回到主线程中更新ui 解决方 ...
- postman的使用概览
本文主要描述postman的功能与使用方法Postman是404大厂的基于javascript语言完成的一款超级强大的插件,名字也很亲近(邮递员).可以用于做API请求测试.前端后台测试使用Postm ...
- Cisco 交换机启用netflow
Router2951#configure terminal //Creating Flow Record router2951(config)# flow record NTArecord route ...
- bootstrap-内联文本元素-小号文本
说明 使用 <small> 标签包裹,其内的文本将被设置为父容器字体大小的 85% 示例 <!DOCTYPE html> <html lang="zh-CN&q ...
- C# richtextbox 自动下拉到最后 方法 & RichTextBox读取txt中文后出现乱码
C# richtextbox 自动滚动到最后 光标到最后 自动显示最后一行 private void richTextBox1_TextChanged(object sender, EventArg ...
- PHP字符串全排列算法
<?php /** * PHP字符串全排列算法 */ $results = []; $arr = []; function bfs($start) { global $arr; global $ ...
- 信息奥赛一本通1486: CH 6202 黑暗城堡 最短路径生成树计数
1486:黑暗城堡 [题目描述] 知道黑暗城堡有 N 个房间,M 条可以制造的双向通道,以及每条通道的长度. 城堡是树形的并且满足下面的条件: 设 Di为如果所有的通道都被修建,第 i 号房间与第 1 ...
- CF906D Power Tower
扩展欧拉定理 CF906D Power Tower 洛谷交的第二个黑题 题意 给出一个序列\(w-1,w_2,\cdots,w_n\),以及\(q\)个询问 每个询问给出\(l,r\),求: \[w_ ...
- Spring Cloud 学习 之 Spring Cloud Bus实现修改远程仓库后配置自动刷新
版本号: Spring Boot:2.1.3.RELEASE Spring Cloud:G版 开发工具:IDEA 搭建配置中心,这里我们搭建一个简单版的就行 POM: <?xml ...