How Youzan Re‑engineered Its Permission System for Scalable Access Control
This article examines the evolution of Youzan's permission management—from the original SAM system using a 64‑bit RBAC model to the flexible 2.0 architecture with rig, dynamic menu, and widget delegation—highlighting design choices, implementation details, challenges solved, and measurable business impact.
Background
Permission management is essential for medium‑to‑large B2B systems to control access and prevent data leaks. Traditional static roles (owner, manager, cashier) were insufficient, so a fine‑grained, merchant‑defined employee permission model was introduced.
Permission Concepts and Models
Key Definitions
Permission : The smallest unit of user‑operable behavior.
User : An entity with a unique identifier, assigned one or more roles.
Role : A collection of permissions.
Permission Management : Controls which permissions a user can access.
Model Selection
ACL (Access Control List) : Directly assigns permissions to users; changes require updating each user’s list.
RBAC (Role‑Based Access Control) : Associates permissions with roles; changing a user’s role updates their permissions.
ABAC (Attribute‑Based Access Control) : Evaluates user, environment, operation, and object attributes against policies.
Considering flexibility, maintenance difficulty, and performance, the RBAC model was chosen.
Permission System 1.0 (SAM)
SAM used RBAC and encoded all permission results into a 64‑bit long value. Clients performed bitwise operations to check access.
{
"menuId": 113101101,
"menuName": "网店查看",
"mapBizPerms": {"retail": [0, 0, 1125899906842624]}
}
// Employee personal capabilities
{
"retail": [4611686018427387903, -144115188075855873, -3]
}Problems with SAM:
Configuration difficulty : Adding a permission required coordination among business, backend, and front‑end teams.
Debugging difficulty : Permission calculations on the client side made root‑cause analysis hard.
Extension difficulty : The system could not easily support multiple platforms (PC, mobile app, mini‑program, desktop) or dynamic UI components.
Opportunities and Challenges
Make the permission language human‑readable so business users can define permissions without deep technical knowledge.
Shift configuration from a developer tool to an operational platform, reducing backend dependency.
Provide richer mobile capabilities, including permission parsing and view rendering, with offline support.
Permission System 2.0
The new system builds on SAM and adds a permission expression layer (rig) plus a configuration platform that lets business, product, or operations teams define permissions, menus, and roles without backend involvement.
rig system : Friendly permission expression layer for business users.
Permission Configuration Platform : Supports shop‑type‑specific permissions and multi‑platform menu configurations.
dynamicMenu : Enables mobile components to be added, edited, and permission‑checked dynamically, supporting offline scenarios.
Permission Backend (rig‑core)
Provides services for menus, APIs, roles, and users. Key endpoints include:
Menu service – fetches menus for rendering.
API service – validates API calls against permission rules.
Role service – creates, updates, deletes, and synchronizes roles.
User service – manages user CRUD operations.
Permission Middle‑Platform (rig‑front)
Acts as the middle layer interfacing with business systems. It offers API validation, menu rendering, role and permission management, and data‑level permission checks. Extensibility is achieved via a SPI engine that allows custom business rule plugins.
Mobile Architecture
JS Engine : Cross‑platform computation engine handling component parsing, permission evaluation, shop‑ability resolution, and extension logic.
WidgetManager : Manages widget lifecycle, routing, and pre‑processing.
UI Component Layer : Basic UI widgets such as forms, grids, and buttons.
UI Presentation Layer : Renders components based on configuration data.
Workbench Implementation Example
The workbench uses the JS engine to filter raw data into UI models. menuItemType maps to WidgetType and menuItemKey maps to widgetId. Widgets are instantiated via routing.
<pre><code>@Nav("dynamicMenuWidget/UrgentNotice")
class UrgentNoticeWidgetFragment : BaseRetailWidgetFragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.home_widget_urgent_notice, container, false)
}
// Data callbacks
override fun loadSuccess(data: UrgentNoticeResponse?) {}
override fun loadError(t: Throwable) {}
// Widget click handling
override fun onWidgetClicked() {}
}
</code></pre>In Android, fragments serve as widget containers and can be obtained via the navigator:
val fragment = Navigator.newInstallOrNull<BaseWidgetFragment>("dynamicMenuWidget/UrgentNotice")A WidgetDelegate manages multiple widgets on a page, handling UI setup and refresh:
<pre><code>class WidgetDelegate {
val widgetMap: HashMap<String, BaseWidgetFragment>
val activity: Activity
var rootWidgetId: String = ""
val children = ArrayList<WidgetDelegate>()
var parentDelegate: WidgetDelegate? = null
constructor(activity: Activity, rootWidgetId: String, parentWidgetDelegate: WidgetDelegate? = null) {}
constructor(fragment: BaseWidgetFragment) {}
/** Load widget */
fun setupUI(fragmentManager: FragmentManager, @IdRes containerId: Int, beforeAction: (List<WidgetInfo>) -> List<WidgetInfo> = { this }) {}
/** Refresh widget */
fun reload() {}
/** Find widget by ID */
fun findWidgetById(widgetId: String): BaseWidgetFragment? = null
}
</code></pre>Global event listeners allow custom handling of widget clicks, e.g., for inventory search:
DynamicMenuGlabalListenerManager.addOnWidgetClickedListener("widget_finc_stock_search") { context, widgetInfo ->
service.query { response ->
if (response.success) {
startActivity()
} else {
showToast(response.msg)
}
}
}Advantages
Decouples component testing from permission logic, reducing test scope.
Strict permission checks in the JS engine eliminate data over‑exposure risks.
Supports version control and on‑demand component publishing.
Ensures consistent content across diverse mobile devices.
Value Review
Permission requests no longer depend on backend resources , shortening the development cycle.
Debugging efficiency improved by 100% , as front‑end teams can isolate issues without backend involvement.
Online incidents reduced dramatically , with a 76% month‑over‑month drop in H2 2020.
Conclusion
The redesigned permission solution meets current merchant requirements and provides a future‑proof framework adaptable to evolving store models and business scenarios.
Youzan Coder
Official Youzan tech channel, delivering technical insights and occasional daily updates from the Youzan tech team.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
