jsp 安全
一. 身份验证和授权
认证是检验某人真正是他/她自称的那个人的过 程。在一个Servlet/JSP应用程序中,身份验证一般通过 检查用户名密码是否正确。授权是检查该级别的用户是 否具备访问权限。它适用于包括多个区域的应用程序, 其中用户可以利用这个应用程序的部分模块,但是其他 模块就没有权限。例如,一个在线商店可被划分成的一 般部分(用于一般公众浏览和搜索产品)、买家部分 (注册用户下订单)和后台管理部分(适用于管理 员)。这三者中,后台管理部分需要访问的最高权限。 管理员用户不仅需要进行身份认证,他们还需要获得后 台管理部分的权限。
访问级别通常被称为角色。在部署一个Servlet/JSP 应用程序时可以方便地通过模块分类和配置,使得每个 模块只有特定角色才能访问。这是通过在部署中声明安 全约束描述符完成的。换句话说,就是声明式安全。在 这个范围的另一端,内容限制是通过编程实现检验用户 名和密码与数据库中存储的用户名和密码对是否匹配。
大多数Servlet和JSP应用程序的身份验证和授权首 先要验证用户名和密码与数据库表是否一致。一旦验证 成功,可检查另一个授权在同一个表中存储的用户名和 密码的表或字段。使用声明式安全可让您的编程更简 洁,因为Servlet/JSP容器负责身份验证和授权过程。此 外,Servlet/JSP容器配置数据库来验证你已经在应用程 序中使用。最重要的是,使用声明式身份验证的用户名 和密码可在被发送到服务器之前由浏览器对其加密后再 发送给服务器。声明式安全的缺点是,支持数据加密的 身份验证方法只能使用一个默认登录对话框,不能对界 面和操作进行个性化定制。这个原因就足以让人放弃声 明式安全。声明性安全的唯一方法是允许使用一个自定 义的HTML表单,不幸的是数据传输不加密。
Web应用程序的某些部分,如管理模块,是不面向 客户的,所以登录表单的外观是没有关联的。在这种情 况下,声明式安全仍然被使用。
声明式安全有趣的部分当然就是安全约束不编入 Servlet了。相反,它们在应用程序部署时声明在部署描 述符中。因此,它具有相当大的灵活性来确定用户和角 色对访问的应用程序或部分模块的权限。
要使用声明式安全,首先定义用户和角色。根据您 所使用的容器,您可以将用户和角色信息存储在一个文 件或数据库表中,然后,您对应用程序中的资源或集合 施加约束。
现在,您如何不通过编程来验证用户?你会发现后 面的答案在于HTTP而不是Servlet规范。
二. 指定用户和角色
每一个兼容Servlet/JSP容器必须提供一个定义用户 和角色的方法。如果你使用Tomcat,可以通过编辑conf 目录中的Tomcat-user.xml来创建用户和角色。tomcat-users.xml
tomcat-users.xml 文件
<?xml version='1.0' encoding='utf-8'?> <tomcat-users> <role rolename="manager"/> <role rolename="member"/> <user username="tom" password="secret" roles="manager,memb er"/> <user username="jerry" password="secret" roles="member"/> </tomcat-users>
tomcat-users.xml文件是一个xml文档的根元素 tomcat-user。在里面是role和user元素。role元素定义角 色,user元素定义用户。role元素有rolename属性指定角 色名。user元素具有username、password和role属性。 sername属性指定用户名,password属性指定密码,role 属性指定角色或用户属于的角色。
tomcat-users.xml文件声明了两个角色 (经理和成员)和两个用户(tom和jerry)。用户tom是 一个成员和经理的角色,而杰瑞只属于成员角色。很明 显,汤姆比杰瑞具有接入更多应用的权限。
Tomcat还支持通过数据库表来匹配角色和用户。 你可以配置Tomcat使用JDBC来验证用户身份。
二. 实施安全约束
你已经学会通过把静态资源和JSP页面放在WEBINF目录下来隐藏起来。资源放置在这里不能直接通过 输入URL访问,但仍然可以从一个Servlet或JSP页面进 入。虽然这种方法简单明了,缺点是资源隐藏在这里永 远是隐藏的,没有办法直接访问。如果你只是简单地想 保护资源不被未经授权的用户访问,你可以把它们放在 应用程序目录下的一个目录中,在部署描述符中声明一 个安全约束。
security-constraint元素指定一个资源集合和角色或 角色可以访问的资源。这个元素有两个子元素:webresource-collection和auth-constraint。
web-resource-collection元素指定一组资源,可以包 括web-resource-name、description、url-pattern、httpmethod和http-method-ommission等子元素。
web-resource-collection元素可以有多个url模式子元 素,每一个都是指一个URL正则表达式用于指定安全约 束。您可以使用星号的url模式元素来引用一个特定资 源类型(例如, * . jsp)或所有资源目录(比如/ *或/ jsp / *)。然而,你不能同时指定两个,例如,在一个 特定的目录中指定一个特定类型。因此,下面的URL表 达式指定jsp目录下的所有JSP页面是无效的:/ JSP / * . JSP。相反,使用/ jsp / * ,也将限制任何在jsp目录下的 非JSP页面。
http-method元素为封闭的安全约束的应用命名了一 个http方法。例如,一个web-resource- collection元素以 GET http-method命名,表明该web-resource-collection元 素仅适用于HTTP GET方法。包含资源集合的安全约束 不能防止其他HTTP方法,如PUT方法。没有httpmethod元素表示安全约束限制了所有HTTP访问方法。 你可以在同一个web-resource-collection中拥有多个 httpmethod元素。
http-method-omission元素指定一个不包含HTTP方 法的安全约束。因此,指定< http-methodomission > GET< / http-method-omission >表示限制除了GET外的所 有HTTP方法。
http-method元素和http-method-omission元素不能出 现在相同的web-resource-collection元素里。
在部署描述符中可以有多个security-constraint元 素。如果security-constraint元素没有auth-constraint元 素,那么这个资源集合是不被保护的。此外,如果你指 定的角色没有在容器中定义,那么没有人能够直接访问 这个资源集合。然而,你仍然可以通过一个servlet或 JSP页面转向集合中的资源。
这里有个例子,下面的xml文件的securityconstraint元素限制所有JSP页面的访问权限。由于authconstraint不包含rolename元素,因此无法通过它们的 urls直接访问这个资源
防止访问特定目录下的资源
<?xml version="1.0" encoding="ISO-8859-1"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee ➥ http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0" > <!-- restricts access to JSP pages --> <security-constraint> <web-resource-collection> <web-resource-name>JSP pages</web-resource-name> <url-pattern>*.jsp</url-pattern> </web-resource-collection> <!-- must have auth-constraint, otherwise the specified web resources will not be restricted --> <auth-constraint/> </security-constraint> </web-app>
servlet容器将发送一个HTTP 403错误:访问所请求 的资源已经被否认。
现在让我们看看如何对用户进行身份验证和授权。
二.身份验证方法
现在你应该知道如何实施资源集合的安全约束,你 也应该学会如何验证访问资源的用户信息。由于以声明 的方式获得的资源,在部署描述符中使用的是安全约束 元素,因此身份验证可以使用HTTP 1.1提供的解决方 案:基本访问认证和摘要访问身份验证。此外,还可以 使用基于表单的访问认证。HTTP身份验证是在RFC 2617中定义的。你可以在这里下载规范:
http://www.ietf.org/rfc/rfc2617.txt
基本访问身份验证,或简称基本认证,是一个接受 用户名和密码的HTTP身份验证。访问受保护的资源的 用户将被服务器拒绝,服务器会返回一个401(未经授 权)响应。该响应包含一个WWW-Authenticate头,包 含至少一个适用于所请求资源的认证域。这里有一个响 应内容的例子:
HTTP/1.1 401 Authorization Required Server: Apache-Coyote/1.1 Date: Wed, 21 Dec 2011 11:32:09 GMT WWW-Authenticate: Basic realm="Members Only
浏览器会显示用户输入用户名和密码的登录对话 框。当用户单击“登录”按钮时,用户名将被加上一个冒 号并与密码连接起来形成一个字符串。该字符串在被发 送到服务器之前将用Base64算法编码。成功登录后,服 务器将发送所请求的资源。Base64是一个非常弱的算 法,因此很容易解密Base64的信息。考虑使用摘要访问 认证来替代。
app12b应用程序展示了如何使用基本访问认证。下面提供了应用程序的部署描述符。第一个securityconstraint元素保护直接访问的JSP页面。第二个限制访 问Servlet1 servlet的经理和成员角色。Servlet1类是一个 简单的进入到1.jsp的servlet,如下所示。
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee ➥ http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <!-- restricts access to JSP pages --> <!-- 启用安全访问 --> <security-constraint> <!-- 设置被保护的资源名 以及路径 --> <web-resource-collection> <web-resource-name>JSP pages</web-resource-name> <url-pattern>*.jsp</url-pattern> </web-resource-collection> <!-- must have auth-constraint, otherwise the specified web resources will not be restricted --> <!-- 保护规则 如果没有auth-constarint 将不会保护资源, 如果没有auth 设置用角色和用户名,则所有人都不得访问--> <auth-constraint /> </security-constraint> <security-constraint> <web-resource-collection> <web-resource-name>Servlet1</web-resource-name> <url-pattern>/servlet1</url-pattern> </web-resource-collection> <auth-constraint> <!-- --> <role-name>tomcat</role-name> <role-name>tom</role-name> </auth-constraint> </security-constraint> <!-- 访问方式设置 --> <login-config> <!-- 设置访问的方法为基本访问权限 --> <auth-method>BASIC</auth-method> <!-- --> <realm-name>Members Only</realm-name> </login-config> </web-app>
在清单12.3中的部署描述符中最重要的元素是 loginconfig元素。它有两个子元素:auth-method和 realm-name。要使用Basic access authentication,您必须 将它的值设为BASIC(所有字母大写)。在浏览器登录 对话框中显示的realm-name元素必须赋值。
tomcat 的tomcat-users.xml
<!-- <role rolename="tomcat"/> <role rolename="role1"/> <user username="tomcat" password="<must-be-changed>" roles="tomcat"/> <user username="both" password="<must-be-changed>" roles="tomcat,role1"/> <user username="role1" password="<must-be-changed>" roles="role1"/> --> <role rolename="tomcat" /> <user username="tom" password="1" roles="tomcat" /> </tomcat-users>
此时,无法像之前那样直接看到Servlet1的输出, 相反,你会收到一个提示——要求输入用户名和密码
只要auth-constraint元素映射到Servlet1指定经理和 成员的角色,您可以使用tom或者jerry登录。 摘要访问接入认证,或简称摘要认证,也是一个 HTTP认证,类似基本认证。但不使用弱加密的base64 算法,而使用MD5算法创建一个组合用户名、域名和 密码的哈希值,并发送到服务器。摘要访问身份验证是 为了取代基本的访问认证,因为它提供了更安全的环 境。
servlet和JSP容器没有义务支持摘要访问认证但大 多数都有做。
配置应用程序使用摘要访问认证的方式类似于使用 基本访问认证。事实上,唯一的区别是login-config元素 内的auth-method元素的值。对于摘要访问认证,authmethod元素值必须是DIGEST(大写)。
作为一个例子,该app12c演示应用的Digest access authentication(摘要访问认证)的使用
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee ➥ http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <!-- restricts access to JSP pages --> <!-- 启用安全访问 --> <security-constraint> <!-- 设置被保护的资源名 以及路径 --> <web-resource-collection> <web-resource-name>JSP pages</web-resource-name> <url-pattern>*.jsp</url-pattern> </web-resource-collection> <!-- must have auth-constraint, otherwise the specified web resources will not be restricted --> <!-- 保护规则 如果没有auth-constarint 将不会保护资源, 如果没有auth 设置用角色和用户名,则所有人都不得访问--> <auth-constraint /> </security-constraint> <security-constraint> <web-resource-collection> <web-resource-name>Servlet1</web-resource-name> <url-pattern>/servlet1</url-pattern> </web-resource-collection> <auth-constraint> <!-- --> <role-name>tomcat</role-name> <role-name>tom</role-name> </auth-constraint> </security-constraint> <!-- 访问方式设置 --> <login-config> <!-- 设置限制访问的规则 --> <auth-method>DIGEST</auth-method> <!-- --> <realm-name>>Digest1 authentication</realm-name> </login-config> </web-app>
我们在浏览器里输入这个地址来测试一下:
1. 基于表单的认证
基本和摘要访问认证不允许你使用一个定制的登录 表单。如果你必须有一个自定义窗体,那么你可以使用 基于表单的认证。由于发送明文,你应当与SSL配合使 用。
基于表单的身份验证,您需要创建一个登录页面和 一个错误的页面,这可以是HTML或JSP页面。第一次 请求受保护的资源,servlet和JSP容器将登录页面。在 成功登录时,所请求的资源将被发送。如果登录失败, 用户会看到错误页.
使用form-based authentication(基于表单的身份验 证),您的部署描述符的auth-method 元素的值必须是 FORM(大写)。此外,login-config元素必须有formlogin-config元素节点,该节点有两个子元素,formlogin-page和form-error-page。如下是一个基于表单的身 份验证登录 login-config元素的示例:
<!-- 访问方式设置 --> <login-config> <!-- 设置登陆验证的方法为来来自form表单 --> <auth-method>FORM</auth-method> <form-login-config> <!-- 甚至登陆页面 --> <form-login-page>/login.html</form-login-page> <!-- 设置错误页面 --> <form-error-page>/error.html</form-error-page> </form-login-config> </login-config>
app12d的部署描述符,基于表单身份验证 的例子
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee ➥ http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <!-- restricts access to JSP pages --> <!-- 启用安全访问 --> <security-constraint> <!-- 设置被保护的资源名 以及路径 --> <web-resource-collection> <web-resource-name>JSP pages</web-resource-name> <url-pattern>*.jsp</url-pattern> </web-resource-collection> <!-- must have auth-constraint, otherwise the specified web resources will not be restricted --> <!-- 保护规则 如果没有auth-constarint 将不会保护资源, 如果没有auth 设置用角色和用户名,则所有人都不得访问 --> <auth-constraint /> </security-constraint> <security-constraint> <web-resource-collection> <web-resource-name>Servlet1</web-resource-name> <url-pattern>/servlet1</url-pattern> </web-resource-collection> <auth-constraint> <!-- --> <role-name>tomcat</role-name> <role-name>tom</role-name> </auth-constraint> </security-constraint> <!-- 访问方式设置 --> <login-config> <!-- 设置登陆验证的方法为来来自form表单 --> <auth-method>FORM</auth-method> <form-login-config> <!-- 甚至登陆页面 --> <form-login-page>/login.html</form-login-page> <!-- 设置错误页面 --> <form-error-page>/error.html</form-error-page> </form-login-config> </login-config> </web-app>
login.html页面
<!DOCTYPE html> <html> <head> <title>Login</title> </head> <body> <h1>Login Form</h1> <form action='j_security_check' method='post'> <div> User Name: <input name='j_username'/> </div> <div> Password: <input type='password' name='j_password'/> </div> <div> <input type='submit' value='Login'/> </div> </form> </body> </html>
error.html页面
<!DOCTYPE html> <html> <head> <title>Login error</title> </head> <body> Login failed. </body> </html>
测序下app12d基于表单的认证,直接浏览器这个 URL。
2.客户端证书认证
也称为client-cert认证,客户端证书身份通过 HTTPS(HTTP通过SSL)认证,要求每个客户有一个 客户端证书。这是一个非常强大的身份验证机制,但不 适合在互联网上部署的应用程序,因为它是不切实际的 要求每个用户自己的数字证书。然而,这种身份验证方 法可以用来访问组织内部的应用。
三. 安全套接层
1. 密码学
我们时不时需要一个安全信息通道,使得信息是安 全的,即便外部可以访问信息,也不能理解并篡改信 息。
从历史上看,密码只关心加密和解密,在双方可以 放心的交换信息,只有他们可以读取消息。在开始的时 候,人们使用对称密码加密和解密消息。在对称密码, 您使用相同的密钥来加密和解密消息。这是一个非常简 单的加密/解密技术。
假设,加密方法使用一个秘密号码前移中每个字符 的字母表。因此,如果密码是2,加密 版“ThisFriday”是“vjkuhtkfca”。当你到了字母表的结 尾,你从头开始,因此Y变成A.接收器,知道密钥是 2,可以很容易地解密消息。
然而,对称加密要求双方提前知道用于加密/解密 的密钥。对称加密是不适合互联网的原因如下:
- 两人交换消息往往不知道对方。例如,在购买一本 书在亚马逊网站上你需要发送您的个人资料和信用 卡信息。如果对称密码被使用,你必须调用亚马逊 的交易之前必须同意这个密钥。
- 每个人都希望能够与其他各方沟通。如果是使用对 称密码,每个人都会有不同的独特的钥匙应对不同 的地方。
- 既然你不知道你要与之通信的实体,你需要确定他 们的真实身份。
- 信息在互联网上通过许多不同的计算机传播。这样 很容易挖掘其他人的消息。对称密码体制并不能保 证数据没被第三方篡改。
因此,今天的安全通信在互联网上使用非对称加 密,提供了这三个特点:
- 加密/解密。信息对第三方进行加密隐藏。只有预期 的接收者才能解密。
- 身份验证。验证确保实体就是声称者。
- 数据的完整性。许多计算机在互联网上发送的消息 传递。它必须是确保发送的数据不变,完好无损。
在非对称加密,使用公钥加密。这种类型的加密, 加密和解密的数据是通过使用一对非对称密钥:公钥和 私钥。私钥是私有的。颁发者必须保持它在一个安全的 地方,它不能落入任何另一方的手里。公钥分发给公 众,通常谁都可以下载这个密钥与颁发者沟通。您可以 使用工具来生成公钥和私钥。
公钥加密的优点是:使用公共密钥加密的数据只能 使用对应的私钥进行解密,在同样使用私钥加密的数据 只能使用对应的公钥解密。这优雅的算法是基于大素 数,由Ron Rivest,Adi Shamir,和Len Adleman在麻省 理工学院(MIT)在1977年发明的。它们简称为RSA算 法,基于他们的姓氏的首字母。
RSA算法是在互联网上的使用实践被证明,特别是 电子商务,因为只有供应商要求有一个密钥对来同所有 的买家进行安全通信。
爱丽丝(Alice)与鲍伯(Bob)是广泛地代入密码 学和物理学领域的通用角色。这里我也会使用他们
2. 加密/解密
交换信息的一方必须有一个密钥对。如果爱丽丝想 和鲍勃交流,鲍勃有公共密钥和私有密钥。鲍勃将公钥 送给爱丽丝,爱丽丝可以用它来加密发送给Bob的消 息。只有鲍伯可以解密因为他拥有对应私钥。若鲍勃要 发消息给爱丽丝,鲍勃使用自己的私钥加密消息,爱丽 丝可以用Bob的公钥解密。
然而,要交出自己的公共密钥,除非鲍勃可以与爱 丽丝见面,这种方法并不完美。有一对密钥的任何人都 可以声称自己是鲍勃,但是爱丽丝却无法辨别。在互联 网上,在双方交换消息经常生活在半个地球之外,会面 往往是不可能的。
3. 认证
SSL身份验证是通过引入证书。证书包含以下几 个:
- 公钥。
- 关于主题的信息,即公开密钥的所有者。
- 证书发行机构的名字。
- 到证书到期时间的时间戳
关于证书,重要的是,它必须由一个可信的数字签 名证书颁发者,如VeriSign或Thawte。对电子文件进行 数字签名(一文件,一个JAR文件,等等)是你的文 档/文件中添加你的签名。原始文件没有加密,和签名 的真正目的是为了保证文件/文件没有被篡改。签署一 份文件涉及创建一个文档的摘要,并使用签名者的私钥 对摘要加密。要检查文档是否仍它还是原来的状态,你 执行这两个步骤:
(1)使用签名者的公钥解密伴随文件摘要。你很 快就会发现,一个受信任的证书发行者的公钥很容易获 得。
(2)创建一个文档的摘要。
(3)比较结果的步骤1和步骤2的结果。如果两者 匹配,那么该文件未被篡改。
这种认证方法因为只有私钥的持有者可以加密文件 摘要,这种摘要只能使用相应的公钥解密。如果你相信 你持有原始公钥,然后你知道文件是否已被改变。
注意:
由于证书可以由受信任的证书颁发者进行数字签名,人们公开发布证书,而不是公钥。
有一些证书发行机构,包括VeriSign和Thawte。证 书颁发者有一对公钥和私钥。要申请一个证书,鲍勃产 生一对密钥,并发送自己的公钥证书给颁发者,后者通 过叫鲍勃送他的护照复印件或其他类型的身份证件进行 验证。经核实,证书颁发者使用其私钥签订证书。通 过“签名”这意味着加密。因此,证书只能通过使用证书 发放者的公钥读取。证书颁发者的公钥通常是分布广 泛。例如,IE浏览器,网景,FireFox和其他浏览器默 认包括几个证书颁发者的公钥。
例如,在IE中,单击“工具-> Internet选项->内容-> 凭证->受信任的根证书颁发机构”选项卡查看证书列 表。。
上图 证书发行者的公钥是嵌入在IE浏览器
现在,有一个证书,鲍勃将分发证书代替他的公钥 在与另一方交换信息之前。
下面是它如何工作的:
A->-B嗨鲍勃,我想和你说话,但首先我需要确认 你真的鲍勃。
B->-A很公平,这里是我的证件
A->-B这是不够的,我需要一些从你身上的别的东 西
B->-A爱丽丝,这真是我+【使用Bob的私钥信息摘 要加密】
在鲍勃爱丽丝的最后一条消息,该消息已经使用其 私钥加密,来说服爱丽丝的消息是真实的。这就是如何 进行认证证明。爱丽丝同鲍勃交流,鲍勃发给爱丽丝其 证书。然而,只有证书是不够的,因为任何人都可以得 到Bob的证书。记得鲍勃给证书的人谁愿意和他交换信 息。因此,鲍勃送她的消息(“爱丽丝,这真是我”), 并用自己的私钥加密同一消息的摘要。
爱丽丝从证书得到Bob的公钥。她能做到这一点, 因为证书是使用证书颁发者的私钥签名,爱丽丝已获得 了证书颁发者的公钥(她的浏览器保持它的一个副 本)。现在,她还得到了消息,并使用Bob的私钥加密 的摘要。所以爱丽丝需要做的就是生成该消息的摘要, 并将其与鲍勃所发送的摘要进行比较。爱丽丝可以解密 它,因为它已经使用Bob的私钥加密,Alice有Bob的公 开密钥的副本。如果两者匹配,爱丽丝可以肯定的是, 对方真的是鲍勃。
爱丽丝验证鲍勃后首先发送将用于随后的消息交换 的密钥。这是正确的,一旦安全通道的建立,SSL使用 对称加密,因为它比非对称加密快得多。
现在,这个情景中还有一件事缺失。在互联网上传 递的消息多台计算机。你如何确保这些信息的完整性, 因为任何人都可以拦截在路上这些消息?
4. 数据的完整性
Mallet,恶意的一方,可以坐在爱丽丝和鲍勃之 间,试图破译发送的消息。不幸的是他,即使他能复制 的讯息,但信息是加密的,且Mallet不知道的密钥。然 而,Mallet会破坏信息或不传达一些他们。为了克服这 个问题,SSL引进一个消息认证码(MAC)。MAC是 用一个密钥和传输的数据计算出的数据块。因为Mallet 不知道秘钥,他不能正确的计算摘要。消息接收器可以 因此会发现是否有人企图篡改数据或者数据不完整。如 果发生这种情况,双方可停止沟通。
MD5是其中一个这样的消息摘要算法。它是由 RSA发明,是非常安全的。举例说明,如果使用128位 的MAC值,恶意的一方的猜测正确的价值的几率是 18446744073709551616分之1,或几乎没有。
5. SSL是怎么工作的
现在你知道SSL如何处理加密/解密,认证和数据完 整性的问题,让我们回顾一下SSL是如何工作的。这一 次,让我们Amazon.com(代替鲍勃)和买方(而不是 爱丽丝)为例。Amazon.com,和任何其他真正的电子 商务供应商一样,他已向受信任的证书颁发者申请证 书。买方使用Internet Explorer,它嵌入了可信证书发行 机构的公钥。买方并不真的需要知道如何SSL的工作原 理,也不需要有一个公共密钥或私有密钥。他需要保证 的一件事是,当进入重要的细节时,如信用卡号时,所 使用的协议是HTTPS来代替HTTP。这出现在url框里。 因此,http://www.amazon.com,它必须以https开头。例 如:https://secure.amazon.com。一些浏览器也显示一个 安全的图标在地址栏。
显示了IE安全标志。
当买家进入一个安全的网页(当他已经完成购 物),这是他的浏览器和亚马逊的服务器在后台发生的 一系列事件。
浏览器:你真的Amazon.com吗?
服务器:是的,这是我的证书。
然后浏览器使用发行者证书的公钥解密检查证书的 有效性。如果有什么是错的,例如,如果证书过期了, 浏览器将警告用户。如果用户同意继续尽管证书过期, 浏览器将继续下去。
浏览器:单独的证书是不够的,请给一些别的东 西。
服务器:我真的Amazon.com + [使用亚马逊网站的 私钥加密同一消息的摘要]。
浏览器使用Amazon的公钥解密摘要,并创建“我真 的Amazon.com”的摘要。如果两者匹配,验证成功。那 么浏览器就会产生一个随机密钥,使用Amazon的公钥 加密。这个随机密钥来加密和解密随后的消息。换句话 说,一旦亚马逊使用加密认证,它将是对称加密认证, 因为它比非对称加密快很多。除了消息,双方还将发送 消息摘要来确保信息的完整不变。
四. 编程式安全
尽管声明性安全简单易懂,但在特殊情况下,你想 写代码来确保你的应用程序。为了这个目的,你可以在 HttpServletRequest接口使用安全注释类型和方法。都是 在这部分讨论。
1. 安全注释类型
部署描述符中的 security-constraint元素集合用来限制访问资源。此元素的 一个方面是您使用相匹配的资源的URL进行限制的URL 模式。Servlet 3提供的注释类型可以在一个servlet级别 执行相同的工作。使用这些注释类型,你可以限制访问 一个servlet,而不用在部署描述符中添加securityconstraint元素。但是,你仍然需要一个login-config元素 的部署描述符来选择一个身份验证方法。
在javax.servlet.annotation包,安全相关的有三个注 释类型。他们是ServletSecurity,HttpConstraint,和 HttpMethodConstraint。
ServletSecurity注释类型是在一个使用在servlet类上 用于强制安全约束。一个servlet安全注解可能有值和 httpMethodConstraint属性。
在HttpConstraint注释类型定义了安全约束,只能分 配给ServletSecurity注解的值属性。
若HttpMethodConstraint属性不存在于 ServletSecurity注释内,由HttpConstraint注解施加的安 全约束适用于所有的HTTP方法。否则,安全约束将应 用于列举HttpMethodContraint属性定义的HTTP方法。 例如,下面的注解HttpConstraint决定了注解的servlet只 能由那些经理角色进行访问:
@ServletSecurity(value = @HttpConstraint(rolesAllowed = "manager"))
当然,注释上述可改写如下:
@ServletSecurity(@HttpConstraint(rolesAllowed = "manager"
你仍然需要在部署描述符来声明login-config元素, 以使容器可以验证用户:
设置transportGuarantee.confidential的HttpConstraint 标注到transportGuarantee属性使servlet只能通过秘密渠 道,如SSL:
@ServletSecurity(@HttpConstraint(transportGuarantee = TransportGuarantee.CONFIDENTIAL))
如果servlet和JSP容器接受这样一个Servlet通过 HTTP请求时,它将浏览器重定向到HTTPS版本相同的 URL。
该HttpMethodConstraint注释类型指定一个安全约 束适用于任何的HTTP方法。它只能出现分配给 ServletSecurity注释的HttpMethodConstraint属性的数组 中。例如,下面的注解HttpMethodConstraint限制通过 HTTP访问该注释的servlet manager角色,对于其他 HTTP方法,则不存在限制:
@ServletSecurity(httpMethodConstraints = {
@HttpMethodConstraint(value = "GET", rolesAllowed = "manage
r")
})
请注意,如果rolesAllowed属性在 HttpMethodConstraint注释里不存在,则对于指定的 HTTP方法没有限制。例如,下面的ServletSecurity注释 同时采用两个约束。HttpConstraint注释定义了可以访问 servlet的角色,而HttpMethodConstraint注解编写了覆盖 GET方法约束,且没有rolesAllowed属性。因此,该 servlet可以被任何用户通过GET方法访问。在另一方 面,通过其他所有的HTTP方法访问只能授予的经理角 色的用户:
@ServletSecurity(value = @HttpConstraint(rolesAllowed = "manager"), httpMethodConstraints = {@HttpMethodConstraint("GET")})
然而,如果对HttpMethodConstraint注释类型的 emptyrolesemantic属性设置为emptyrolesemantic. deny, 那么方法是限制所有用户。例如,servlet使用以下 ServletSecurity注释的,防止通过Get方法访问,但是允 许所有用户成员的角色通过其他HTTP方法访问:
@ServletSecurity(value = @HttpConstraint(rolesAllowed = "member"), httpMethodConstraints = {@HttpMethodConstraint(value = "GET", emptyRoleSemantic = EmptyRoleSemantic.DENY)})
2. Servlet的安全API
除了在上一节讨论的注释类型,程序的安全性也可 以在HttpServletRequest接口使用以下方法实现
java.lang.String getAuthType()
返回用来保护servlet认证方案,如果没有安全约束 则返回空。
java.lang.String getRemoteUser()
返回发出此请求登录用户,如果用户尚未验证则返 回空。
boolean isUserInRole(java.lang.String role
返回一个指示用户是否属于指定的角色布尔值。
java.lang.Principal getUserPrincipal()
返回包含当前通过验证的用户的细节信息的java、 security.principal,如果用户没有通过认证返回空。
boolean authenticate(HttpServletResponse response) throws
java.io.IOException
通过指示浏览器显示登录表单来验证用户。
void login(java.lang.String userName, java.lang.String password) throws javax.servlet.ServletException
试图使用所提供的用户名和密码进行登录。该方法 没有返回,如果登录失败,它会抛出一个 ServletException异常。
void logout() throws javax.servlet.ServletException
注销用户。
ProgrammaticServlet是app12e应 用程序的一部分,演示如何使用编程的方式来验证用 户,相配套的部署描述符,描述符中声明了一个采用摘要访问认证的login-config元素。
ProgrammaticServlet类
package servlet; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet(urlPatterns = { "/prog" }) public class ProgrammaticServlet extends HttpServlet { private static final long serialVersionUID = 87620L; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 发送一个登陆验证页面给客户端 if (request.authenticate(response)) { response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println("Welcome"); } else { // user not authenticated // do something System.out.println("User not authenticated"); } } }
web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee ➥ http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <login-config> <auth-method>DIGEST</auth-method> <realm-name>Digest authentication</realm-name> </login-config> </web-app>
当用户第一次请求servlet,用户未经身份验证和认 证方法返回false。作为一个结果,servlet和JSP容器将 发送一个WWW-Authenticate头,浏览器会显示一个摘 要访问认证登录对话框。当用户提交表单时使用正确的 用户名和密码进行身份验证,该方法返回true,显示欢 迎信息。
3.