承接上一个篇博文,我们继续向下做翻译。
希望大家共同勉励学习。有错误的地方或者觉得需要补充的地方欢迎留言。
Authentication:
认证和授权的处理方式在Restlet和Servlet的世界中是截然不同的。
在restlet中,你有用绝对的过程控制权,再也没有如同Servlet一样的必要的内部的XML描述。
通常情况下,你只需要使用org.restlet.Guard或者它的子类就可以达到认证的目的。一般的编程过程需要使用一个map去存储登录名和密码信息,如果有自定义的需要,我们可以通过重写“authenticate(Request)”这个方法来达到目的。
这个说明对应的代码大概是这样:
MapVerifier verifier = new MapVerifier();
verifier.getLocalSecrets().put("scott", "tiger".toCharArray());//make map
ChallengeAuthenticator guard = new ChallengeAuthenticator(getContext(),ChallengeScheme.HTTP_BASIC,"");
guard.setVerifier(verifier); //guard authenticate it
-------------------------------------------------
获取到用户名和密码可以使用这样的代码来获取(org.restlet.data.Request):
Request.getChallengeResponse().getIdentifier();//login
Request.getChallengeResponse().getSecret();//pwd
可以参见《Restlet core 核心包之Security package. Part 1.》中的示例工程。
在AuthenticatorResource类的test()方法中添加打印来做测试:
String login = this.getRequest().getChallengeResponse().getIdentifier();
char[] password = this.getRequest().getChallengeResponse().getSecret();
StringBuffer sb = new StringBuffer();
for(char c : password) {
sb.append(c);
}
System.out.println("login:" + login);
System.out.println("password:" + sb.toString());
启动服务,启动客户端,运行结果是:
login:scott
password:tiger
success la!
如果你使用浏览器访问,那么会跳出一个弹出框要求你输入用户名和密码,示例代码中只能填入scott做用户名tiger做密码。然后就可以看到打印控制台也打印出相同的信息。
这个结果说明,可以使用这种方式来获取HTTP_BASIC协议类型的用户名和密码信息。
这种获取用户名和密码的方式目前只支持HTTP_BASIC认证。
SSL 客户端认证:为了满足HTTPS服务连接的客户端的SSL认证,要设置wantClientAuthentication或者needClientAuthentication 的参数为true.(在第一个示例中,呈现的客户端证书是可选的。)(有示例,不知道在哪里.. 看到再说)
客户端证书链会被接受为一个List,每个list元素为X509Certificates,放置到org.restlet.https.clientCertificates 请求属性中。
这句话的意思如果用代码来解释,应该是在ServerResource中这样书写:this.getRequest().getClientInfo().getCertificates(); 或者this.getClientInfo().getCertificates();
----------------------------------------
Coarsed grained authorization(粗粒度的授权):
通常授权是针对一系列的资源而言的,一个Guard的子类能被重写authorize(Request)方法,从而自定义授权规则。默认情况下,这个方法会接受所有的认证请求。你可以自定义你自己的认证机制,比如访问一个LDAP知识库。(对这个可以不用了解,只要清楚可以通过重写authorize(Request)自定义一个认证机制即可)
Fine grained authorization(细粒度的授权):
如果希望权限是细粒度的,那么就要求授权应该是针对资源水平的,而不是在Guard这样的水平。也就是说,细粒度的授权需要在访问资源的时候起到过滤的作用。当然,如果是在Guard水平上做过滤,那么就相当于在每个资源的前面都有一个过滤的规则,这样和细粒度的区别是无法针对特定的几个资源做过滤,而变成正对所有的资源。
使用这种方式,我们能够自定义依赖目标资源的值的权限并使用具体的方法来处理等等。
以上的这种细粒度的授权方式暂时没有看见过示例代码,不过可以想象应该是在ServerResource中添加代码处理。
potential vulnerability(潜在弱点):
有很多中方式能够确保REST风格的应用程序;最常见最容易的的方式是使用涉及一系列凭证的多个认证。当用户每次提出请求的时候,这些认证会让用户代理自动监视。HTTP Basic,DIgest Authentication,cookies甚至是SSL certificate authentication都是使用这种工作方式。一旦用户代理(也就是浏览器)得到了证书,它将会在每次请求服务的时候都会携带该证书。
但是,这种便利性是有代价的:有一种脆弱性叫跨站请求伪造-----当一个恶意的网站比如http://badsite,嵌入了恶意请求操作到我们的网站http://targetsite上,比如<img src='http://targetsite/delete/account/'>。当用户代理通过授权成功访问了我们的网站,那么在之后的任何一次访问中都会自动携带授权凭证。然后不小心点击了恶意网站的这个链接,那么就会在不知情的情况下,执行了一次删除操作。要命的是这个链接的操作也会自动带上授权信息。因此这个删除操作并不会被拦截,而是成功执行。
只需要稍微做一点工作就可以很容易的让javascript被很方便的使用到恶意网站中,这种攻击是很容易被拓展去执行POST(修改)操作。其他的HTTP动词(比如PUT和DELETE)相对来说想利用ajax跨站请求伪造是比较困难的。因为这要求访问XmlHttpRequest或者类似的组件,而它们通常都坚持同一来源原则。也就是说,如果不是在同一个网站上发起ajax,那么这个来源就是异常的,请求并不会被通过,可能需要做更多的工作才可能用到PUT和DELETE方法。然而如果你想要将这种漏洞的可能性完全排除也是不大明智的。
这里有一些好的防止跨站请求伪造的方法:
绝不允许GET操作有副作用(比如 将钱转移到攻击者的银行账户上),GET只能是无状态的情况。GET应该只是暴露资源的状态,而不是一个命令操作。有很多情况下,我们会用GET做一些其他操作,比如修改,增加,甚至是删除,这些都是不合理的。GET应该只是做查询。这种查询并不会改变服务上任何东西。
POST就是用来发送命令的,它会有副作用。为了增加平常的保护,就要有一些额外的认证表格被放入post操作实体中。例如,如果客户端是使用cookie认证,就要求客户端在POST实体中重申cookie.这样的措施会让跨站请求伪造变的更加困难.(无具体代码,无法直观体会)
对大多数的验证拦截,服务器会针对客户端使用application级别的验证令牌,这种令牌是一个对外不透明的值能够让服务器验证是否是属于有权限的用户。
1.这样的令牌应该要是很难被第三方计算出来的,比如一个用户的识别验证的MD5或者SHA1哈希。
2.为了让跨站域名伪造失效,这个application级别的令牌需要被传输,这意味着,用户代理(浏览器)不自动返回每个请求。例如,它能够在一个html的表单隐藏域中被发送并且通过POST在一个被编码的表单体中被返回。
(以上的这个思路,大概意思是不自动返回请求,就是不自动让请求携带认证信息,而通过其他方式传输这个认证信息。比如可以用表单提交的方式发送到服务器中被校验,然后服务使用POST方式将令牌信息编码到表单体中返回。这样即便用户代理获得了被编码的表单体也不知道令牌信息是什么,也就不能轻易恶意发送这个令牌信息了。)
由于Restlet支持柔性的URI模式,我们能够用多种方式在URI中嵌入一个application级别的的令牌。例如/admin/{token}/delete/count/.这种方式能够避免跨站请求伪造攻击。它同样能够避免暴力破解或者拒绝服务攻击。
如果cookie是使用来作为主要或者辅助的认证,抵御跨站请求伪造攻击可以通过设置在CookieSetting中的accessRestricted属性来达到目的。这样会阻止被所有类型的浏览器脚本使用的cookie。(这个很好,一下子就将这种认证漏洞给绞杀了)
-----------------------------------------------
今天先翻译到这里。
共同勉励