Ownable源码源码概览功能说明onlyOwner修饰器控制谁能调用敏感函数transferOwnership将权限转让给别人renounceOwnership放弃控制权,实现去中心化OwnableInvalidOwner/Unau
功能 | 说明 |
---|---|
onlyOwner 修饰器 |
控制谁能调用敏感函数 |
transferOwnership |
将权限转让给别人 |
renounceOwnership |
放弃控制权,实现去中心化 |
OwnableInvalidOwner/UnauthorizedAccount |
更清晰错误信息与 gas 优化 |
构造传参方式 | 支持更灵活的部署方式(如代理部署) |
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* Ownable 是一个抽象合约,不能直接部署
*/
abstract contract Ownable is Context {
// _owner 私有变量存储合约当前的拥有者地址
address private _owner;
// 自定义错误:非 owner 账户调用了仅限 owner 的方法
error OwnableUnauthorizedAccount(address account);
//自定义错误:表示 newOwner 是无效地址(如 `address(0)`)
error OwnableInvalidOwner(address owner);
// 记录 ownership 转移历史,对前端/链上分析友好
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* 设置初始owner:在构造函数中传入初始 owner,不再自动使用 `msg.sender`
* 可以支持更灵活的部署模式(如代理合约部署时设定 owner)
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* onlyOwner 修饰器
* 为函数添加访问权限:仅限 owner 执行
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* 返回当前合约 owner
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* 用于 `onlyOwner` 检查当前调用者是否为 owner
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* 放弃所有权
* 1. 当前 owner 可以主动放弃权限,将 owner 设置为 address(0)
* 2.常用于让合约去中心化(不可控)后彻底解绑 owner 权限
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* 转让所有权
* 1. 只有当前owner可以调用
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
// 防止转给无效地址
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* 执行转移并触发事件
* 1. 支持继承时重写
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
特性 | 说明 |
---|---|
灵活的角色系统 | 任意角色都可以被定义与管理,支持多层级权限结构。 |
模块化设计 | _grantRole / _revokeRole / _setRoleAdmin 提供灵活可扩展的逻辑。 |
安全性考虑 | renounceRole 设计可防止误调用或攻击者冒充操作。 |
事件追踪 | 所有角色变更都会记录事件,便于链下索引与审计。 |
接口兼容性 | 通过 ERC165 提供合约接口发现能力。 |
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (access/AccessControl.sol)
pragma solidity ^0.8.20;
import {IAccessControl} from "./IAccessControl.sol";
import {Context} from "../utils/Context.sol";
import {ERC165} from "../utils/introspection/ERC165.sol";
/**
* AccessControl 是 OpenZeppelin 提供的一种 "基于角色的权限控制系统"
* 支持为任意角色定义管理员角色,可以非常灵活地为不同功能设置不同权限
*/
abstract contract AccessControl is Context, IAccessControl, ERC165 {
/**
* RoleData:表示一个角色的数据结构。
* hasRole: 记录哪些地址拥有该角色。
* adminRole: 管理该角色的管理员角色
*/
struct RoleData {
mapping(address account => bool) hasRole;
bytes32 adminRole;
}
// 主状态变量,存储所有角色及其状态
mapping(bytes32 role => RoleData) private _roles;
// 默认的管理员角色,管理其他所有角色,自己也是自己的管理员
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* 函数修饰器
* 所有需要角色权限的函数使用的修饰器
* 调用 `_checkRole` 来判断调用者是否具有该角色
*/
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
/**
* 支持 ERC165 接口,用于合约间兼容性检查
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
/**
* 角色判断函数
*/
function hasRole(bytes32 role, address account) public view virtual returns (bool) {
return _roles[role].hasRole[account];
}
/**
* 检查当前合约调用者是否拥有role
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* 检查某个 account 是否拥有 role
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert AccessControlUnauthorizedAccount(account, role);
}
}
/**
* 获取角色的管理员角色(谁能授予和撤销它)
*/
function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
return _roles[role].adminRole;
}
/**
* 授予角色
* 1. 只能由 role 的管理员调用
* 2. 底层使用 `_grantRole`
*/
function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* 撤销角色
*/
function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* 自我放弃角色
*/
function renounceRole(bytes32 role, address callerConfirmation) public virtual {
if (callerConfirmation != _msgSender()) {
revert AccessControlBadConfirmation();
}
_revokeRole(role, callerConfirmation);
}
/**
* 设置角色管理员
* 可改变角色的管理员角色(例如多层管理)
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = getRoleAdmin(role);
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* 授予角色
* 如果账号尚未拥有该角色,写入状态,并发出 RoleGranted 事件
*/
function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
if (!hasRole(role, account)) {
_roles[role].hasRole[account] = true;
emit RoleGranted(role, account, _msgSender());
return true;
} else {
return false;
}
}
/**
* 撤销角色(与_grandRole 对称)
*/
function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
if (hasRole(role, account)) {
_roles[role].hasRole[account] = false;
emit RoleRevoked(role, account, _msgSender());
return true;
} else {
return false;
}
}
}
疑问:在AccessControl 源码通篇讲到了角色的授予、撤销等,并没有讲到角色的创建、删除?这些逻辑又是如何处理的!
在 AccessControl
的设计中,其实并没有明确的 “创建角色” 的函数。角色的创建是隐式的,也就是说:
只要某个角色的
bytes32
被使用了,它就被认为是“存在”的角色。
具体表现如下:
调用 grantRole(role, account)
之前:
_roles[role]
不存在(mapping
的默认值),但不会报错
调用 grantRole(role, account)
时:
getRoleAdmin(role)
会读取默认的 adminRole
(为0)。_grantRole()
会为该角色映射 hasRole[account] = true
。📌 也就是说:
bytes32 public constant EDITOR_ROLE = keccak256("EDITOR_ROLE");
grantRole(EDITOR_ROLE, someUser); // 这就是“创建” + “赋予”
OpenZeppelin 的 AccessControl
并没有提供“删除角色” 的功能。原因主要有两个:
mapping
无法删除 key,只能“重置值”例如:
_roles[role].hasRole[account] = false; // 只是撤销,不是删除整个角色
role
的 onlyRole(role)
修饰器,它就不会产生任何影响;追问:从未出现过的角色,第一次调用 grantRole(role, account)
时,这个调用者是否默认就是该角色的“管理员”?
答:不是 任何人都不能直接授予一个新角色,除非他已经拥有该角色的“管理员角色”。
默认情况下,新角色的管理员是 DEFAULT_ADMIN_ROLE
(即 0x00),只有拥有该 DEFAULT_ADMIN_ROLE
的账户,才有权限调用 grantRole
追问:所有角色默认管理员角色都是 DEFAULT_ADMIN_ROLE
。 _setRoleAdmin(role, newAdminRole)
是 internal
方法,是否任何人都能设置,会不会造成混乱?
_setRoleAdmin() 是 internal
方法
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual
追问:从上面的内容分析看,角色是没有层级结构的,为什么说可以设计多级角色体系?
表面上看,OpenZeppelin 的 AccessControl
的确没有强制的角色层级结构:角色本身是平级的
,但其实它是支持通过 “角色的管理员角色”机制 来手动构建“软层级”结构的。
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
bytes32 public constant SUPER_ADMIN = keccak256("SUPER_ADMIN");
bytes32 public constant ADMIN = keccak256("ADMIN");
bytes32 public constant WRITER = keccak256("WRITER");
bytes32 public constant READER = keccak256("READER");
// 你可以这样初始化
constructor() {
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(SUPER_ADMIN, msg.sender);
// 构建“软层级”结构
_setRoleAdmin(SUPER_ADMIN, DEFAULT_ADMIN_ROLE); // msg.sender 可管 SUPER_ADMIN
_setRoleAdmin(ADMIN, SUPER_ADMIN); // SUPER_ADMIN 可管 ADMIN
_setRoleAdmin(WRITER, ADMIN); // ADMIN 可管 WRITER
_setRoleAdmin(READER, ADMIN); // ADMIN 可管 READER
// 如此:就构建出了一个:四层的权限结构树
}
系统设计方式 | 是否强制层级 | 是否支持多级 | 授权是否自动传递 | 管理方式 |
---|---|---|---|---|
AccessControl |
❌ 否 | ✅ 可构建 | ❌ 不自动继承权限 | 手动设置 _setRoleAdmin |
RBAC(比如 RBAC2) | ✅ 有角色层级 | ✅ 有继承 | ✅ 权限自动继承 | 声明式结构 |
现象 | 解答 |
---|---|
所有角色是否是平级? | ✅ 是的,默认平级,没有强约束的层级系统 |
能不能构建层级结构? | ✅ 可以!通过 _setRoleAdmin 手动构建“管理层级” |
角色权限是否自动继承? | ❌ 不会,需要你自己写逻辑来授权控制 |
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!