一、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. angular.js:13920 Error: [$injector:unpr] Unknown provider: $scopeProvider <- $scope <- testServe

    angular.js:13920 Error: [$injector:unpr] Unknown provider: $scopeProvider <- $scope <- testSer ...

  2. [VB] if 判断语句 和 If、IIf函数的比较

    Module Module1 Sub Main() Dim s1 As String = "我是真的" Dim s2 As String = "我不是真的" D ...

  3. android/java 根据当前时间判断股票交易状态(未开盘 交易中 休市中 已收盘)

    /** * @param data yyyy-MM-dd HH:mm:ss 时间 * @return 未开盘 交易中 休市中 已收盘 */ public static String getSotckS ...

  4. Go 作用

    Go语句的作用是表示一个batch(多条Tsql命令)的结束,并向sql server 提交batch,由于局部变量的作用域是基于batch的,所以,go语句限制局部变量的作用域在一个batch中. ...

  5. Sql Server系列:数据库操作

    1 创建数据库 1.1 CREATE DATABASE语法 CREATE DATABASE database_name [ ON [ PRIMARY ] <filespec> [ ,... ...

  6. Javascript对象的方法赋值

    Javascript对象编程学习中,一直不能很好的掌握对象的属性(property)和方法(method).今天在写代码过程中,又犯了一个低级错误. <!DOCTYPE html> < ...

  7. 应用程序框架实战十三:DDD分层架构之我见

    前面介绍了应用程序框架的一个重要组成部分——公共操作类,并提供了一个数据类型转换公共操作类作为示例进行演示.下面准备介绍应用程序框架的另一个重要组成部分,即体系架构支持.你不一定要使用DDD这样的架构 ...

  8. 【记录】ASP.NET MVC View 移动版浏览的奇怪问题

    ASP.NET MVC View 中的一段代码: <span id="span_Id">@Model.ID</span> 没什么问题吧,浏览器浏览正常,查看 ...

  9. 关于JqueryEasyUI集合Kindeditor

    写在前面 上一篇<初试JqueryEasyUI(附Demo)>: 在上一篇说过,下面要试下easyui集合编辑器,关于编辑器网上有很多,ckeditor.ueditor.kindedito ...

  10. C#开发微信公众平台-就这么简单(附Demo)

    写在前面 阅读目录: 服务号和订阅号 URL配置 创建菜单 查询.删除菜单 接受消息 发送消息(图文.菜单事件响应) 示例Demo下载 后记 最近公司在做微信开发,其实就是接口开发,网上找了很多资料, ...