第十四章_安全性

14.1、验证和授权

验证是检验某个人是否是他/她所声称的那个人的过程。在Servlet/JSP应用程序中,验证一般是通过要求用户输入用户名和密码来完成的。

授权主要是确定一个用户具有什么样的访问级别。它使用于包含多个访问区域的应用程序,使用户能够访问应用程序的某一部分,但是不能访问其他部分。例如,网上商店可以分为公共区(供一般的公共浏览和查找商品),买家区(供已注册用户下单用),以及需要最高访问级别的管理区。不仅管理员用户自身也需要进行验证,并且还必须是已经被允许访问管理区的用户才行。访问级别经常被称作角色。

 

14.1.1、定义用户和角色

每一种兼容的Servlet/JSP容器都必须提供一种定义用户和角色的方法。如果你使用的是Tomcat,就可以通过编辑conf目录下的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,member”/>

<user username=”jerry” password=”secret” roles=”member”/>

</tomcat-users>

tomcat-users.xml文件是一个带有tomcat-users根源素的XML文档。其中有roleuser元素。role元素定义角色,user元素定义用户。role元素有一个rolename属性,用来指定角色的名称。user元素有usernamepasswordroles属性。username属性指定用户名称,password属性指定密码,roles属性则指定该用户所属的一个或多个角色


14.1.2、强加安全性约束

前面讲过,将静态资源和JSP页面保存在WEB-INF或其下的某一个目录下,可以讲他们隐藏起来。放在这里的资源无法直接通过输入网址而访问到,但是仍然可以通过一个Servlet或者JSP页面跳转到那里。虽然这种方法简单直接,但缺点是藏在这里的资源就永远被藏起来了。它们永远无法被直接访问到。如果只是想避免未授权的用户访问这些资源,那么可以讲它们放在应用程序目录下的某一个目录中,并在部署描述符中声明一个安全性约束。

security-constraint元素用于指定一个资源集合,以及可以访问这些资源的一个或多个角色。这个元素可以有两个子元素:web-resource-collectionauth-constraint

web-resource-collection元素用于指定一个资源集合,并且可以带有以下元素:web-resource-namedescriptionurl-patternhttp-methodhttp-method-ommission

web-resource-collection元素可以带有多个url-pattern元素,每一个子元素都表示所包含安全性约束实行的一种URL模式。我们可以在url-pattern元素中用一个星号(*)表示一种特定的资源类型(*.jsp),或者表示某个目录下的所有资源(/*/jsp/*)。但是这两种不能合二为一,比如,无法表示某一个特定目录下的某一种特定的类型。因此,如果用/jsp/*.jsp表示jsp目录下的所有jsp页面,那么这个URL模式将是无效的,而是要用/jsp/*来表示,但是这样又限制了jsp目录下那些非jsp的页面。

http-method元素中定义了一个HTTP方法,包含的安全性约束即应用到该方法中。例如,web-resource-collection元素带有一个GET http-method元素,表示web-resource-collection元素只应用于HTTPget方法。包含资源集合的安全性约束并不能保护其他的HTTP方法,例如Post方法和Put方法。如果没有http-method元素,表示这个安全性约束将会限制对所有HTTP方法的访问。同一个web-resource-collection元素中可以带有多个http-method元素。

http-method-omission元素指定的是不再所包含安全性约束中的HTTP方法。因此,指定<http-method-omission>GET</http-method-omission>限制了对除GET之外的所有HTTP方法的访问。

http-methodhttp-method-omission元素不能同时出现在同一个web-resource-collection元素中。

部署描述符中可以有多个security-constraint元素。如果安全性约束元素中没有auth-constraint元素,那么这个资源集合将不能受到保护。此外,如果你指定了一个没有在容器中定义的角色,那么将没有人能够直接访问这个资源集合。但是,你还是可以通过一个Servlet或者JSP页面跳转到集合中的某个资源。

举个例子。下面的web.xml文件中的security-constraint元素限制了对所有jsp页面的访问。由于auth-constraint元素中没有包含role-name元素,因此不能通过url来访问这些资源。

 

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 
	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">
  <display-name></display-name>	
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
  <security-constraint>
  	<web-resource-collection>
  		<web-resource-name>JSP pages</web-resource-name>
  		<!-- 所有的jsp页面都不能被访问 -->
  		<url-pattern>*.jsp</url-pattern>
  	</web-resource-collection>
  	<auth-constraint></auth-constraint>
  </security-constraint>
</web-app>

现在,可以在浏览器中打开网址测试一下:

Servlet容器会发送一条HTTP403错误,温柔地告诉你:Access to the requested resource has been denied(禁止访问)。


14.2、验证方法

学会如何对一个资源集合强加安全性约束之后,还应该知道如何对访问这些资源的用户进行验证。对于声明式保护的资源,可以在部署描述符中使用security-constraint元素,通过使用HTTP 1.1提供的解决方案来完成验证:基本访问验证和摘要访问验证。此外,也可以使用基于表单的访问验证。

 

14.2.1、基本访问验证

基本访问验证,简称基本验证,是一种接受用户名和密码的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”

随后浏览器屏幕上回显示一个登录对话框,供用户输入用户名和密码。当用户单击Login登录按钮时,用户名后面会被添上一个冒号,并和密码结合在一起。这个字符串被送到服务器之前,将先用Base64算法进行编码。登录成功之后,服务器就会发出被请求的资源。

Base64是一种很弱的算法,因此Base64消息很容易被破解。因此,要考虑使用摘要访问来代替。

下面展示了如何使用基本访问验证。第一个security-constraint元素是避免JSP页面被直接访问。第二个是限制只有managermember角色的用户才能访问Servlet1 ServletServlet1类是一个跳转到1.jsp页面的简单Servlet

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 
	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">
  <display-name></display-name>	
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
  <security-constraint>
  	<web-resource-collection>
  		<web-resource-name>JSP pages</web-resource-name>
  		<!-- 所有的jsp页面都不能被访问 -->
  		<url-pattern>*.jsp</url-pattern>
  	</web-resource-collection>
  	<auth-constraint></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>member</role-name>
  		<role-name>manager</role-name>
  	</auth-constraint>
  </security-constraint>
  
  <login-config>
  <!-- BASIC必须全部大写 -->
  	<auth-method>BASIC</auth-method>
  	<!-- 显示在浏览器的登陆对话框中 -->
  	<realm-name>Members Only</realm-name>
  </login-config>
</web-app>

servlet1.java

package app11a.servlet;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
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={"/servlet1"})
public class Servlet1 extends HttpServlet{

	private static final long serialVersionUID = 1L;
	
	@Override
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		RequestDispatcher dispatcher = request.getRequestDispatcher("/jsp/1.jsp") ;
		dispatcher.forward(request, response);
	}
}


由于映射到Servlet1的auth-constraint元素指定了manager和member两种角色,因此通过tom或者jerry都可以登录。

14.2.2、摘要访问验证

摘要访问验证,或简称摘要验证,它也是一种HTTP验证,与基本访问验证相似。摘要访问验证不是使用软弱的Base64算法,而是用MD5算法创建一个散列,其中结合了用户名、relam名称以及密码,并将这个散列发送到服务器。摘要访问验证旨在取代基本访问验证,因为她提供了更加安全的环境。

Servlet/JSP容器没有强制要求支持摘要访问验证,但是大多数容器都支持。

将应用程序配置成使用摘要访问验证,做法与使用基本访问验证时相似。事实上,它们之间的唯一区别在于login-config元素中的auth-method元素的值。在摘要访问验证中,这个元素的值必须为DIGEST(全部大写)。

下面就是举个例子使用了摘要访问验证。

 web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 
	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">
  <display-name></display-name>	
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
  <security-constraint>
  	<web-resource-collection>
  		<web-resource-name>JSP pages</web-resource-name>
  		<!-- 所有的jsp页面都不能被访问 -->
  		<url-pattern>*.jsp</url-pattern>
  	</web-resource-collection>
  	<auth-constraint></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>member</role-name>
  		<role-name>manager</role-name>
  	</auth-constraint>
  </security-constraint>
  
  <login-config>
  	<auth-method>DIGEST</auth-method>
  	<!-- 显示在浏览器的登陆对话框中 -->
  	<realm-name>Disest authentication</realm-name>
  </login-config>
</web-app>

打开浏览器输入http:/.localhost:8080/SingleUploadServlet/servlet1进行验证,又出现了刚才上面的页面。



14.2.3、基于表单的验证

基本和摘要访问验证都不允许使用定制的登陆表单。如果你必须使用定制的表单,那么可以使用基于表单的验证方法。由于她所传输的值没有进行加密,因此这个应该与SSL结合起来使用。

使用基于表单的验证时,需要创建一个login页面和一个error页面,它们可以是HTML页面,也可以是JSP页面。当第一次请求某一个受保护的资源时,Servlet/JSP容器将会发出Login页面。登录成功后,再发出被请求的资源。但是如果登录失败,用户将会看到Error页面。

使用基于表单的验证时,部署描述符中的auth-method元素值必须设为FORM(全部大写)。此外,login-config元素必须有一个form-login-config元素,它带有两个子元素:form-login-pageform-error-page。下面就是在基于表单的验证中,login-config元素的范例。

<login-config>

<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.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 
	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">
  <display-name></display-name>	
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
  <security-constraint>
  	<web-resource-collection>
  		<web-resource-name>JSP pages</web-resource-name>
  		<!-- 所有的jsp页面都不能被访问 -->
  		<url-pattern>*.jsp</url-pattern>
  	</web-resource-collection>
  	<auth-constraint></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>member</role-name>
  		<role-name>manager</role-name>
  	</auth-constraint>
  </security-constraint>
  
  <login-config>
  	<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>
	
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    <meta http-equiv="description" content="this is my page">
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    
    <!--<link rel="stylesheet" type="text/css" href="./styles.css">-->

  </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>
	
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    <meta http-equiv="description" content="this is my page">
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    
    <!--<link rel="stylesheet" type="text/css" href="./styles.css">-->

  </head>
  
  <body>
    Login failed
  </body>
</html>

打开浏览器继续输入刚才的地址,会出来以下的页面:



客户端证书验证

客户端证书验证是在HTTPS(用SSL加密了的HTTP)上进行的,它要求每一个客户端都要有一个客户端证书。这是一种非常健壮的验证机制,但是不适用于在互联网上部署的应用程序,因为不可能要求每一个互联网用户都拥有一个数字证书。然而,这种验证方法可以用来访问公司内部网的应用程序。

 

15.3SSL

SSLSecure Socket Layer,加密套接字层)协议最早是Netscape公司开发出来的,它可以针对互联网上的通信进行加密,同时确保数据的保密性和完整性。

 

14.3.1、密码学

一开始,人们是利用对称加密进行加密和解密的。在对称加密中,用同一个密钥对信息进行加密和解密。这是一种非常简单的加密/解密技术。

假设,加密方法是用一个加密数字将字母表中的每一个字母都往前移。因此,如果加密数字为2”ThisFriday”经过加密之后就变成了”VjkuHtkfca”。当你读完字母表时,又从头开始,因此y变成了a。接收方知道密钥为2,可以很容易地破解信息。

但是,对称加密要求双方都要提前知道加密/解密的密钥。基于以下原因,对称加密不适用于互联网:

1、交换信息的双方经常是互相不认识的。例如,在XX网上买书时,要将你的个人资料和信用卡资料发过去。如果使用对称加密术,你将不得不在进行交易之前给XX网站打电话,双方先确定以下密钥。

2、大家都希望能够与许多其他方进行通信。如果使用对称加密术,大家只好保留许多不同的唯一密钥,每个不同的通信方一个密钥。

3、由于你不认识即将进行通信的实体,因此必须确认对方是否真的是他们所生成的那个人。

4、在互联网上传递的信息要经过许多不同的计算机,因此要窃取别人的信息是很容易地事情。对称加密不能确保第三方不会篡改数据。

因此,当今互联网上的加密通信是采用非对称加密,它具有以下三个特征:

1、加密/解密。加密过的信息对第三方隐藏了信息。只有既定的接受方才能对信息进行解密。

2、验证。验证时证明某个实体是否为它所声明的那一个。

3、数据完整性。在互联网上发送的信息要传过许多计算机,因此必须确保所发送的数据没有被修改过,并且是完整的。

在非对称加密中,使用的是公钥加密法。在这种加密方法中,数据的加密和解密是通过一对非对称的密钥来实现的,即一个公钥和一个私钥。私钥是私人的,主人必须把它保存在一个安全的地方,绝不能被任何地方占有。公钥则是分发给公众的,通常想要与这些公钥的主人进行通信时,就可以下载到。配对的公钥和私钥可以利用工具来生成。

公钥加密的魅力在于,用公钥加密的数据只能利用对应的私钥才能进行解密;由此类推,用私钥加密的数据也只能用对应的公钥才能进行解密。这种完美的算法是基于非常巨大的质数,由麻省理工学院(MIT)的Ron RivestAdi ShamirLen Adleman发明与1977年。他们将这种算法简单地称作RSA算法,即以它们三个人的姓的首字母组合而成。

 

14.3.2加密/解密

想要交换信息的双方中,必须要有偶乙方拥有一对密钥。假设Alice想要和Bob进行通信。Bob有一个公钥和一个私钥。Bob要将他的公钥发给AliceAlice就可以用它对要发给Bob的信息进行加密。只有Bob能够对这些信息进行解密,因为他拥有对应的私钥。为了将信息发给AliceBob要利用它的私钥进行加密,Alice就可以用Bob的公钥进行解密。

但是,除非Bob能够亲自见到Alice并把它的公钥交给他,否则这种方法就很不完善了,单凡拥有密钥对的人们都可能声称自己是BobAlice根本无法查证。在互联网上,交换信息的双方经常住的相隔遥远,见面通常是不太可能的。

 

14.3.3、验证

SSL中,验证是通过引入证书来解决的。证书包含以下内容:

1、一个公钥

2、相关主题的信息,如公钥的所有者。

3、证书颁发商的名称。

4、某种时间戳,使证书过了一定的时期之后会过期。

证书最为关键的是,必须通过可I型男人的证书颁发商进行数字签名,例如VeriSign或者Thawte,对一个电子文档进行数字签名,就是将你的的签名添加到文档/文件中。原始文件没有进行加密,签名的真正目的是要确保文档/文件没有被篡改,对一个文档进行签名,要包括创建文档的摘要信息,并利用颁发商的私钥对摘要进行加密,为了查看这个文档是否仍然保持原封不动,要执行以下两个步骤:

1、利用签署者的公钥对文档的摘要信息进行解密。你很快就会学到,可信任证书颁发商的公钥是很容易获取到的。

2、给文档创建摘要信息。

3、将上述第一步和第二步的结果进行比较,如果这两个结果匹配,则表示文件没有被篡改。

这种验证方法之所以可行,是因为只有私钥的所有者能够对文档摘要进行解密,并且这个柴窑信息只能通过对应公钥进行解密。加入你能确定自己所有拥有的是原版的公钥,那么就会知道这个文件有没有被修改过。

注意:由于证书可以通过可信任的证书颁发商进行数字签名,因此人们可以将他们的证书供大家公开下载,而不是采用他们的公钥。

证书颁发商会有一对公钥和私钥。申请证书时,Bob必须生成一对密钥,并将其公钥发给证书颁发商,随后证书颁发商就会请Bob发一份护照或者其他身份证明给他,由此证明Bob的身份。对Bob晚上验证后,证书颁发商就会用它的私钥对证书进行签名。“签名”的意思就是加密。因此,证书只能利用证书颁发商的公钥才能解毒。证书颁发商的公钥一般很容易下载到。例如,几乎所有主流浏览器都默认包含几家证书颁发商的公钥。

现在,有了数字证书之后,Bob在与别人交换信息之前,可以先发布他的数字证书,而不是发布他的公钥了。

其工作流程如下:

A->B Bob,你好!我要跟你通话,但我需要先确认以下你是否真实Bob

B->A 当然可以,这是我的证书。

A->B 这些还不够,我还需要你其他的身份证明资料。

B->A Alice,真的是我+[Bob的密钥加密过的信息摘要]

Bob发给Alice的最后一条信息中,也就是用Bob的公钥进行签名的那条消息,可以向Alice证明这条信息是可信的。这就是验证的过程。Alice联系BobBob将其证书发过去。但是,光有证书还不够,因为任何人都可能得到Bob的证书。别忘了,Bob可是将他的证书发给所有想要与他通信的人啊!因此,Bob还要给她发送一条消息(“Alice,真的是我”),并附上曾用其私钥对这条信息进行过加密的摘要。

Alice从证书中获得Bob的公钥。这个她可以做到,因为证书是利用证书颁发商的私钥签名的,Alice可以访问证书颁发商的公钥(她的浏览器上就有一份)。现在,她又收到一条消息,以及用Bob私钥加密过的摘要。Alice只要给这条消息创建一个摘要,并将它与Bob发给她的加密摘要进行对比即可。Alice之所以能够解密,是因为它是用Bob的私钥加密的,而Alice又有一份Bob的公钥。如果两者匹配,Alice就可以确定对方就是真正的Bob

AliceBob完成验证之后,第一件事就是发送一个加密密钥,这个将在接下来的信息交流中使用。使得,一但建立其了加密渠道,SSL就会采用对称加密法,因为它的速度要比不对称加密法快得多。

这里还漏掉了一件事情没有交代。互联网上的信息要在许多计算机上传递的,那么你如何确保哪些信息的完整性呢?因为在传输途中,任何人都可能截取那些信息啊。

 

14.3.4、数据完整性

假设Mallet是一个怀有恶意的人,他很可能就坐在AliceBob之间,试图破解正在传递的消息,令Mallet遗憾的是,虽然他能够复制到信息,但是因为进行过加密,他并不知道密钥。但Mallet仍然可能破坏信息,或者不转播其中的某一个部分。为了解决这个问题,SSL引用了一个消息验证码(Message Authentication CodeMAC)。MAC是通过一个密钥和一些传输数据计算出来的一组数据。由于Mallet不知道密钥,因此他无法算出摘要的正确值,信息的接受者则可以,并且因此可以发现是否有人试图破坏数据,或者发现数据是否不完整。如果答案是肯定的,那么双方可以停止通信。

这类信息摘要算法之一为MD5,是由RSA发明的,非常安全。例如,如果使用128位的MAC值,那么恶意破坏者猜中正确值的几率大约为18 446 744 073 709 551 616分之一,也就是说,几乎永远不可能猜中。

 

14.3.5SSL工作原理

知道了SSL如何解决加密/解密、验证及数据完整性的问题,现在来看一下SSL的工作原理。这次我们以Amazon(代替Bob)和一个买家(代替Alice)为例。就像其他任何合法的电子商务卖家一样,Amazon也向一个可信任的证书颁发商申请了一份证书。买家使用的是IE浏览器,其中嵌有可信任的证书颁发商的公钥。买家并不需要真正了解SSL的工作原理,也不需要拥有公钥和私钥。他只需要确保在输入重要的资料时,例如信用卡号码时,用HTTPS代替HTTP协议即可。这个必须显示在地址栏中的。

当买家输入一个加密页面(当他完成购物时),在后台中,这个浏览器和Amazon服务器之间会发生以下一系列事件。

浏览器:你真的是Amazon.com吗?

服务器:是的,这是我的证书。

然后,浏览器会利用证书颁发商的公钥进行解密,来验证证书的有效性。如果有哪里不对,例如证书过期,浏览器就会向用户发出警告。如果用户不顾证书过期,同意继续,浏览器就会继续。

浏览器:光有证书还不够,请再发点别的身份证明给我。

服务器:我真的是Amazon.com+[这条信息用Amazon.com的私钥加密过的摘要]
浏览器利用Amazon的公钥对消息摘要进行解密,并未“我真的是Amazon.com”创建一条消息摘要。如果两者匹配,表示验证成功。随后,浏览器就会产生一个随机密钥,利用Amazon的公钥对它进行加密。这个随机密钥将用于对后续的消息进行加密和解密,换句话说,一旦完成对Amazon的验证,就会采用对称加密,因为它的速度比飞对称加密要快得多。除了消息之外,双方还要发送消息摘要,以确保这些消息是完整的,没有被修改过。

 

14.4、通过编程确保安全性

尽管声明式安全简单易用,但偶尔还是需要通过编写代码来确保应用程序的安全性。为此,可以在HttpServletRequest接口中使用安全注解类型和方法。这两者都在下面进行讨论。

 

14.4.1、安全注解类型

javax.servlet.annotation包中与安全相关的3个注解类型是:ServletSecurityHttpConstraintHttpMethodConstraint

ServletSecurity注解可以带有valuehttpMethodConstraints属性。

httpConstraint注解类型用来定义一个安全约束,并且只能赋给ServletSecurity注解的value属性。如果包含的ServletSecurity注解中没有出现httpMethodConstraints属性,那么HttpConstraint注解所强加的安全约束将应用于所有的HTTP方法。否则,安全约束将只应用于httpMethodConstraints属性中定义的HTTP方法。例如,以下HttpConstraint注解表示所注解的Servlet只能由manager角色中的访客进行访问。

@ServletSecurity(value = @HttpConstraint(rolesAllowed = “manager”))

当然,上述注解也可以改写成:

@ServletSecurity(@HttpConstraint(rolesAllowed = “manager”))

但仍然需要在部署描述符中声明一个login-config元素,以便容器能够对用户进行验证。

将一个HttpConstraint注解的transportGuarantee属性设置为TransportGuarantee.CONFIDENTIAL,将使这个Servlet只能通过某个秘密渠道进行访问,例如SSL

@ServletSecurity(@HttpConstraint(transportGuarantee = TransportGuarantee.CONFIDENTIAL))

如果Servlet/JSP容器通过HTTP接收到针对这种Servlet的请求,它会将浏览器重定向到该URLHTTPS版本。

HttpMethodConstraint注解类型用于定义某个安全约束索要应用到的HTTP方法,它只能放在ServletSecurity注解的httpMethodConstraints属性值数组中。例如,下面的httpMethodConstraint注解限制了只有manager角色的用户可以通过HTTP Get访问被注解的Servlet。对于其他的HTTP方法,则不存在任何限制。

@ServletSecurity(httpMethodConstraints = {@HttpMethodConstraint(value = “GET”, rolesAllowed = “manager”)})

注意,如果HttpMethodConstraint注解中没有出现rolesAllowed属性,那么所定义的HTTP方法将不受任何限制。例如,以下的ServletSecurity注解中使用了valuehttpMethodConstraints这两个属性。

@ServletSecurity(value = @HttpConstraint(rolesAllowed = “manager”),

httpMethodConstraints = {@HttpMethodConstraint(“GET”)}

)

但是,如果HttpMethodConstraint注解类型的emptyRoleSemantic属性值为Empty-RoleSemantic.DENY,那么该方法将限制所有用户访问。例如,用下列ServletSecurity进行注解的Servlet,将禁止通过Get方法进行访问,但是云溪member角色中的所有用户通过其他的HTTP方法进行访问。

@ServletSecurity(value = @HttpConstraint(rolesAllowed = “member”),

httpMethodConstraints = {@HttpMethodConstraint(value = “GET”,

emptyRoleSemantic = EmptyRoleSemantic.DENY

)}

)

 

14.4.2Servlet安全API

除了利用注解类型,也可以在HttpServletRequest接口中利用以下方法来实现编程式的安全性。

java.lang.String getAuthType()

返回用来保护Servlet的验证方案,如果该Servlet中没有应用任何安全约束,则返回null

java.lang.String getRemoteUser()

返回发出该请求的用户的登录名,如果该用户未经验证,则返回null

boolean isUserInRole(java.lang.String role)

返回一个布尔值,表明该用户是否属于规定的角色。

java.lang.Principal getUserPrincipal()

返回一个包含当前被验证用户详细信息的java.security.Principal,如果该用户未经验证,则返回null

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.java

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 = 1L;
	
	@Override
	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 {
			System.out.println("User not authenicated");
		}
	}
}
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
  <login-config>
  	<auth-method>DIGEST</auth-method>
  	<realm-name>Digest authentication</realm-name>
  </login-config>
</web-app>
当用户第一次请求该Servlet时,用户还没有进行验证,验证方法将返回false。因此,Servlet/JSP容器会发出一个WWW-Authenticate标头,使浏览器显示一个登录对话框,进行摘要访问验证。当用户提交填有正确用户名和密码的表单时,authenticate方法返回true,并显示欢迎消息。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值