一、ng-bing-html指令问题

需求:我需要将一个变量$scope.x = '<a href="http://www.cnblogs.com/web2-developer/">王大鹏</a>'绑定到angular的视图上,希望视图上显示的一个链接.

1.如果,我采用ng-bind="x",或者{{x}},我在视图看到的结果就是上面那个字符串,就说里面的<和>都被转义了.

2.如果,我在用ng-bind-html,视图上什么都没有,并且会抛出一个错误:"Attempting to use an unsafe value in a safe context."

问题来了,该怎么解决呢?

二、SCE

针对上面的问题,官方文档给出了解决方法:方法1,引入ngSanitize模块,方法而利用$sce.trustAsHtml将要绑定的值变成一个可信任的值。

那么,问题来了:$sce到底是什么鬼?

SCE是Strict Contextual Escaping的缩写,不知道怎么翻译,从$sce干的事情来看就是将语境中存在的跨站攻击的风险给干掉.SCE是一种模式,用于满足angular在某些情况下需要绑定一个上下文被标记为安全上下文的值.其中一个例子就是"ng-bind-html"这个指令,要绑定任意的html,我们参考上下文特权和SCE的上下文.(原文,Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain contexts to result in a value that is marked as safe to use for that context. One example of such a context is binding arbitrary html controlled by the user via ng-bind-html. We refer to these contexts as privileged or SCE contexts.)

$sce提供了一种将可能存在跨站风险的内容(包括html,url,css,js,resourceUrl)标记为被信任的内容。这是为什么呢?因为,在angular中,默认的这些内容是不被信任,所以,在绑定数据的时候,这些内容会被认为不安全。但是如果我们的确有这样的需求,就需要用$sce来做标记处理。

三、$sce如何使用

1.$sce提供的方法:

$sce.getTrustedXXX,获取被信任的数据值。其中的XXX代表Hmtl,Css,Js,Url,ResourceUrl。

$sce.trustXXX,让绑定内容,成为受信任的XXX

2.将$sce用于指令编写

在指令值,一般需要操作dom,在添加元素时,如果要将传入的变量直接作为dom元素进行添加,就会可能会带来跨站风险,这时就需要,用$sce.getTrustedXXX从变量中获取受信任的数据。

3.在controller中使用。

使用$sce.trustXXX来将确实需要被信任的数据标记为信任数据。

请慎用!

四、$sce的代码实现

1.$sce是依赖于$sceDelegate,$sce的实现就是调用$sceDelegate来完成,$sceDelegate是$sce的代理者,这里的设计采用了代理模式,所以我们可以通过修改$sceDelegate来完成对$sce的功能增强。

    sce.trustAs = $sceDelegate.trustAs;
sce.getTrusted = $sceDelegate.getTrusted;
sce.valueOf = $sceDelegate.valueOf;

2.$sce的基础方法就只有trustAs,getTrusted,valueOf,其他的方法都是这三个方法的"快捷方式"

    // Shorthand delegations.
var parse = sce.parseAs,
getTrusted = sce.getTrusted,
trustAs = sce.trustAs; forEach(SCE_CONTEXTS, function(enumValue, name) {
var lName = lowercase(name);
sce[camelCase("parse_as_" + lName)] = function(expr) {
return parse(enumValue, expr);
};
sce[camelCase("get_trusted_" + lName)] = function(value) {
return getTrusted(enumValue, value);
};
sce[camelCase("trust_as_" + lName)] = function(value) {
return trustAs(enumValue, value);
};
});

3.$sce.parse

    sce.parseAs = function sceParseAs(type, expr) {
var parsed = $parse(expr);
if (parsed.literal && parsed.constant) {
return parsed;
} else {
return $parse(expr, function(value) {
return sce.getTrusted(type, value);
});
}
};

五、$sceDelegate的实现

$sceDelegate实现三个函数:trustAs, getTrusted , valueOf,如果想实现一些自定义的安全策略,可以修改$sceDelegate或对这三个方法进行重载。

1.资源地址的白名单和黑名单

在资源的处理上,$sceDelegate引入了白黑名单机制,可以允许用户编写不同的安全策略来控制不同域名的不同权限。

function adjustMatcher(matcher) {
if (matcher === 'self') {
return matcher;
} else if (isString(matcher)) {
// Strings match exactly except for 2 wildcards - '*' and '**'.
// '*' matches any character except those from the set ':/.?&'.
// '**' matches any character (like .* in a RegExp).
// More than 2 *'s raises an error as it's ill defined.
if (matcher.indexOf('***') > -1) {
throw $sceMinErr('iwcard',
'Illegal sequence *** in string matcher. String: {0}', matcher);
}
matcher = escapeForRegexp(matcher).
replace('\\*\\*', '.*').//两个*号,将匹配任意打印字符
replace('\\*', '[^:/.?&;]*');//一个*,只能匹配url中的分隔符间的内容
return new RegExp('^' + matcher + '$');
} else if (isRegExp(matcher)) {
// The only other type of matcher allowed is a Regexp.
// Match entire URL / disallow partial matches.
// Flags are reset (i.e. no global, ignoreCase or multiline)
return new RegExp('^' + matcher.source + '$');//转正则式
} else {
throw $sceMinErr('imatcher',
'Matchers may only be "self", string patterns or RegExp objects');
}
} function adjustMatchers(matchers) {//工具函数,将配置转换成正则表达式数组
var adjustedMatchers = [];
if (isDefined(matchers)) {
forEach(matchers, function(matcher) {
adjustedMatchers.push(adjustMatcher(matcher));//调用上面的工具函数,将使用通配符方式的配置转成正则表达式
});
}
return adjustedMatchers;
} this.resourceUrlWhitelist = function(value) {//提供$sceDelegate.resourceUrlWhitelist 配置白名单
if (arguments.length) {
resourceUrlWhitelist = adjustMatchers(value);//调用上面的工具函数
}
return resourceUrlWhitelist;
}; this.resourceUrlBlacklist = function(value) {//提供$sceDelegate.resourceUrlBlacklist 配置黑名单
if (arguments.length) {
resourceUrlBlacklist = adjustMatchers(value);//调用上面的工具函数
}
return resourceUrlBlacklist;
};
    function matchUrl(matcher, parsedUrl) {//url匹配函数
if (matcher === 'self') {
return urlIsSameOrigin(parsedUrl);
} else {
// definitely a regex. See adjustMatchers()
return !!matcher.exec(parsedUrl.href);//双!限制,返回的只能是bool值
}
} function isResourceUrlAllowedByPolicy(url) {//执行白黑名单策略:只允许在白名单中且不再黑名单中的内容
var parsedUrl = urlResolve(url.toString());
var i, n, allowed = false;
// Ensure that at least one item from the whitelist allows this url.
for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) {//先判断白名单
if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) {
allowed = true;
break;
}
}
if (allowed) {
// Ensure that no item from the blacklist blocked this url.
for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) {//后处理黑名单
if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) {
allowed = false;
break;
}
}
}
return allowed;
}

2.下面byType将是什么?

    function generateHolderType(Base) {
var holderType = function TrustedValueHolderType(trustedValue) {
this.$$unwrapTrustedValue = function() {
return trustedValue;
};
};
if (Base) {
holderType.prototype = new Base();
}
holderType.prototype.valueOf = function sceValueOf() {
return this.$$unwrapTrustedValue();
};
holderType.prototype.toString = function sceToString() {
return this.$$unwrapTrustedValue().toString();
};
return holderType;
} var trustedValueHolderBase = generateHolderType(), //这里trustedValueHolderBase 将是构造函数TrustedValueHolderType,且没有绑定原型
byType = {}; //下面的都是函数
byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase);
byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase);
byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase);
byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase);
byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]);

上面的代码执行后的结果是:

3.trustAs,valueOf和getTrusted

    var htmlSanitizer = function htmlSanitizer(html) {
throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
}; if ($injector.has('$sanitize')) {//这里检查是否有$sanitize
htmlSanitizer = $injector.get('$sanitize');
} function trustAs(type, trustedValue) {
var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
if (!Constructor) {
throw $sceMinErr('icontext',
'Attempted to trust a value in invalid context. Context: {0}; Value: {1}',
type, trustedValue);
}
if (trustedValue === null || isUndefined(trustedValue) || trustedValue === '') {
return trustedValue;
}
// All the current contexts in SCE_CONTEXTS happen to be strings. In order to avoid trusting
// mutable objects, we ensure here that the value passed in is actually a string.
if (typeof trustedValue !== 'string') {
throw $sceMinErr('itype',
'Attempted to trust a non-string value in a content requiring a string: Context: {0}',
type);
}
return new Constructor(trustedValue);//将一个值标记为可信,就是用相应的构造函数进行包装
} function valueOf(maybeTrusted) {
if (maybeTrusted instanceof trustedValueHolderBase) {
return maybeTrusted.$$unwrapTrustedValue();
} else {
return maybeTrusted;
}
} function getTrusted(type, maybeTrusted) {
if (maybeTrusted === null || isUndefined(maybeTrusted) || maybeTrusted === '') {
return maybeTrusted;
}
var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
if (constructor && maybeTrusted instanceof constructor) {
return maybeTrusted.$$unwrapTrustedValue();
}
// If we get here, then we may only take one of two actions.
// 1. sanitize the value for the requested type, or
// 2. throw an exception.
if (type === SCE_CONTEXTS.RESOURCE_URL) {
if (isResourceUrlAllowedByPolicy(maybeTrusted)) {
return maybeTrusted;
} else {
throw $sceMinErr('insecurl',
'Blocked loading resource from url not allowed by $sceDelegate policy. URL: {0}',
maybeTrusted.toString());
}
} else if (type === SCE_CONTEXTS.HTML) {
return htmlSanitizer(maybeTrusted);//如果htmlSanitizer = $injector.get('$sanitize');,这里就调用了$sanitize
}
throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
} return { trustAs: trustAs,
getTrusted: getTrusted,
valueOf: valueOf };
}];

上一期:angular源码分析:angular中脏活累活承担者之$parse

下期预告:angular源码分析:angular中脏活累活的承担者之$interpolate

angular源码分析:angular中入境检察官$sce的更多相关文章

  1. angular源码分析:angular中脏活累活承担者之$parse

    我们在上一期中讲 $rootscope时,看到$rootscope是依赖$prase,其实不止是$rootscope,翻看angular的源码随便翻翻就可以发现很多地方是依赖于$parse的.而$pa ...

  2. angular源码分析:angular中$rootscope的实现——scope的一生

    在angular中,$scope是一个关键的服务,可以被注入到controller中,注入其他服务却只能是$rootscope.scope是一个概念,是一个类,而$rootscope和被注入到cont ...

  3. angular源码分析:angular中脏活累活的承担者之$interpolate

    一.首先抛出两个问题 问题一:在angular中我们绑定数据最基本的方式是用两个大括号将$scope的变量包裹起来,那么如果想将大括号换成其他什么符号,比如换成[{与}],可不可以呢,如果可以在哪里配 ...

  4. angular源码分析:injector.js文件分析——angular中的依赖注入式如何实现的(续)

    昨天晚上写完angular源码分析:angular中jqLite的实现--你可以丢掉jQuery了,给今天定了一个题angular源码分析:injector.js文件,以及angular的加载流程,但 ...

  5. angular源码分析:angular中jqLite的实现——你可以丢掉jQuery了

    一.从function JQLite(element)函数开始. function JQLite(element) { if (element instanceof JQLite) { //情况1 r ...

  6. angular源码分析:angular中各种常用函数,比较省代码的各种小技巧

    angular的工具函数 在angular的API文档中,在最前面就是讲的就是angular的工具函数,下面列出来 angular.bind //用户将函数和对象绑定在一起,返回一个新的函数 angu ...

  7. angular源码分析:angular中的依赖注入式如何实现的

    一.准备 angular的源码一份,我这里使用的是v1.4.7.源码的获取,请参考我另一篇博文:angular源码分析:angular源代码的获取与编译环境安装 二.什么是依赖注入 据我所知,依赖注入 ...

  8. angular源码分析:$compile服务——directive他妈

    一.directive的注册 1.我们知道,我们可以通过类似下面的代码定义一个指令(directive). var myModule = angular.module(...); myModule.d ...

  9. angular源码分析:angular的整个加载流程

    在前面,我们讲了angular的目录结构.JQLite以及依赖注入的实现,在这一期中我们将重点分析angular的整个框架的加载流程. 一.从源代码的编译顺序开始 下面是我们在目录结构哪一期理出的an ...

随机推荐

  1. [译]AngularJS $apply, $digest, 和$evalAsync的比较

    原文:The differences between AngularJS $apply, $digest, and $evalAsync 你是不是也常在想AngularJS $apply, $dige ...

  2. Enterprise Solution 虚拟测试环境

    在不联网的情况下,一台物理电脑安装数据库服务,VMware创建多个虚拟机,虚拟机中多个客户端并发连接到物理主机.可共用同一个物理主机的数据库,也可以测试多用户并发等问题. 1  安装微软虚拟网卡.在控 ...

  3. 虚拟机网络驱动(共享文件夹)不见了的解决方案-适用于win7~win10 and Windows Server 2008~Windows Server 2012R2

    具体看图 手动打入下面选择部分的字符 \\vmware-host\Shared Folders 然后就可以了,这边有个红叉,重启后就没了 重启后

  4. 针对格式文件,Python读取一定大小的文件内容

    由数据库导出的数据是格式化数据,如下所示,每两个<REC>之间的数据是一个记录的所有字段数据,如<TITLE>.<ABSTRACT>.<SUBJECT_COD ...

  5. lintcode二叉树的锯齿形层次遍历 (双端队列)

    题目链接: http://www.lintcode.com/zh-cn/problem/binary-tree-zigzag-level-order-traversal/ 二叉树的锯齿形层次遍历 给出 ...

  6. Ubuntu杂记——Ubuntu下Eclipse搭建Maven、SVN环境

    正在实习的公司项目是使用Maven+SVN管理的,所以转到Ubuntu下也要靠自己搭环境,自己动手,丰衣足食.步骤有点简略,但还是能理解的. 一.安装JDK7 打开终端(Ctrl+Alt+T),输入  ...

  7. JavaScript的全局变量与局部变量解析

    一.JavaScript scope 的划分标准是function函数块,不是以 if.while.for来划分的 <script> function f1(){ alert(" ...

  8. Oracle数据库资源管理

    1.了解Resource Manager术语 2.了解Resource Manager分配方法 3.了解DEFAULT_PLAN 4.新建资源计划 5.创建使用者组 6.了解资源分配方法 7.分配使用 ...

  9. struts2学习笔记--上传单个和批量文件示例

    struts2提供了对上传文件的支持,将上传后的文件封装为java.io.File对象,开发者只需要在Action中定义一个File类型的变量,然后直接使用该变量,将它复制到目的目录即可. 单个文件上 ...

  10. DotNet项目中的一些常用验证操作

    在项目中需要对用户输入的信息,以及一些方法生成的结果进行验证,一般在项目中较多的采用js插件或js来进行有关信息的校验,但是从项目安全性的角度进行考虑,可对系统进行js注入. 如果在后台对用户输入的信 ...