(1)Web容器对于以下的四个基本安全特性提供了基础:
验证:身份验证,也就是确认目前沟通的对象(号称自己有访问权的对象),真的是自己所宣称的用户或身份。
资源访问控制:基于完整性、机密性、可用性限制等目的,对资源的访问必须设限,仅提供一些特定的用户或程序。
数据完整性:在信息传输期间,必须保证信息的内容不被第三方修改。
数据机密性或私密性:只允许具有合法权限的用户访问特定的数据。
在Java EE中,容器(无论是Web容器还是EJB容器)提供了这些需求的实现,这些实现是Java EE的标准,只要是符合Java EE规范的容器,都可以使用这些实现。
(2)几个Java EE的名词与概念:
用户(User):允许应用程序服务的合法个体(也许是一个人或是一台机器),简单地说,应用程序会定义用户列表,要使用应用程序服务必须先通过身份验证称为用户。
组(Group):为了方便管理用户,可以将多个用户定义在一个组中加以管理,例如,普通用户组、系统管理组、应用程序管理组等,通常一个用户可以同时属于多个组。
角色(Role):Java应用程序许可证管理的依据。用户是否可以存取某些资源,所凭借的是用户是否具备某种角色。组是系统上管理用户的方式,角色是Java应用程序中管理授权的方式。
Realm:存储身份验证时所需数据的地方。如果进行身份验证的方式是基于名称和密码,则储存名称及密码的地方就称为Realm,这也许来自文件,或是数据库中的用户表格,也可能是内存中的数据,甚至来自网络。验证的方式不仅是基于名称和密码,也有可能是基于证书之类的机制,这时提供整数的源就是Realm。
(3)使用Web容器安全管理,基本上可以提供两个安全管理的方式:声明安全与编程安全。
声明安全:可在配置文件中声明哪些资源是只有合法授权的用户才可以访问,在不修改应用程序源代码的情况下,就可以为应用程序加上安全管理机制,这就是所谓的声明安全。
编程安全:在程序代码中的编写逻辑根据不同权限的用户,给予不同懂得操作功能。
(1)假设已经开发好应用程序,现在想针对几个页面进行保护,只有通过身份验证且具备足够权限的用户,才可以浏览这些页面,这个需求有几个部分必须实现:身份验证的方式(采用最简单的基本验证)、授予访问页面的权限、定义用户。
(2)如果打算让Web容器提供基本身份验证的功能,则可以在web.xml中定义。接着要授予指定角色访问页面的权限,所以要先定义角色,可以在web.xml中定义。
在初次请求某个受保护的URL时,容器会检查请求中是否包括Authorization标头,如果没有的话,则容器会响应401 Unauthorized的状态码与信息,以及WWW-Authenticate标头给浏览器,浏览器收到WWW-Authenticate标头之后,就回出现对话框要求用户输入名称及密码。
如果用户在对话框中输入名称、密码后按下确定键,则浏览器会将名称、密码以BASE64(将二进制的字节编码为ASCII序列的编码方式)方式编码,然后放在Authorization标头总送出。容器会检查请求中是否包括Authorization标头,并验证名称、密码是否正确,如果正确,就将资源传送给浏览器。
接下来在关闭浏览器之前,只要是对服务器的请求,每次都会包括Authorization标头,而服务器每次也都会检查是否有Authorization标头,所以登录有效期会一直持续到关闭浏览器为止。
由于是使用对话框输入名称、密码,所以使用基本身份验证时无法自定义登录画面(只能使用浏览器的弹出对话框)。由于传送名称、密码时是使用Authorization标头,无法设计注销机制,关闭浏览器是结束会话的唯一方式。
(1)如果要自定义登录的画面,以及登陆错误时的画面,则可以改用容器所提供窗体验证,要将之前的基本身份验证改为窗体验证的话,可以在web.xml中修改
(2)窗体发送的URL必须是j_security_check,发送名称的请求参数必须是j_username,发送密码的请求参数必须是j_password。
(1)当使用窗体验证时,如果要访问受保护的资源,容器会检查用户有无登录,方式是查看HttpSession中有无“javax.security.auth.subject”属性,若没有这个属性,则表示没有经过容器验证流程,则转发至登录页面,用户输入名称、密码并发送后,若验证成功,则容器会在HttpSession中设置属性名称“javax.security.auth.subject”的对应值javax.security.auth.Subject实例。
(2)用户是否登录是通过HttpSession的“javax.security.auth.subject”属性来判断,所有要让此次登录失败,可以调用HttpSession的invalidate()方法,因此在窗体验证时可以设计注销机制。
(3)除了基本身份验证与窗体验证之外,在
DIGEST即所谓的“摘要验证”,浏览器也会出现对话框输入名称、密码,而后通过Authorization标头传送,只不过并非使用BASE64来编码名称、密码。浏览器会直接传送名称,但对密码则先进行(MD5)摘要演算(非加密),得到理论上唯一且不可逆的字符串再传送,服务器端根据名称需哦那个后端取得密码,以同意的方式作摘要演算,再比对浏览器送来的摘要字符是否符合,如果符合就验证成功。
CLIENT-CERT也是用对话框的方式来输入名称与密码,因为使用PKC作加密,可保证数据传送时的机密性及完整性,但客户端需要安装证书,不适用于普通用户及应用程序之间的数据传送,在一般用户以及应用程序之间并不常使用。
(1)在身份验证的四种方式中,BASIC、FORM、DIGEST都无法保证数据的机密性与完整性。
(2)通常Web应用程序要在传输过程中保护数据,会采用HTTP over SSL,也就是俗称的HTTPS。在HTTPS中,服务器端会提供证书来证明自己的身份及提供加密用的公钥,而浏览器会利用公钥加密信息再传送给服务器端,服务器端在用对应的私钥进行解密以获取信息,客户端本身不用安装证书,因此在保护数据传送上是最常用的方式。
(3)如果要使用HTTPS来传输数据,则只要在web.xml中需要安全传输的
(4)若服务器有支持SSL且安装好证书,当你请求受保护的资源时,服务器会要求浏览器重定向使用https。服务器必须支持SLL并安装证书。
(1)Web容器的声明式安全管理,仅能针对URL来设置哪些资源必须受到保护,如果打算依据不同的角色在同一个页面中设置可存取的资源,无法单纯使用声明式安全管理来实现。
(2)HttpServletRequest安全有关的方法:authenticate()、login()、logout()、getUserPrincipal()、getRemoteUser()、isUserInRole()。
authenticate()会检查用户是否已通过容器验证,否则根据web.xml中的设置,要求进行身份验证,若通过验证,则可显示接下来的内容。
login()在调用时则可以提供用户名称、密码,利用容器设置的身份信息来进行验证。
getUserPrincipal()与EJB组件的沟通有关。
getRemoteUser()可以取得用户登录名称(如果验证成功的话)或是返回null(如果没有验证成功的用户)。
isUserInRole(),可以传给它一个角色名称,如果登录的用户属于该角色,则返回true,否则返回false(没有登录就调用也会返回false)。
(1)除了在web.xml中设置
(2)如果没有任何web.xml的额外设置验证方式,则默认使用基本(BASIC)验证。