在项目中一般需要设置不同用户的不同权限,fabric中的链码执行同样如此,可以通过用户标签来实现。
流程:sdk申请用户时,指定用户标签,链码执行时获取对应标签实现链码调用权限验证。
sdk代码(注册用户):
/**
* 注册用户并进行登记
* @param orgName 所在组织
* @param userName 用户名
* @param pwd 用户密码
* @return 一个新的用户
*/
public FabricUser registerAndEnrolledUser(String orgName, String userName, String pwd) throws Exception {
FabricUser user = getUser(orgName, userName);
RegistrationRequest request = new RegistrationRequest(userName, orgName.toLowerCase() + AFFILIATION);
request.setSecret(pwd);
//开始设置值
request.addAttribute(new Attribute("org","org1"));
request.addAttribute(new Attribute("peer","peer0.org1.example.com"));
request.addAttribute(new Attribute("user", "fang"));
request.addAttribute(new Attribute("dept", "test"));
EnrollmentRequest req = new EnrollmentRequest();
req.addAttrReq(); // empty ensure no attributes.
user.setEnrollmentSecret(ca.register(request, admin));
if (!user.getEnrollmentSecret().equals(pwd)) {
throw new RuntimeException("设置密码异常,您设置的密码与系统返回的密码不一致:yourPwd:" + pwd + ", system:" + user.getEnrollmentSecret());
}
user.setEnrollment(ca.enroll(userName, pwd, req));
//测试
user.setMspId(orgName + "MSP");
client.setUserContext(user);
return user;
}
链码操作代码:
func (t *TestChaincode) getUser(stub shim.ChaincodeStubInterface, args []string) pb.Response {
logger := getLogger(stub)
logger.Info(fmt.Sprintf("get args: %s", args))
//getting submitter of the transaction
logger.Info("begin to read userInfo")
sinfo, err := cid.New(stub)
if err != nil {
logger.Error(fmt.Sprintf("get submitter of the transaction: %s", sinfo))
return shim.Error(err.Error())
}
id, _ := sinfo.GetID()
logger.Info(fmt.Sprintf("get clientIdentityId: %s", id))
mspId, _ := sinfo.GetMSPID()
logger.Info(fmt.Sprintf("get clientIdentityMSPId: %s", mspId))
//读取dept的相关值
dv, df, err := sinfo.GetAttributeValue("dept")
if err != nil {
logger.Error(fmt.Sprintf("get deptAttrVal err: %s", err.Error()))
} else {
if df {
logger.Info(fmt.Sprintf("get deptAttrVal: %s", dv))
} else {
logger.Debug(fmt.Sprintf("not found deptAttrbute"))
}
}
//读取org
ov, of, err := sinfo.GetAttributeValue("org")
if err != nil {
logger.Error(fmt.Sprintf("get orgAttrVal err: %s", err.Error()))
} else {
if of {
logger.Info(fmt.Sprintf("got orgAttrVal: %s", ov))
} else {
logger.Debug(fmt.Sprintf("not found orgAttrbute"))
}
}
//读取peer
pv, pf, err := sinfo.GetAttributeValue("peer")
if err != nil {
logger.Error(fmt.Sprintf("get peerAttrVal err: %s", err.Error()))
} else {
if pf {
logger.Info(fmt.Sprintf("got peerAttrVal: %s", pv))
} else {
logger.Debug(fmt.Sprintf("not found peerAttrbute"))
}
}
//读取user
uv, uf, err := sinfo.GetAttributeValue("user")
if err != nil {
logger.Error(fmt.Sprintf("get userAttrVal err: %s", err.Error()))
} else {
if uf {
logger.Info(fmt.Sprintf("got userAttrVal: %s", uv))
} else {
logger.Debug(fmt.Sprintf("not found userAttrbute"))
}
}
return shim.Success([]byte("请看日志"))
}
日志部分截图:
通过自定义标签即可实现权限的基本控制,具体设计就看公司项目了。
其它代码:
@Slf4j
public class PeerConfig {
private static final String PROP_BASE = "org.hyperledger.fabric.bdo.";
private static final String GOSSIP_WAIT_TIME = PROP_BASE + "GossipWaitTime";
private static final String INVOKE_WAIT_TIME = PROP_BASE + "InvokeWaitTime";
private static final String DEPLOY_WAIT_TIME = PROP_BASE + "DeployWaitTime";
private static final String PROPOSAL_WAIT_TIME = PROP_BASE + "ProposalWaitTime";
private static final String BDO_CHANNEL_NAME = PROP_BASE + "ChannelName";
private static final String INTEGRATIONTESTS_ORG = PROP_BASE + "org.";
private static final Pattern ORG_PAT = Pattern.compile("^" + Pattern.quote(INTEGRATIONTESTS_ORG) + "([^\\.]+)\\.mspid$");
private static final Properties SDK_PROP = new Properties();
private boolean runningTLS;
private boolean runningFabricCATLS;
private boolean runningFabricTLS;
private static final HashMap<String, FabricOrg> ORGS = new HashMap<>();
public static PeerConfig peerConfig = null;
public static PeerConfig getInstance() {
if (peerConfig == null) {
synchronized (PeerConfig.class) {
if (peerConfig == null) {
peerConfig = new PeerConfig();
}
}
}
return peerConfig;
}
private PeerConfig() {
log.info("初始化配置文件");
File loadFile;
FileInputStream configProps;
try {
loadFile = ResourceUtils.getFile(Consts.CHAIN_PATH + "utils.properties");
log.info(String.format("从 %s 下加载配置文件,文件是否存在:%b", loadFile.toString(), loadFile.exists()));
configProps = new FileInputStream(loadFile);
SDK_PROP.load(configProps);
} catch (FileNotFoundException e) {
log.error("配置文件丢失,请核实");
} catch (IOException e) {
log.error("出现了异常:{}", e.getMessage());
} finally {
//为什么要加这一句
defaultProperty(INTEGRATIONTESTS_ORG, null);
runningTLS = true;
runningFabricCATLS = true;
runningFabricTLS = runningTLS;
for (Map.Entry<Object, Object> x : SDK_PROP.entrySet()) {
final String key = x.getKey() + "";
final String val = x.getValue() + "";
if (key.startsWith(INTEGRATIONTESTS_ORG)) {
Matcher match = ORG_PAT.matcher(key);
if (match.matches() && match.groupCount() == 1) {
String orgName = match.group(1).trim();
ORGS.put(orgName, new FabricOrg(orgName, val.trim()));
}
}
}
log.info(JSON.toJSONString(SDK_PROP));
for (Map.Entry<String, FabricOrg> org : ORGS.entrySet()) {
final FabricOrg fabricOrg = org.getValue();
final String orgName = org.getKey();
String peerNames = SDK_PROP.getProperty(INTEGRATIONTESTS_ORG + orgName + ".peer_locations");
String[] ps = peerNames.split("[ \t]*,[ \t]*");
for (String peer : ps) {
String[] nl = peer.split("[ \t]*@[ \t]*");
fabricOrg.addPeerLocation(nl[0], grpcTLSify(nl[1]));
}
final String domainName = SDK_PROP.getProperty(INTEGRATIONTESTS_ORG + orgName + ".domname");
fabricOrg.setDomainName(domainName);
String ordererNames = SDK_PROP.getProperty(INTEGRATIONTESTS_ORG + orgName + ".orderer_locations");
ps = ordererNames.split("[ \t]*,[ \t]*");
for (String peer : ps) {
String[] nl = peer.split("[ \t]*@[ \t]*");
fabricOrg.addOrdererLocation(nl[0], nl[1]);
}
String eventHubNames = SDK_PROP.getProperty(INTEGRATIONTESTS_ORG + orgName + ".eventhub_locations");
ps = eventHubNames.split("[ \t]*,[ \t]*");
for (String peer : ps) {
String[] nl = peer.split("[ \t]*@[ \t]*");
fabricOrg.addEventHubLocation(nl[0], grpcTLSify(nl[1]));
}
fabricOrg.setCaLocation(httpTLSify(SDK_PROP.getProperty(INTEGRATIONTESTS_ORG + org.getKey() + ".ca_location")));
if (runningFabricCATLS) {
log.info("loading fabric ca tls cert");
String cert = Consts.CHAIN_PATH + "/e2e/channel/crypto-config/peerOrganizations/DNAME/ca/ca.DNAME-cert.pem".replaceAll("DNAME", domainName);
log.info("tls cert path:" + cert);
File cf;
try {
cf = ResourceUtils.getFile(cert);
} catch (FileNotFoundException e) {
throw new RuntimeException("证书文件不存在:" + e.getMessage());
}
// if (!cf.exists() || !cf.isFile()) {
// throw new RuntimeException("证书文件不存在:" + cf.getAbsolutePath());
// }
Properties properties = new Properties();
properties.setProperty("pemFile", cf.getAbsolutePath());
properties.setProperty("allowAllHostNames", "true");
fabricOrg.setCaProperties(properties);
}
}
log.info(JSON.toJSONString(SDK_PROP));
}
}
public boolean isRunningTLS(){
return runningTLS;
}
private String httpTLSify(String location) {
location = location.trim();
return runningFabricCATLS ? location.replaceFirst("^http://", "https://") : location;
}
private String grpcTLSify(String location) {
location = location.trim();
Exception e = Utils.checkGrpcUrl(location);
if (e != null) {
throw new RuntimeException(String.format("grpc指定的url:%s 参数异常", location), e);
}
log.info(runningFabricTLS ? location.replaceFirst("^grpc://", "^grpcs://") : location);
return runningFabricTLS ? location.replaceFirst("^grpc://", "^grpcs://") : location;
}
private String getProperty(String property) {
String ret = SDK_PROP.getProperty(property);
if (null == ret) {
log.warn(String.format("没有发现该属性:%s", property));
}
return ret;
}
private static void defaultProperty(String key, String value) {
String ret = System.getProperty(key);
if (ret != null) {
SDK_PROP.put(key, value);
} else {
String envKey = key.toUpperCase().replaceAll("\\.", "_");
ret = System.getenv(envKey);
if (null != ret) {
SDK_PROP.put(key, value);
} else {
if (null == SDK_PROP.getProperty(key) && value != null) {
SDK_PROP.put(key, value);
}
}
}
}
public int getTransactionWaitTime() {
return Integer.parseInt(getProperty(INVOKE_WAIT_TIME));
}
public int getDeployWaitTime() {
return Integer.parseInt(getProperty(DEPLOY_WAIT_TIME));
}
public int getGossipWaitTime() {
return Integer.parseInt(getProperty(GOSSIP_WAIT_TIME));
}
public int getProposalWaitTime() {
return Integer.parseInt(getProperty(PROPOSAL_WAIT_TIME));
}
public String getDefaultChannelName(){
return getProperty(BDO_CHANNEL_NAME);
}
public Collection<FabricOrg> getIntegrationTestFabricOrgs() {
return Collections.unmodifiableCollection(ORGS.values());
}
public FabricOrg getIntegrationFabricOrg(String name) {
return ORGS.get(name);
}
public String getPeerProperty(String org) {
org = org.substring(0, 1).toUpperCase() + org.substring(1);
return getProperty(INTEGRATIONTESTS_ORG + "peer" + org + ".peer_locations");
}
public String getOrdererProperty() {
// org.hyperledger.fabric.wnzx.org.peerOrg1.orderer_locations
return getProperty(INTEGRATIONTESTS_ORG + "peerOrg1.orderer_locations");
}
public Properties getPeerProperties(String name) {
return getEndPointProperties("peer", name);
}
public Properties getOrdererProperties(String name) {
return getEndPointProperties("orderer", name);
}
public Properties getEndPointProperties(final String type, final String name) {
final String domainName = getDommainName(name);
File cert = Paths.get(getChannelPath(),
"crypto-config/ordererOrganizations".replace("orderer", type),
domainName,
type + "s",
name,
"tls/server.crt")
.toFile();
if (!cert.exists()) {
throw new RuntimeException(String.format("%s 文件加载失败,在指定地址未找到:%s", name, cert.getAbsolutePath()));
}
Properties ret = new Properties();
ret.setProperty("pemFile", cert.getAbsolutePath());
ret.setProperty("hostnameOverride", name);
ret.setProperty("sslProvider", "openSSL");
ret.setProperty("negotiationType", "TLS");
return ret;
}
private String getDommainName(final String name) {
int dot = name.indexOf(".");
if (-1 == dot) {
return null;
} else {
return name.substring(dot + 1);
}
}
public Properties getEventHubProperties(String name) {
return getEndPointProperties("peer", name);
}
public String getChannelPath() {
return Consts.CHAIN_PATH + "e2e/channel";
}
}
@Slf4j
public class CAClient {
private static final String AFFILIATION = ".department1";
private HFCAClient caClient;
private CryptoSuite cryptosuite;
private HFClient client = HFClient.createNewInstance();
private PeerConfig config = PeerConfig.getInstance();
private CAClient() {
try {
log.debug("init crypto...");
cryptosuite = CryptoSuite.Factory.getCryptoSuite();
client.setCryptoSuite(cryptosuite);
} catch (Exception e) {
e.printStackTrace();
}
}
public static CAClient createNewInstance() {
return new CAClient();
}
public HFClient getClient() {
return client;
}
public String register(FabricUser register, String userName, String secret, String affiliation, String type, Map<String, String> attrs) throws CAException {
return register(register, userName, secret, affiliation, type, -1, attrs);
}
/**
* @param register 注册发起人
* @param userName 注册用户名
* @param secret 用户密码
* @param affiliation 用户关联关系
* @param type 用户类型
* @param maxEnrollments 最大注册数量
*
* @throws CAException 注册异常
*/
public String register(FabricUser register, String userName, String secret, String affiliation, String type, int maxEnrollments, Map<String, String> attrs) throws CAException {
try {
initClient(register);
RegistrationRequest request = new RegistrationRequest(userName, affiliation);
request.setAffiliation(affiliation);
request.setEnrollmentID(userName);
request.setType(type);
if (!"".equals(secret)){
request.setSecret(secret);
}
request.setMaxEnrollments(maxEnrollments);
for (String key : attrs.keySet()){
request.addAttribute(new Attribute(key, attrs.get(key)));
}
return caClient.register(request, register);
} catch (Exception e) {
e.printStackTrace();
throw new CAException(e.getMessage());
}
}
/**
* 注册用户并进行登记
* @param orgName 所在组织
* @param userName 用户名
* @param pwd 用户密码
* @return 一个新的用户
*/
public FabricUser registerAndEnrolledUser(String orgName, String userName, String pwd, FabricUser admin, Map<String, String> attrs) throws CAException {
try {
initClient(admin);
FabricUser user = new FabricUser(userName, orgName);
RegistrationRequest request = new RegistrationRequest(userName, orgName.toLowerCase() + AFFILIATION);
request.setSecret(pwd);
//开始设置值(进行权限控制)
for (String key : attrs.keySet()) {
request.addAttribute(new Attribute(key, attrs.get(key)));
}
user.setEnrollmentSecret(caClient.register(request, admin));
if (!user.getEnrollmentSecret().equals(pwd)) {
throw new RuntimeException("设置密码异常,您设置的密码与系统返回的密码不一致:yourPwd:" + pwd + ", system:" + user.getEnrollmentSecret());
}
user.setEnrollment(caClient.enroll(userName, pwd));
//测试
user.setMspId(orgName + "MSP");
client.setUserContext(user);
return user;
} catch (Exception e) {
e.printStackTrace();
throw new CAException(e.getMessage());
}
}
/**
* 撤销用户
*/
public void revokeUser(FabricUser admin, String userName, String reason) throws CAException {
try {
initClient(admin);
caClient.revoke(admin, userName, reason);
} catch (Exception e) {
e.printStackTrace();
throw new CAException(e.getMessage());
}
}
private void initClient(FabricUser user) throws MalformedURLException {
log.info(user.getOrganization()+"'s."+user.getName()+" begin to enroll fabric CA");
FabricOrg org = config.getIntegrationFabricOrg("peer" + user.getOrganization());
log.info("get organization's config file");
String caLocation = org.getCaLocation();
caClient = HFCAClient.createNewInstance(caLocation, org.getCaProperties());
caClient.setCryptoSuite(cryptosuite);
}
/**
* 登录一个用户
*/
public FabricUser enroll(FabricUser user) throws CAException {
try {
initClient(user);
EnrollmentRequest req = new EnrollmentRequest();
Enrollment enrollment = caClient.enroll(user.getName(), user.getEnrollmentSecret(), req);
if (enrollment == null) {
log.error("login to ca fail, please check userinfo");
throw new CAException("登录失败,请检查用户信息。");
}
user.setEnrollment(enrollment);
user.setMspId(user.getOrganization() + "MSP");
client.setUserContext(user);
return user;
} catch (Exception e) {
throw new CAException(e.getMessage());
}
}
public FabricUser reenroll(FabricUser user) throws CAException {
try {
initClient(user);
Enrollment e = caClient.reenroll(user);
if (e == null){
return null;
}
user.setEnrollment(e);
client.setUserContext(user);
return user;
} catch (Exception e) {
e.printStackTrace();
throw new CAException(e.getMessage());
}
}
public HFCAIdentity readIdentified(FabricUser user) throws CAException {
try {
initClient(user);
HFCAIdentity hfcaIdentity = caClient.newHFCAIdentity(user.getName());
hfcaIdentity.read(user);
return hfcaIdentity;
} catch (InvalidArgumentException e) {
e.printStackTrace();
throw new CAException("参数异常:" + e.getMessage());
} catch (IdentityException e) {
e.printStackTrace();
throw new CAException("身份异常:" + e.getMessage());
} catch (MalformedURLException e) {
e.printStackTrace();
throw new CAException(e.getMessage());
}
}
public HFCAAffiliation readAffiliation(FabricUser user) throws CAException {
HFCAAffiliation hfcaAffiliation;
try {
hfcaAffiliation = caClient.newHFCAAffiliation(user.getName());
hfcaAffiliation.read(user);
return hfcaAffiliation;
} catch (InvalidArgumentException e) {
e.printStackTrace();
throw new CAException("参数异常:" + e.getMessage());
} catch (AffiliationException e) {
e.printStackTrace();
throw new CAException("身份异常:" + e.getMessage());
}
}
}
@Slf4j
@Data
public class FabricUser implements User {
/**
* 名称
*/
private String name;
/**
* 权限
*/
private Set<String> roles;
/**
* 账户
*/
private String account;
/**
* 用户关联关系
*/
private String affiliation;
/**
* 组织
*/
private String organization;
/**
* 秘钥
*/
private String enrollmentSecret;
/**
* mspid
*/
private String mspId;
/**
* 登录后返回的证书和私钥
*/
Enrollment enrollment = null;
private Attrs attrs;
public FabricUser(String name, String org) {
this.name = name;
this.organization = org;
}
/**
* 设置账户信息并将用户状态更新只存储配置对象
*/
public void setAccount(String account) {
this.account = account;
}
@Override
public String getAccount() {
return this.account;
}
@Override
public String getAffiliation() {
return this.affiliation;
}
@Override
public Enrollment getEnrollment() {
return this.enrollment;
}
@Override
public String getMspId() {
return this.mspId;
}
@Override
public String getName() {
return this.name;
}
@Override
public Set<String> getRoles(){
return this.roles;
}
public String getEnrollmentSecret() {
return enrollmentSecret;
}
/**
* 设置注册操作的秘钥信息将用户状态更新至存储配置对象
*/
public void setEnrollmentSecret(String enrollmentSecret) {
this.enrollmentSecret = enrollmentSecret;
}
/**
* 设置注册登记操作信息并将用户状态更新只存储配置对象
*/
public void setEnrollment(Enrollment enrollment) {
this.enrollment = enrollment;
}
/**
* 确定这个名称是否已注册
*/
public boolean isRegistered() {
return !"".equals(enrollmentSecret) && enrollmentSecret != null;
}
/**
* 是否已经登录
*/
public boolean isEnrolled() {
return this.enrollment != null;
}
public String getOrganization() {
return organization;
}
public void setOrganization(String org) {
this.organization = org;
}
public Attrs getX509CertExtension() throws Exception {
if (!isEnrolled()){
throw new CAException("用户未登录");
}
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
X509Certificate x509Certificate = (X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(enrollment.getCert().getBytes()));
// System.out.println("读取Cer证书信息...");
// System.out.println("x509Certificate_SerialNumber_序列号__: " + x509Certificate.getSerialNumber());
// System.out.println("x509Certificate_getIssuerDN_发布方标识名__: " + x509Certificate.getIssuerDN());
// System.out.println("x509Certificate_getSubjectDN_主体标识__: " + x509Certificate.getSubjectDN());
// System.out.println("x509Certificate_getSignAlgOID_证书算法OID字符串__: " + x509Certificate.getSigAlgOID());
// System.out.println("x509Certificate_getNotBefore_证书有效期__: " + x509Certificate.getNotAfter());
// System.out.println("x509Certificate_getSigAlgName_签名算法__: " + x509Certificate.getSigAlgName());
// System.out.println("x509Certificate_getVersion_版本号__: " + x509Certificate.getVersion());
// System.out.println("x509Certificate_getPublicKey_公钥__: " + x509Certificate.getPublicKey());
//
// System.out.println("\n\n");
byte[] extensionValue = x509Certificate.getExtensionValue("1.2.3.4.5.6.7.8.1");
// 重新解析json串
StringBuilder builder = new StringBuilder();
builder.append("{");
String obj = new String(extensionValue);
obj = obj.substring(obj.indexOf("{"));
JSONObject jsonObject = JSON.parseObject(obj);
Object attrsV = jsonObject.get("attrs");
JSONObject jsonObject1 = JSON.parseObject(attrsV.toString());
Set<Map.Entry<String, Object>> entries = jsonObject1.entrySet();
for (Map.Entry<String, Object> key : entries){
builder.append(",\"").append(key.getKey().replaceAll("\\.", "")).append("\":\"").append(key.getValue()).append("\"");
}
builder.append("}");
obj = builder.toString().replaceFirst(",", "");
this.attrs = JSON.parseObject(obj, Attrs.class);
return this.attrs;
}
}