CSS in JS是一种解决css问题想法的集合,而不是一个指定的库。从CSS in JS的字面意思可以看出,它是将css样式写在JavaScript文件中,而不需要独立出.css、.less之类的文件。将css放在js中使我们更方便的使用js的变量、模块化、tree-shaking。还解决了css中的一些问题,譬如:更方便解决基于状态的样式,更容易追溯依赖关系,生成唯一的选择器来锁定作用域。尽管CSS in JS不是一个很新的技术,但国内的普及程度并不高。由于Vue和Angular都有属于他们自己的一套定义样式的方案,React本身也没有管用户怎样定义组件的样式[1],所以CSS in JS在React社区的热度比较高。
目前为止实现CSS in JS的第三方库有很多:(http://michelebertoli.github.io/css-in-js/)。像JSS[2]、styled-components[3]等。在这里我们就不展开赘述了(相关链接已放在下方),这篇文章的重点是JS in CSS。
在上面我们提到CSS in JS就是把CSS写在JavaScript中,那么JS in CSS我们可以推断出就是可以在CSS中使用JavaScript脚本,如下所示。可以在CSS中编写Paint API的功能。还可以访问:ctx,geom。甚至我们还可以编写自己的css自定义属性等。这些功能的实现都基于CSS Houdini[4]。
.el {
--color: cyan;
--multiplier: 0.24;
--pad: 30;
--slant: 20;
--background-canvas: (ctx, geom) => {
let multiplier = var(--multiplier);
let c = `var(--color)`;
let pad = var(--pad);
let slant = var(--slant);
ctx.moveTo(0, 0);
ctx.lineTo(pad + (geom.width - slant - pad) * multiplier, 0);
ctx.lineTo(pad + (geom.width - slant - pad) * multiplier + slant, geom.height);
ctx.lineTo(0, geom.height);
ctx.fillStyle = c;
ctx.fill();
};
background: paint(background-canvas);
transition: --multiplier .4s;
}
.el:hover {
--multiplier: 1;
}
在如今的Web开发中,JavaScript几乎占据了项目代码的大部分。我们可以在项目开发中使用ES 2020、ES2021、甚至提案中的新特性(如:Decorator[5]),即使浏览器尚未支持,也可以编写Polyfill或使用Babel之类的工具进行转译,让我们可以将最新的特性应用到生产环境中(如下图所示)。
JavaScript标准制定流程.png
而CSS就不同了,除了制定CSS标准规范所需的时间外,各家浏览器的版本、实战进度差异更是旷日持久(如下图所示),最多利用PostCSS、Sass等工具來帮我们转译出浏览器能接受的CSS。开发者们能操作的就是通过JS去控制DOM与CSSOM来影响页面的变化,但是对于接下來的Layout、Paint与Composite就几乎没有控制权了。为了解决上述问题,为了让CSS的魔力不在受到浏览器的限制,Houdini就此诞生。
CSS 标准制定流程.png
我们上文中提到JavaScript中进入提案中的特性我们可以编写Polyfill,只需要很短的时间就可以讲新特性投入到生产环境中。这时,脑海中闪现出的第一个想法就是CSS Polyfill,只要CSS的Polyfill 足够强大,CSS或许也能有JavaScript一样的发展速度,令人可悲的是编写CSS Polyfill异常的困难,并且大多数情况下无法在不破坏性能的情况下进行。这是因为JavaScript是一门动态脚本语言[6]。它带来了极强的扩展性,正是因为这样,我们可以很轻松使用JavaScript做出JavaScript的Polyfill。但是CSS不是动态的,在某些场景下,我们可以在编译时将一种形式的CSS的转换成另一种(如PostCSS[7])。如果你的Polyfill依赖于DOM结构或者某一个元素的布局、定位等,那么我们的Polyfill就无法编译时执行,而需要在浏览器中运行了。不幸的是,在浏览器中实现这种方案非常不容易。
页面渲染流程.png
如上图所示,是从浏览器获取到HTML到渲染在屏幕上的全过程,我们可以看到只有带颜色(粉色、蓝色)的部分是JavaScript可以控制的环节。首先我们根本无法控制浏览器解析HTML与CSS并将其转化为DOM与CSSOM的过程,以及Cascade,Layout,Paint,Composite我们也无能为力。整个过程中我们唯一完全可控制的就是DOM,另外CSSOM部分可控。
CSS Houdini草案中提到,这种程度的暴露是不确定的、兼容性不稳定的以及缺乏对关键特性的支持的。比如,在浏览器中的 CSSOM 是不会告诉我们它是如何处理跨域的样式表,而且对于浏览器无法解析的 CSS 语句它的处理方式就是不解析了,也就是说——如果我们要用 CSS polyfill让浏览器去支持它尚且不支持的属性,那就不能在 CSSOM 这个环节做,我们只能遍历一遍DOM,找到 <style> 或 <link rel="stylesheet"> 标签,获取其中的 CSS 样式、解析、重写,最后再加回 DOM 树中。令人尴尬的是,这样DOM树全部刷新了,会导致页面的重新渲染(如下如所示)。
即便如此,有的人可能会说:“除了这种方法,我们也别无选择,更何况对网站的性能也不会造成很大的影响”。那么对于部分网站是这样的。但如果我们的Polyfill是需要对可交互的页面呢?例如scroll,resize,mousemove,keyup等等,这些事件随时会被触发,那么意味着随时都会导致页面的重新渲染,交互不会像原本那样丝滑,甚至导致页面崩溃,对用户的体验也极其不好。
综上所述,如果我们想让浏览器解析它不认识的样式(低版本浏览器使用grid布局),然而渲染流程我们无法介入,我们也只能通过手动更新DOM的方式,这样会带来很多问题,Houdini的出现正是致力于解决他们。
Houdini是一组底层API,它公开了CSS引擎的各个部分,如下图所示展示了每个环节对应的新API(灰色部分各大浏览器还未实现),从而使开发人员能够通过加入浏览器渲染引擎的样式和布局过程来扩展CSS。Houdini是一群来自Mozilla,Apple,Opera,Microsoft,HP,Intel和Google的工程师组成的工作小组设计而成的。它们使开发者可以直接访问CSS对象模型(CSSOM),使开发人员可以编写浏览器可以解析为CSS的代码,从而创建新的CSS功能,而无需等待它们在浏览器中本地实现。
CSS Houdini-API
尽管当前已经有了CSS变量,可以让开发者控制属性值,但是无法约束类型或者更严格的定义,CSS Houdini新的API,我们可以扩展css的变量,我们可以定义CSS变量的类型,初始值,继承。它是css变量更强大灵活。
CSS变量现状:
.dom {
--my-color: green;
--my-color: url('not-a-color'); // 它并不知道当前的变量类型
color: var(--my-color);
}
Houdini提供了两种自定义属性的注册方式,分别是在js和css中。
CSS.registerProperty({
name: '--my-prop', // String 自定义属性名
syntax: '<color>', // String 如何去解析当前的属性,即属性类型,默认 *
inherits: false, // Boolean 如果是true,子节点将会继承
initialValue: '#c0ffee', // String 属性点初始值
});
我们还可以在css中注册,也可以达到上面的效果
@property --my-prop {
syntax: '<color>';
inherits: false;
initial-value: #c0ffee;
}
这个API中最令人振奋人心的功能是自定义属性上添加动画,像这样:transition: --multiplier 0.4s;,这个功能我们在前面介绍什么是js in css那个demo[8]用使用过。我们还可以使用+使syntax属性支持一个或多个类型,也可以使用|来分割。更多syntax属性值:
属性值描述<length>长度值<number>数字<percentage>百分比<length-percentage>长度或百分比,calc将长度和百分比组成的表达式<color>颜色<image>图像<url>网址<integer>整数<angle>角度<time>时间<resolution>分辨率<transform-list>转换函数<custom-ident>ident
Worklets是渲染引擎的扩展,从概念上来讲它类似于Web Workers[9],但有几个重要的区别:
Worklet是一个JavaScript模块,通过调用worklet的addModule方法(它是个Promise)来添加。比如registerLayout,registerPaint, registerAnimator 我们都需要放在Worklet中
//加载单个
await demoWorklet.addModule('path/to/script.js');
// 一次性加载多个worklet
Promise.all([
demoWorklet1.addModule('script1.js'),
demoWorklet2.addModule('script2.js'),
]).then(results => {});
registerDemoWorklet('name', class {
// 每个Worklet可以定义要使用的不同函数
// 他们将由渲染引擎在需要时调用
process(arg) {
return !arg;
}
});
Worklets的生命周期
Worklets lifecycle
Typed OM是对现有的CSSOM的扩展,并实现 Parsing API 和 Properties & Values API相关的特性。它将css值转化为有意义类型的JavaScript的对象,而不是像现在的字符串。如果我们尝试将字符串类型的值转化为有意义的类型并返回可能会有很大的性能开销,因此这个API可以让我们更高效的使用CSS的值。
现在读取CSS值增加了新的基类CSSStyleValue,他有许多的子类可以更加精准的描述css值的类型:
子类描述CSSKeywordValueCSS关键字和其他标识符(如inherit或grid)CSSPositionValue位置信息 (x,y)CSSImageValue表示图像的值属性的对象CSSUnitValue表示为具有单个单位的单个值(例如50px),也可以表示为没有单位的单个值或百分比CSSMathValue比较复杂的数值,比如有calc,min和max。这包括子类 CSSMathSum, CSSMathProduct, CSSMathMin,CSSMathMax, CSSMathNegate 和 CSSMathInvertCSSTransformValue由CSS transforms组成的CSSTransformComponent列表,其中包括CSSTranslate, CSSRotate, CSSScale, CSSSkew, CSSSkewX, CSSSkewY, CSSPerspective 和 CSSMatrixComponent
使用Typed OM主要有两种方法:
使用attributeStyleMap设置并获取
myElement.attributeStyleMap.set('font-size', CSS.em(2));
myElement.attributeStyleMap.get('font-size'); // CSSUnitValue { value: 2, unit: 'em' }
myElement.attributeStyleMap.set('opacity', CSS.number(.5));
myElement.attributeStyleMap.get('opacity'); // CSSUnitValue { value: 0.5, unit: 'number' };
在线demo[10]
使用computedStyleMap
.foo {
transform: translateX(1em) rotate(50deg) skewX(10deg);
vertical-align: baseline;
width: calc(100% - 3em);
}
const cs = document.querySelector('.foo').computedStyleMap();
cs.get('vertical-align');
// CSSKeywordValue {
// value: 'baseline',
// }
cs.get('width');
// CSSMathSum {
// operator: 'sum',
// length: 2,
// values: CSSNumericArray {
// 0: CSSUnitValue { value: -90, unit: 'px' },
// 1: CSSUnitValue { value: 100, unit: 'percent' },
// },
// }
cs.get('transform');
// CSSTransformValue {
// is2d: true,
// length: 3,
// 0: CSSTranslate {
// is2d: true,
// x: CSSUnitValue { value: 20, unit: 'px' },
// y: CSSUnitValue { value: 0, unit: 'px' },
// z: CSSUnitValue { value: 0, unit: 'px' },
// },
// 1: CSSRotate {...},
// 2: CSSSkewX {...},
// }
开发者可以通过这个API实现自己的布局算法,我们可以像原生css一样使用我们自定义的布局(像display:flex, display:table)。在Masonry layout library[11] 上我们可以看到开发者们是有多想实现各种各样的复杂布局,其中一些布局光靠 CSS 是不行的。虽然这些布局会让人耳目一新印象深刻,但是它们的页面性能往往都很差,在一些低端设备上性能问题犹为明显。
CSS Layout API 暴露了一个registerLayout方法给开发者,接收一个布局名(layout name)作为后面在 CSS中使用的属性值,还有一个包含有这个布局逻辑的JavaScript类。
my-div {
display: layout(my-layout);
}
// layout-worklet.js
registerLayout('my-layout', class {
static get inputProperties() { return ['--foo']; }
static get childrenInputProperties() { return ['--bar']; }
async intrinsicSizes(children, edges, styleMap) {}
async layout(children, edges, constraints, styleMap) {}
});
await CSS.layoutWorklet.addModule('layout-worklet.js');
目前浏览器大部分还不支持
我们可以在CSS background-image中使用它,我们可以使用Canvas 2d上下文,根据元素的大小控制图像,还可以使用自定义属性。
await CSS.paintWorklet.addModule('paint-worklet.js');
registerPaint('sample-paint', class {
static get inputProperties() { return ['--foo']; }
static get inputArguments() { return ['<color>']; }
static get contextOptions() { return {alpha: true}; }
paint(ctx, size, props, args) { }
});
这个API让我们可以控制基于用户输入的关键帧动画,并且以非阻塞的方式。还能更改一个 DOM 元素的属性,不过是不会引起渲染引擎重新计算布局或者样式的属性,比如 transform、opacity 或者滚动条位置(scroll offset)。Animation API的使用方式与 Paint API 和Layout API略有不同我们还需要通过new一个WorkletAnimation来注册worklet。
// animation-worklet.js
registerAnimator('sample-animator', class {
constructor(options) {
}
animate(currentTime, effect) {
effect.localTime = currentTime;
}
});
await CSS.animationWorklet.addModule('animation-worklet.js');
// 需要添加动画的元素
const elem = document.querySelector('#my-elem');
const scrollSource = document.scrollingElement;
const timeRange = 1000;
const scrollTimeline = new ScrollTimeline({
scrollSource,
timeRange,
});
const effectKeyframes = new KeyframeEffect(
elem,
// 动画需要绑定的关键帧
[
{transform: 'scale(1)'},
{transform: 'scale(.25)'},
{transform: 'scale(1)'}
],
{
duration: timeRange,
},
);
new WorkletAnimation(
'sample-animator',
effectKeyframes,
scrollTimeline,
{},
).play();
关于此API的更多内容:(https://github.com/w3c/css-houdini-drafts/tree/main/css-animation-worklet-1)
允许开发者自由扩展 CSS 词法分析器。
解析规则:
const background = window.cssParse.rule("background: green");
console.log(background.styleMap.get("background").value) // "green"
const styles = window.cssParse.ruleSet(".foo { background: green; margin: 5px; }");
console.log(styles.length) // 5
console.log(styles[0].styleMap.get("margin-top").value) // 5
console.log(styles[0].styleMap.get("margin-top").type) // "px"
解析CSS:
const style = fetch("style.css")
.then(response => CSS.parseStylesheet(response.body));
style.then(console.log);
它将提供一些方法来测量在屏幕上呈现的文本元素的尺寸,将允许开发者控制文本元素在屏幕上呈现的方式。使用当前功能很难或无法测量这些值,因此该API将使开发者可以更轻松地创建与文本和字体相关的CSS特性。例如:
Is Houdini ready yet
(https://ishoudinireadyyet.com/)
了解到这里,部分开发者可能会说:“我不需要这些花里胡哨的技术,并不能带收益。我只想简简单单的写几个页面,做做普通的Web App,并不想试图干预浏览器的渲染过程从而实现一些实验性或炫酷的功能。”如果这样想的话,我们不妨退一步再去思考。回忆下最近做过的项目,用于实现页面效果所使用到的技术,grid布局方式在考虑兼容老版本浏览器时也不得不放弃。我们想控制浏览器渲染页面的过程并不是仅仅为了炫技,更多的是为了帮助开发者们解决以下两个问题:
几年过后再回眸,当主流浏览器完全支持Houdini的时候。我们可以在浏览器上随心所欲的使用任何CSS属性,并且他们都能完美支持。像今天的grid布局在旧版本浏览器支持的并不友好的这类问题,那时我们只需要安装对应的Polyfill就能解决类似的问题。
者:HXGNMSL来源:CSDN原文:https://blog.csdn.net/HXGNMSL/article/details/89076476
Javascript的历史来源
94年网景公司 研发出世界上第一款浏览器。
95年 sun公司 java语言诞生
网景公司和sun合作。
Javascript ===> javascript
JavaScript和ECMAScript的关系
简单来说ECMAScript不是一门语言,而是一个标准。符合这个标准的比较常见的有:JavaScript、Action Script(Flash中用的语言)
JavaScript的基本结构:
JavaScript的语法:
JavaScript的基础语法
变量的声明及使用
数据类型
运算符
逻辑控制语句
注释
语法规则
变量的声明语法:
var变量名;
例如:
Var num;
然后进行赋值:num = 10;也可以声明时直接赋值:
Var num =10;
在JavaScript中,提供了常用的基本数据类型:
undefined 未定义;
null 空;
string 字符串类型;
boolean 布尔类型;
number 数值类型;
运算符:
算数运算符:+、-、*、/、%、++、–;
比较运算符:>、<、> =、< =、==、!=;
逻辑运算符:&&、||、!;
赋值运算符:=;
逻辑控制语句:
JavaScript的逻辑控制语句也分为两类:条件结构和循环机构。
条件结构
条件机构分为if结构和switch结构:
If…else
Switch
循环结构
JavaScript的循环结构的执行顺序与Java类似,主要包括以下几种结构:
for循环
while循环
do…while循环
for…inx循环
示例:
for(var i=0;i<10;i++){
Document.write(“*”);
}
输出结果:**********
循环中断:
用于循环中断的语句有以下两种:
break.
continue.
与Java用法一样,break是跳出循环,continue是跳入下一次循环。
函数
函数有两种:一种是系统函数,一种是自定义函数。
常用的系统函数包括:
parseInt():转换为整数。
parseFloat():转换为浮点型。
isNaN():判断非数字。
Eval():计算表达式值。
自定义函数:
自定义函数的语法
function 函数名(参数1,参数2,…){
…//语句
Return 返回值;//可选
}
函数的调用:
函数的调用方式有以下两种
事件名=函数名(传递的实参值),例如:
“函数名()”
直接使用函数名(传递的实参值),例如:
var recult = add(2,3);
匿名函数
匿名函数的语法
var sumFun=function(num1,num2){
…
return(nun1,num2);
} ;
在语法中:
var sunFun=function(num1,num2)表示声明一个变量等于某个函数体。
{…};是把整个函数体放在变量的后面,并把末尾添加一个分号。
匿名函数的调用:
由于匿名函数定义的整个语句,可以像赋值一样赋给一个变量进行保存,所以可以使用如下方式调用语法中的匿名函数:
var sum=sumFun(2,3)
BOM概述
使用BOM可以移动窗口,改变状态栏中的文本,执行其他与页面内容不直接相关的动作。它包含的对象主要有以下几种;
Window对象
Window对象是指整个窗口对象,可以通过操作Window对象的属性和方法控制窗口,例如,打开或关闭一个窗口。
History对象
浏览器访问过的历史页面对应History对象,通过History对象的属性和方法实现浏览器的前进或后退的功能。
Location对象
浏览器的地址栏对应Location对象,通过Location对象的属性和方法控制页面跳转。
Document对象
浏览器内的网页内容对应Document对象,通过Document对象的属性和方法,控制页面元素。
Window常用的属性有:
history:有关客户访问过的URL的信息。
location:有关当前URL的信息。
Screen: 有关客户端的屏幕和显示性能的信息。
Window对象常用的方法:
prompt():显示可提示用户输入的对话框。
alert():显示带有一段消息和一个人“确认”按钮的警告框。
confirm():显示带有一段消息以及“确认”按钮“取消”按钮的对话框。
close():关闭浏览器窗口。
open():打开一个新的浏览器窗口,加载给定URL所指定的文档。
setTimeout():用于在指定(以毫秒计)后调用函数或计算表达式。
setTneerval():按照指定的周期 (以毫秒计)数来调用函数或计算表达式。
Window对象常用窗口特征属性
height、width:窗口文档显示区的高度、宽度,以像素计。
left、top:窗口的x坐标y坐标,以像素计。
toolbar:yes|no|1|0:是否显示浏览器的工具栏,默认是yes。
scrollbars =yes|no|1|0:是否显示滚动条,默认是yes。
locationyes|no|1|0:是否显示地址栏,默认是yes。
status|no|1|0:是否添加地址栏,默认是yes。
menubar|no|1|0:是否显示菜单栏,默认是yes。
resizable|no|1|0:窗口是否可调节尺寸,默认是yes。
Window对象的常用事件:
onload:一个页面或一副图像完成加载。
onmouseover:鼠标指针移到某元素之上。
onclick:单击某个对象。
onkeydown:某个键盘按键被按下。
onchange:域的内容被改变。
History对象的方法:
back():加载History对象列表中的上一个URL。
forward():加载History对象列表中的下一个URL。
go():加载History对象列表中的某个具体URL。
Location对象的属性:
host:设置或返回主机名和当前URL的端口号。
hostname:设置或返回当前URL的主机名。
href:设置或返回完整的URL。
Location对象的方法:
reload():重新加载当前文档。
replace():用新的文档替换当前文档。
Document对象常用的属性:
referrer:返回载入当前文档的URL。
URL:返回当前文档的URL。
Document对象的常用方法:
getElementById():返回对拥有指定id的第一个对象的引用。
getElementsByName():返回带有指定名称的对象的集合。
getElementsByTagName():返回带有指定标签名的对象的集合。
write():向文档写文本、HTML表达式代码。
内置对象
系统的内置对象有Date对象、Array对象、String对象和Math对象等。
Date:用于操作日期和时间。
Array:用于在单独的变量名中储存一系列的值。
String:用于支持对字符串的处理。
Math:用于执行数学任务,包含了若干数字常量和函数。
Date对象:
1:创建日期对象
Date对象包含日期和时间两个信息,创建日期对象的基本语法有两种:
创建日期的基本语法1: var 日期实例化=new Date(参数);
创建日期的基本语法2: var 日期实例化=new Date();
Date对象的常用方法:
getDate():从Date对象返回一个月中的某一天,其值介于1到31之间。
getDay():从Date对象返回星期中的某一天,其值介于0到6之间。
getHours():返回Date对象的小时,其值介于0到23之间。
getMinutes():返回Date对象的分钟,其值介于0到59之间。
getSeconds():返回Date对象的秒数,其值介于0到59之间。
getMonth():返回Date对象的月份,其值介于0到11之间。
getFullYear():返回Date对象的年份,其值为4位数。
getTime():返回自某一时刻(2010年1月1日)以来的毫秒数。
DOM概述
什么是DOM
DOM是文档对象的缩写,和语言无关。它提供了访问、动态修改结构文档的接口,W3C制定了DOM规范,主流浏览器都支持。
使用Core DOM操作节点
访问节点:
使用getElement系列方法访问指定节点。
getElementById():返回对拥有指定id的第一个对象的引用。
getElementsByName():返回带有指定名称的对象的集合。
getElementsByTagName():返回带有指定标签名的对象的集合。
使用层次关系访问节点。
parenNode:返回节点的父节点。
firstChild:返回节点的首个节点。文本和属性节点没有父节点,会返回一个空数组,对于元素节点,若是没有子节点会返回null。
lastChild:返回节点的最后一个子节点,返回值同firstChild。
操作节点属性值
CoreDOM的标准方法包括以下两种:
getAttribute(“属性名”):获取属性值。
getAttribute(“属性名”,“属性值”):设置属性值
创建和增加节点:
创建节点
createElement(tagName):按照给定的标签名称创建一个新的元素节点
appendChild(nodeName):向以存在节点列表的末尾添加新的节点。
inserBefore(newNode,oldNode):向指定的节点之前插入一个新的子节点。
cloneNode(deep):复制某个指定的节点。
删除和替换节点
removeChild(node):删除指定的节点。
replaceChild(newNode,oldNode):用其他的节点替换指定的节点。
Table对象的属性和方法
属性:
rows[]:返回包含表格中所有行的一个数组。
rows[]用于返回表格中所有行的一个数组。
方法:
inserRow():在表格中插入一个新行。
deleteRow():从表格中删除一行。
数组
数组是具有相同数据类型的一个或多个值得集合
创建数组的语法:
var 数组名称=new Array(size);
数组的赋值的两种方式:
先声明在赋值
var province = new Array(4);
province[0]=“河北省”;
province[1]=“河南省”;
索引也可以使用标识(字符串),例如:
var province=new Array(4);
province[‘河北省’]=“河北省”;
province[‘河南省’]=“河南省”;
声明时同时初始化
var province=new Array(“河北省”,“河南省”,“湖北省”,“广东省”);
Array对象的常用属性和方法:
属性:
length:设置或返回数组中元素的数目。
方法:
join():把数组的所有元素放入一个字符串,通过一个分隔符进行分割。
sort():对数组的元素进行排序。
在本教程中,我们有一个使用 HTML、CSS 和 JS 制作的待办事项列表应用程序,我们将把它与 Cerbos 集成以向应用程序添加授权。授权确定用户是否可以执行特定操作或访问某些资源或数据。它使组织能够控制和保护对敏感数据库、私人和个人数据以及公司资源的访问。在我们的 JS 应用程序中,授权将定义用户可以执行的操作(创建待办事项并阅读待办事项)以及管理员可以执行的操作(创建、阅读和删除待办事项)。
基于角色的访问控制 (RBAC)是一种访问控制方法,它根据最终用户的组织角色为其分配权限。RBAC 提供细粒度的控制,提供了一种简单、易于管理的访问管理方法,与单独分配权限相比,这种方法不容易出错。
使用 RBAC 的优点是管理授权权限变得更加容易,因为系统管理员可以批量管理用户和权限,而不是逐个管理。
我们已经定义了 RBAC 策略,并将集成 Cerbos 以根据用户身份授权创建、读取和删除待办事项。我们对谁可以做什么的业务要求如下:
Cerbos 是一个开源授权层,可让您轻松实现应用程序的授权。在Cerbos,我们提供细粒度的访问控制来增强安全性,同时使您的应用程序更快、更具可扩展性。使用 Cerbos 进行授权具有使用严格的 JS 代码无法获得的各种优势
当组织采用 RBAC 访问模型时,必须遵守有关安全和管理的最佳实践,并致力于不断改进其访问协议以防范新出现的威胁。
以下列表包含一些您必须遵循的 RBAC 最佳实践:
我们将在示例代码中使用这些角色来演示访问控制如何工作。
使用 Cerbos,访问规则始终面向资源,您编写的策略会映射到系统中的这些资源。资源可以是任何东西,而您建模策略的方式则由您决定 — 您可以通过多种方式实现相同的逻辑结果:以操作为主导、以角色为主导、以属性为主导或以它们的组合为主导。
话虽如此,有些模式更适合特定场景——让我们看看一些不同的方法。考虑这个权限模型:
Actions | Roles | ||||
CPO | CTO | Exec-1 | Exec-2 | Exec-3 | |
View | Allowed | Allowed | Allowed | Allowed | Allowed |
Add | Allowed | Allowed | Allowed | Allowed | Allowed |
Delete | Allowed | Allowed | Not Allowed | Not Allowed | Not Allowed |
我们将描述以行动为主导的政策,因为我们已经为我们的 JS 应用程序实施了以行动为主导的政策。
在这里,我们关注一个动作并列出可以执行该动作的所有角色。为了更好地理解这一点,我们文档中列出了一个示例,如下所示:
# Principals in the following three roles can perform the `run` action
- actions:
- "run"
effect: EFFECT_ALLOW
roles:
- JR_MANAGER
- SR_MANAGER
- CFO
# All principals can perform the `view` action
- actions:
- "view"
effect: EFFECT_ALLOW
roles:
- ["*"]
如果您的系统符合以下任一情况,则此方法可能适用:
构建成功后,您应该会看到网页在浏览器的localhost:5500上加载。
目前,没有任何策略,此应用程序中的 CPO 和 CTO 被授予删除待办事项的权限,而高管(1、2 和 3)只能查看和添加待办事项。但是,由于我们尚未将 Cerbos 与应用程序集成以进行权限管理,因此 CTO 和 CPO 无法删除待办事项。成功集成 Cerbos 后,CTO 和 CPO 将能够删除待办事项。
要将 Cerbos 集成到我们的 JavaScript 应用程序中,我们首先要启动 Cerbos Docker 容器。在根目录 (/to-dolist-cerbos) 中使用以下命令运行 Docker 容器:
docker run --rm --name cerbos -d -v $(pwd)/cerbos/policies:/policies -p 3592:3592 -p 3593:3593 ghcr.io/cerbos/cerbos:0.34.0
Cerbos Playground是一款用于在线创建和测试策略的实用程序。Cerbos Playground 是了解策略创建甚至生成可用策略的绝佳方式:https://play.cerbos.dev/new ?generator 。
如何使用 RBAC 策略生成器为我们的 javascript 应用程序生成策略?
我们有两个角色:用户和管理员(其中 CTO/CPO 是管理员,而高管是用户)。添加操作并根据我们要授予的权限选择复选框。
根据偏好选择后,我们可以点击**生成**按钮,生成一个名为to-dos.yaml的YAML策略文件。
使用生成器生成策略后,只需将其复制并添加到应用程序的文件结构中即可。策略生成器还会生成一个 to-dos_test.yaml 文件,该文件旨在帮助自动测试访问控制策略。确保测试文件始终以 _test 后缀结尾。以下是它通常包含的内容和功能:
现在我们已经将 Cerbos 与 JS 应用程序集成,我们将运行它来检查策略是否按预期工作。
让我们使用测试文件和 Cerbos RBAC 策略生成器生成的 _testdata _ 来测试策略文件。
您可以使用此 Docker 命令(在 PowerShell 终端中)来运行测试:
docker run -i -t -v "$(Get-Location)/cerbos/policies:/policies" ghcr.io/cerbos/cerbos:latest compile /policies
运行此命令后,测试成功执行。
如果高管(具有角色:用户)尝试删除待办事项:
随着政策的实施,高管不得删除待办事项;他们会收到一条警告:您无权删除此待办事项。该权限仅授予 CPO 和 CTO 角色。
根据政策,高管可以将待办事项添加到列表中:
将任务添加到文本框并单击添加待办事项按钮将其添加到待办事项列表中。
CPO 已授权,待办事项已成功删除。
这个实际演示成功地描绘了 Cerbos 与我们的 JS 应用程序的集成。
Q1. 制定 RBAC 政策时我们必须考虑哪些最佳实践?
A1:当组织采用 RBAC 访问模型时,必须遵守有关安全和管理的最佳实践,并致力于不断改进其访问协议,以防范新出现的威胁。
以下列表包含 RBAC 最佳实践的公平样本:
问题 2:在政策生效之前,可以使用 Cerbos 进行测试吗?
A2:是的,可以在策略生效之前使用 Cerbos 对其进行测试。Cerbos 提供强大的策略测试功能,允许您在将访问控制规则部署到生产环境之前对其进行验证。使用 Cerbos Policy Generator 中提供的 Cerbos 测试框架,您可以确保策略强制执行所需的访问控制规则并避免实际场景中的潜在问题。这种部署前测试有助于维护访问控制策略的完整性和安全性,降低未经授权访问的风险并确保遵守组织的安全准则。
您已通过以下关键步骤成功将 Cerbos 集成到演示待办事项列表应用程序中:
*请认真填写需求信息,我们会在24小时内与您取得联系。