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网络测试了测试了自己编写的链码程序. 本 ...
随机推荐
- opencv-3-图片存储与相对路径
opencv-3-图片存储与相对路径 opencvqtC++ 在上一篇文章opencv 显示第一副图像 中, 我们完成了一副图像的显示, 包括使用VS,和QT 进行显示.. 本文将展示如何进行图像的显 ...
- Spring Boot devtool的使用
文章目录 添加Spring Boot devtool依赖 默认属性 自动重启 Live Reload 全局配置 Spring Boot devtool的使用 Spring Boot为我们提供了一个便捷 ...
- Spring5参考指南:容器扩展
文章目录 BeanPostProcessor自定义bean BeanFactoryPostProcessor自定义配置元数据 使用FactoryBean自定义实例化逻辑 Spring提供了一系列的接口 ...
- 标准库模块time,datetime
在Python中,通常有这几种方式来表示时间: 1)时间戳 2)格式化的时间字符串 3)元组(struct_time)共九个元素. 由于Python的time模块实现主要调用C库,所以各个平台可能有所 ...
- CYQ.Data 轻量数据层之路 使用篇-MProc 存储过程与SQL 视频[最后一集] H (二十八)
2019独角兽企业重金招聘Python工程师标准>>> 说明: 本次录制主要为使用篇:CYQ.Data 轻量数据层之路 使用篇五曲 MProc 存储过程与SQL(十六) 的附加视 ...
- javascript 控制台调试方法
console在我们调试js程序的时候是一个非常有效的工具. 日志输出是我们最常用的功能: console.log(); console.info(); console.warn(); console ...
- C++类的开发实战--班级成绩管理系统
#include<bits/stdc++.h> using namespace std; struct birth { int year,month,day; }; struct grad ...
- 基于UDP的客户端和服务器端的代码设计
实验平台 linux 实验内容 编写UDP服务器和客户端程序,客户端发送消息,服务器接收消息,并打印客户端的IP地址和端口号. 实验原理 UDP是无需连接的通信,其主要实现过程如下: 同样,我们可以按 ...
- UIResponder相关
UIResponder是OC中一个响应事件的类.UIApplication.UIView.UIViewController都是它的子类.UIWindow是UIView的子类,因此也能响应事件. UIR ...
- mui日期设置与时钟样式时间设置
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name ...