2021SC@SDUSC
目录
一、账本的安全管理器以及特定交易实务的账本安全管理
1.基于区块链的网络安全机制具有去中心化、不可篡改、可追溯、高可信和高可用的特性,有利于提升网络安全性。
2.网络安全可以采用区块链(blockchain)技术,建立一个去中心化的、由各节点共同参与运行的分布式系统架构进行数据的管理,避免中心节点故障引起的网络安全事故。
3.将区块链技术和链外数据库结合,分离数据和数据权限,能够实现去中心化的个人数据管理系统,进行数据和权限的管理。应用程序访问用户数据之前,需要得到用户的访问授权。
4.数字签名是一项用于证实某个文件或数据的完整性和来源的技术,确保文件或数据未被修改和不可抵赖。
二、代码分析
1.接口
LedgerSecurityManager
下面代码返回与指定的终端用户和节点参与方相关的安全策略。
ndpoints 终端用户的地址列表;
nodes 节点参与方的地址列表。
SecurityPolicy getSecurityPolicy(Set<Bytes> endpoints, Set<Bytes> nodes);
代码可以返回指定用户的角色权限。
UserRolesPrivileges getUserRolesPrivilegs(Bytes userAddress);
SecurityPolicy
这个接口是针对特定交易请求的账本安全策略,我们可以看看里面的内容
签署交易的终端用户的地址列表
Set<Bytes> getEndpoints();
签署交易的节点参与方的地址列表
Set<Bytes> getNodes();
检验终端身份是否合法,如果不合法,则返回false,反之返回true。
boolean isEndpointValid(MultiIDsPolicy midPolicy);
检验节点身份是否合法,如果不合法,则返回false,反之返回true。
boolean isNodeValid(MultiIDsPolicy midPolicy);
检查签署交易的终端用户。
要检查的权限;
midPolicy 针对多个签名用户的权限策略;
返回 true 表示获得授权; 返回 false 表示未获得授权;
boolean isEndpointEnable(LedgerPermission permission, MultiIDsPolicy midPolicy);
查签署交易的终端用户;
midPolicy 针对多个签名用户的权限策略;
返回 true 表示获得授权; 返回 false 表示未获得授权;
boolean isEndpointEnable(TransactionPermission permission, MultiIDsPolicy midPolicy);
检查签署交易的节点参与方
permission 要检查的权限;
midPolicy 针对多个签名用户的权限策略;
返回 true 表示获得授权; 返回 false 表示未获得授权;
boolean isNodeEnable(LedgerPermission permission, MultiIDsPolicy midPolicy);
检查签署交易的节点参与方;
permission 要检查的权限;
midPolicy 针对多个签名用户的权限策略;
返回 true 表示获得授权; 返回 false 表示未获得授权;
boolean isNodeEnable(TransactionPermission permission, MultiIDsPolicy midPolicy);
检查终端身份的合法性;
void checkEndpointValidity(MultiIDsPolicy midPolicy) throws LedgerSecurityException;
检查节点身份的合法性;
void checkNodeValidity(MultiIDsPolicy midPolicy) throws LedgerSecurityException;
检查签署交易的终端用户
permission 要检查的权限;
midPolicy 针对多个签名用户的权限策略;
void checkEndpointPermission(LedgerPermission permission, MultiIDsPolicy midPolicy) throws LedgerSecurityException;
检查签署交易的终端账户{@linkTransactionRequest#getEndpointSignatures()})是否被授权了参数指定的权限,如果未获授权,方法将引发 {@link LedgerSecurityException} 异常;
void checkEndpointPermission(TransactionPermission permission, MultiIDsPolicy midPolicy)
throws LedgerSecurityException;
检查签署交易的节点参与方(来自{@link TransactionRequest#getNodeSignatures()})是否被授权了参数指定的权限;如果未获授权,方法将引发 {@link LedgerSecurityException} 异常;
void checkNodePermission(LedgerPermission permission, MultiIDsPolicy midPolicy) throws LedgerSecurityException;
检查签署交易的节点参与方(来自{@link TransactionRequest#getNodeSignatures()})是否被授权了参数指定的权限;如果未获授权,方法将引发 {@link LedgerSecurityException} 异常;
void checkNodePermission(TransactionPermission permission, MultiIDsPolicy midPolicy) throws LedgerSecurityException;
2.类
SecurityContext
1.第一个这个是一个private的对象,新建了一个名为policyHolder的SecurityPolicy的对象,我们可以在下面的方法中在这个对象中添加上述接口的方法来定制特定的检验policy
2.第二个静态方法是在policyHolder中添加新的校验方法。
3.第三个方法是在policyHolder中删除特定的校验方法。
4.第四个方法适用于获取并查看新建对象中的检验policy。
*1*
private static ThreadLocal<SecurityPolicy> policyHolder = new ThreadLocal<SecurityPolicy>();
*2*
public static void setContextUsersPolicy(SecurityPolicy policy) {
policyHolder.set(policy);
}
*3*
public static SecurityPolicy removeContextUsersPolicy() {
SecurityPolicy p = policyHolder.get();
policyHolder.remove();
return p;
}
*4*
public static SecurityPolicy getContextUsersPolicy() {
return policyHolder.get();
}
把上下文安全策略切换为指定的策略,并执行参数指定的Runnable}操作,当操作完成后恢复原来的上下文策略;
public static void switchContextUsersPolicy(SecurityPolicy contextUsersPolicy, Runnable runnable) {
}
LegderSecurityManagerImpl
这个类我们可以把它理解成一个账本管理器
这些都是下面用的隐藏对象;
rolePrivilegeSettings是对于角色权限的设置对象;
userPrivilegeSettings这个是用户权限的设置对象;
接下来是三个是用户权限的配置;
最后两个是用户账户查询。
private RolePrivilegeSettings rolePrivilegeSettings;
private UserAuthorizationSettings userRolesSettings;
// 用户的权限配置
private Map<Bytes, UserRolesPrivileges> userPrivilegesCache = new ConcurrentHashMap<>();
private Map<Bytes, UserRoles> userRolesCache = new ConcurrentHashMap<>();
private Map<String, RolePrivileges> rolesPrivilegeCache = new ConcurrentHashMap<>();
private ParticipantCollection participantsQuery;
private UserAccountSet userAccountsQuery;
这个方法是让this获取当前对象的当前状态的值
public LedgerSecurityManagerImpl(RolePrivilegeSettings rolePrivilegeSettings, UserAuthorizationSettings userRolesSettings,
ParticipantCollection participantsQuery, UserAccountSet userAccountsQuery) {
this.rolePrivilegeSettings = rolePrivilegeSettings;
this.userRolesSettings = userRolesSettings;
this.participantsQuery = participantsQuery;
this.userAccountsQuery = userAccountsQuery;
}
我们可以看到这段代码中有一个put()
的方法,它是java.util.HashMap 中的方法,其作用是在此映射中关联指定值与指定键;
接下来我们来看看这个方法具体的作用,这个方法是用于获取账户的用户配置,并且与其对应的哈希地址关联起来,如果哈希值为null,则新分配一个地址储存相应的值。
public SecurityPolicy getSecurityPolicy(Set<Bytes> endpoints, Set<Bytes> nodes) {
Map<Bytes, UserRolesPrivileges> endpointPrivilegeMap = new HashMap<>();
Map<Bytes, UserRolesPrivileges> nodePrivilegeMap = new HashMap<>();
for (Bytes userAddress : endpoints) {
UserRolesPrivileges userPrivileges = getUserRolesPrivilegs(userAddress);
endpointPrivilegeMap.put(userAddress, userPrivileges);
}
for (Bytes userAddress : nodes) {
UserRolesPrivileges userPrivileges = getUserRolesPrivilegs(userAddress);
nodePrivilegeMap.put(userAddress, userPrivileges);
}
return new UserRolesSecurityPolicy(endpointPrivilegeMap, nodePrivilegeMap, participantsQuery, userAccountsQuery);
}
这个方法首先获取一个账户的用户地址,如果改地址不为null则返回该用户账本的信息;
然后方法建立了一个用户权限的表,然后用if语句嵌套put()方法,加载出用户的角色列表;
在接下来计算用户的综合权限,用for语句遍历角色的权限列表,再将它们一一put出来,完成这一系列操作后,这一用户的权限将会捆绑到该用户被记录到上面创建的表格当中;
最后如果该角色没有任何的权限,则赋予它默认的角色权限。
public UserRolesPrivileges getUserRolesPrivilegs(Bytes userAddress) {
UserRolesPrivileges userPrivileges = userPrivilegesCache.get(userAddress);
if (userPrivileges != null) {
return userPrivileges;
}
UserRoles userRoles = null;
List<RolePrivileges> privilegesList = new ArrayList<>();
// 加载用户的角色列表;
userRoles = userRolesCache.get(userAddress);
if (userRoles == null) {
userRoles = userRolesSettings.getUserRoles(userAddress);
if (userRoles != null) {
userRolesCache.put(userAddress, userRoles);
}
}
// 计算用户的综合权限;
if (userRoles != null) {
String[] roles = userRoles.getRoles();
RolePrivileges privilege = null;
for (String role : roles) {
// 先从缓存读取,如果没有再从原始数据源进行加载;
privilege = rolesPrivilegeCache.get(role);
if (privilege == null) {
privilege = rolePrivilegeSettings.getRolePrivilege(role);
if (privilege == null) {
// 略过不存在的无效角色;
continue;
}
rolesPrivilegeCache.put(role, privilege);
}
privilegesList.add(privilege);
}
}
// 如果用户未被授权任何角色,则采用默认角色的权限;
if (privilegesList.size() == 0) {
RolePrivileges privilege = getDefaultRolePrivilege();
privilegesList.add(privilege);
}
if (userRoles == null) {
userPrivileges = new UserRolesPrivileges(userAddress, RolesPolicy.UNION, privilegesList);
} else {
userPrivileges = new UserRolesPrivileges(userAddress, userRoles.getPolicy(), privilegesList);
}
userPrivilegesCache.put(userAddress, userPrivileges);
return userPrivileges;
}
接下来一个隐藏的类,我们先来看看他的方法头部
我们可以看到它调用了SecurityPolicy的接口,这个是终端用户的权限列表,因此是不对使用者公开的。
private class UserRolesSecurityPolicy implements SecurityPolicy
和上述差不多:
这些都是下面用的隐藏对象;
rolePrivilegeSettings是对于角色权限的设置对象;
userPrivilegeSettings这个是用户权限的设置对象;
接下来是三个是用户权限的配置;
最后两个是用户账户查询;
这个方法是让this获取当前对象的当前状态的值。
public UserRolesSecurityPolicy(Map<Bytes, UserRolesPrivileges> endpointPrivilegeMap,
Map<Bytes, UserRolesPrivileges> nodePrivilegeMap, ParticipantCollection participantsQuery,
UserAccountSet userAccountsQuery) {
this.endpointPrivilegeMap = endpointPrivilegeMap;
this.nodePrivilegeMap = nodePrivilegeMap;
this.participantsQuery = participantsQuery;
this.userAccountsQuery = userAccountsQuery;
}
这是一个boolean的方法,返回值是true或者false,这个方法主要是用于判断endpoint的是否具有它请求的权限。
分为有一个符合和全部符合的返回值,如果处于中间值则抛出错误。
public boolean isEndpointEnable(LedgerPermission permission, MultiIDsPolicy midPolicy) {
if (MultiIDsPolicy.AT_LEAST_ONE == midPolicy) {
// 至少一个;
for (UserRolesPrivileges p : endpointPrivilegeMap.values()) {
if (p.getLedgerPrivilegesBitset().isEnable(permission)) {
return true;
}
}
return false;
} else if (MultiIDsPolicy.ALL == midPolicy) {
// 全部;
for (UserRolesPrivileges p : endpointPrivilegeMap.values()) {
if (!p.getLedgerPrivilegesBitset().isEnable(permission)) {
return false;
}
}
return true;
} else {
throw new IllegalArgumentException("Unsupported MultiIdsPolicy[" + midPolicy + "]!");
}
}
在这个方法之后还有验证是事务性的方法,以及账本权限,但是代码和上述的执行过程以及结构大同小异,不过多赘述,我们现在来看看检查许可的方法,因为和上述方法一样有三个结构相同且类似的方法,我们就只拿其中一个举例。
这个方法是用于检查终点的许可的,输入是事务许可及multipolicy就可以检查对应的终点许可。
public void checkEndpointPermission(TransactionPermission permission, MultiIDsPolicy midPolicy)
throws LedgerSecurityException {
if (!isEndpointEnable(permission, midPolicy)) {
throw new LedgerSecurityException(String.format(
"The security policy [Permission=%s, Policy=%s] for endpoints rejected the current operation!",
permission, midPolicy));
}
}
这个方法是用来确认终点是否有效,传入multipolicy的参数,方法会对其进行一个和全部的验证,同样的检查事务性是否有效的方法和这个也是大同小异。
public boolean isEndpointValid(MultiIDsPolicy midPolicy) {
if (MultiIDsPolicy.AT_LEAST_ONE == midPolicy) {
// 至少一个;
for (Bytes address : getEndpoints()) {
if (userAccountsQuery.contains(address)) {
return true;
}
}
return false;
} else if (MultiIDsPolicy.ALL == midPolicy) {
// 全部;
for (Bytes address : getEndpoints()) {
if (!userAccountsQuery.contains(address)) {
return false;
}
}
return true;
} else {
throw new IllegalArgumentException("Unsupported MultiIdsPolicy[" + midPolicy + "]!");
}
}
这个是检查终点的许可域。
public void checkEndpointValidity(MultiIDsPolicy midPolicy) throws LedgerSecurityException {
if (MultiIDsPolicy.AT_LEAST_ONE == midPolicy) {
// 至少一个;
for (Bytes address : getEndpoints()) {
if (userAccountsQuery.contains(address)) {
return;
}
}
throw new UserDoesNotExistException("All endpoint signers were not registered!");
} else if (MultiIDsPolicy.ALL == midPolicy) {
// 全部;
for (Bytes address : getEndpoints()) {
if (!userAccountsQuery.contains(address)) {
throw new UserDoesNotExistException("The endpoint signer[" + address + "] was not registered!");
}
}
return;
} else {
throw new IllegalArgumentException("Unsupported MultiIdsPolicy[" + midPolicy + "]!");
}
}