Web容器安全管理(下)——容器基本身份验证
来源:互联网 发布:圣思园java视频下载 编辑:程序博客网 时间:2024/05/17 22:36
为了更好地了解并实现Web容器的安全管理,笔者以两篇博客的篇幅来介绍,即:《Web容器安全管理(上)——Java EE的安全概念》 和 《Web容器安全管理(下)——容器基本身份验证》。上篇博客已经介绍了Java EE安全的基本概念,打下了基础。在本文,我们详述Web容器提供的基本身份验证方式。
1、容器声明式基本身份验证
假设你已经开发好了应用程序,现在想针对几个页面进行保护,只有通过身份验证且具备足够权限的用户,才可以浏览这些页面。这个需求有几个部分必须实现:
(1)身份验证的方式
(2)授予访问页面的权限
(3)定义用户
这里采用Web容器提供的最简单的基本(Basic)验证,在访问藉此受保护的资源时,浏览器会弹出对话框要求输入用户名和密码。如下图所示,是chrome弹出的身份验证对话框。
使用Web容器提供的基本身份验证功能,需要在应用程序的web.xml中定义:
<login-config> <auth-method>BASIC</auth-method></login-config>
接着要授予指定角色访问页面的权限,所以要先定义角色,在授权之前,必须在应用程序中,定义角色名称。可以在web.xml中如下定义:
<security-role> <role-name>admin</role-name></security-role><security-role> <role-name>manager</role-name></security-role>
在这里定义了admin与manager两个角色名称。接着定义哪些URL可以被哪些角色以哪种HTTP方法访问。例如设置/admin下所有页面,无论使用哪个HTTP方法,都只能被admin角色访问:
<security-constraint> <web-resource-collection> <web-resource-name>Admin</web-resource-name> <url-pattern>/admin/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>admin</role-name> </auth-constraint></security-constraint>
如果有多个角色可以访问某些页面,则<auth-constraint>标签可以设置多个<role-name>标签。在这里看不到任何HTTP方法规范的定义,默认就是所有HTTP方法都受到限制。再来看另一个例子:
<security-constraint> <web-resource-collection> <web-resource-name>Manager</web-resource-name> <url-pattern>/manager/*</url-pattern> <http-method>GET</http-method> <http-method>POST</http-method> </web-resource-collection> <auth-constraint> <role-name>admin</role-name> <role-name>manager</role-name> </auth-constraint></security-constraint>
在这个设置中,对于/manager下的所有页面,根据&http-method>的设置,只有admin或manager才可以使用GET与POST方法进行访问。请留意这个语义“只有admin或manager才可以使用GET与POST方法进行访问”,这表示,其他HTTP方法,如PUT、TRACE、DELETE、HEAD和OPTIONS等,无论是否具备admin或manager角色,都可以访问!
若没有设置<http-method>,则所有HTTP方法都受到限制。设置了<http-method>,则只有被设置HTTP受到限制,其他方法不受限制。如果没有设置<auth-constraint>标签,或<auth-constraint>标签中设置了<role-name>*</role-name>,表示任何角色都可以访问。如果直接编写了<auth-constraint/>,那就没有任何角色可以访问了。
下面是一个完整的设置范例:
<?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/javaeehttp://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"> <display-name>SecurityBasicDemo</display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <session-config> <!-- 在工程的web.xml文件中设置session失效时间,时间单位为分钟 --> <!-- Tomcat默认session超时时间为30分钟,可以根据需要修改,负数或0为不限制session失效时间。--> <session-timeout>30</session-timeout> <!--代码中设置 session.setMaxInactiveInterval(30*60); 以秒为单位--> </session-config> <security-constraint> <web-resource-collection> <web-resource-name>Admin</web-resource-name> <url-pattern>/admin/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>admin</role-name> </auth-constraint> </security-constraint> <security-constraint> <web-resource-collection> <web-resource-name>Manager</web-resource-name> <url-pattern>/manager/*</url-pattern> <http-method>GET</http-method> <http-method>POST</http-method> </web-resource-collection> <auth-constraint> <role-name>admin</role-name> <role-name>manager</role-name> </auth-constraint> </security-constraint> <login-config> <auth-method>BASIC</auth-method> </login-config> <security-role> <role-name>admin</role-name> </security-role> <security-role> <role-name>manager</role-name> </security-role></web-app>
就Web应用程序的设置部分,工作已经结束!但在将应用程序部署至服务器时,在服务器上设置角色与用户或组的对应,设置的方式并非Java EE的标准,而是各服务器都有所不同。例如在Tomcat中,可以在/conf/tomcat-users.xml中定义:
<tomcat-users version="1.0" xmlns="http://tomcat.apache.org/xml"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd"> <role rolename="manager"/> <role rolename="admin"/> <user username="caterpillar" password="123456" roles="admin,manger"/> <user username="momor" password="654321" roles="manager"/></tomcat-users>
要启用Tomcat的安全管理功能,还必须在Server Options中选取Enable security,才会读取tomcat-users.xml中的设置信息。
在这个设置中caterpillar同时具备admin与manager角色,而momor则具备manager角色。在启动应用程序之后,如果访问/admin或/manager,就会出现对话框要求输入名称、密码。如果输入错误,就会被一起要求输入直到正确为止。
如果访问/admin下的页面,只有输入了caterpillar名称及正确的密码,才可以正确浏览到页面。如果输入了momor名称及正确的密码,会提示权限不足,拒绝访问。
上面虽然输入了momor名称及正确的密码,通过了浏览器的身份验证,但授权失败,弹出403画面。
tomcat-user.xml是Tomcat预设的Realm(不知道什么是Realm,请参看上一篇博文),角色、用户名称、密码都存储在这个xml文件中。你也可以改用数据库表格,这需要额外配置。
2、容器声明式基本身份验证的原理
在初次请求某个受保护的URL时,容器会检查请求中是否包括Authorization标头,如果没有的话,则容器会响应401 Unauthorized的状态码与信息,以及WWW-Authenticate标头给浏览器,浏览器收到WWW-Authenticate标头之后,就会出现对话框要求用户输入名称及密码,原理如下图所示:
如果用户在对话框中输入名称、密码后按下确定键,则浏览器会将名称密码以BASE64方式编码,然后放在Authorization标头中送出。容器会检查请求中是否包括Authorization标头,并验证名称、密码是否正确,如果正确,就将资源传送给浏览器。如下图所示:
BASE64是将二进制的字节编码为ASCII序列的编码方式,在HTTP中可用来传送内容较长的数据。编码并非加密,只要译码方式正确,就可以取得原本的信息。
接下来在关闭浏览器之前,只要是对服务器资源的请求,每次都包括Authorization标头,而服务器每次也都会检查是否有Authorization标头,所以登录期间会一起持续到关闭浏览器为止。如下图所示,为基本身份验证的流程图。
由于使用的是浏览器提供的对话框输入名称、密码,所以基本身份验证时无法自定义登录画面。由于传送名称、密码时使用的是Authenticate标头,无法设计注销机制,关闭浏览器是结束会话的唯一方式。
3、容器声明式窗体身份验证
如果需要自定义登录页面,以及登录错误的页面,则可以改用容器所提供的窗体(Form)验证。要将之前的基本身份验证改为窗体验证的话,可以在web.xml中修改<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>//略...
在<auth-method>的设置从BASIC改为FORM。由于使用了窗体网页进行登录,所以必须告诉容器,登录页面是哪个,登录失败页面是哪个。这是由<form-login-config>标签对来设置,设置时必须以斜杠开始,也就是从应用程序根目录开始的URL路径。
接下来就可设置自己的窗体登录页面,但必须注意!窗体发送的URL必须是j_security_check,发送名称的请求参数必须是j_username,发送密码的请求参数必须是j_password。以下是login.html的简单示例:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>登录页面</title></head><body> <form action="j_security_check" method="post"> 名称:<input type="text" name="j_username"><br> 密码:<input type="password" name="j_password"><br> <input type="submit" value="送出"/> </form></body></html>
error.html的简单示例:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>登录失败网页</title></head><body> <h1>用户名或者密码错误,登录失败</h1> <a href='login.html'>返回登录页面</a></body></html>
登录时的页面如图所示:
登录失败时的页面如图所示:
4、容器窗体身份验证的原理
来了解一下容器利用窗体进行验证的原理。当使用窗体身份验证时,如果要访问受保护的资源,容器会检查用户有无登录,方式是查看HttpSession中有无”javax.security.auth.subject“属性,若没有这个属性,则表示没有经过容器的验证流程,则转发至登录页面,用户输入名称、密码并发送后,若验证成功,则容器会在HttpSession中设置属性名称”javax.security.auth.subject“的对应值javax.security.auth.subject实例。具体的流程如下图所示:
用户是否登录是通过HttpSession的”javax.security.auth.subject“属性来判断,所以要让此次登录失败,可以调用HttpSession的invalidate()方法,因此窗体验证时可以设计注销机制。
除了基本身份验证与窗体验证之外,在<auth-method>中还可以设置DIGEST或CLIENT-CERT。
DIGEST即所谓”摘要验证“,浏览器也会出现对话框输入名称、密码,而后通过Authorization标头传送,只不过并非使用BASE64来编码名称、密码。浏览器会直接传送名称,但对密码则先进行(MD5)摘要演算(非加密),得到理论上唯一且不可逆的字符串再传送,服务器根据名称从后端取得密码,以同样的方式作摘要演算,再比对浏览器送来的摘要字符串是否符合,如果符合就验证成功。由于网络上传送的并不是真正的密码,而不是不可逆的摘要,密码不会被得知,理论上比较安全。不过Java EE规范中并无要求一定得支持DIGEST的验证方式(看厂商的需要,Tomcat是支持的)。
CLIENT-CERT也是用对话框的方式来输入名称与密码,因为使用PKC(Public Key Certificate)作加密,可保证数据传送时的机密性及完整性,但客户端需要安装证书(Certificate),在一般用户及应用程序之间并不常采用。
5、编程式安全管理
Web容器的声明式安全管理,仅能针对URL来设置哪些资源必须受到保护,如果打算依据不同的角色在同一个页面中设置可访问的资源,例如只有站长或版面管理员可以看到删除整个讨论组的功能,普通用户不行,那么显然无法单纯使用声明式安全管理来实现。
在Servlet3.0中,HttpServletRequest新增了三个与安全有关的方法:authenticate()、login()、logout()。
首先来看authenticate()方法,搭配先前的声明式身份验证的web.xml的设置,你可以决定程序中哪一段逻辑,只有通过了容器身份验证的用户才可以看到。
package cc.openhome;import java.io.IOException;import java.io.PrintWriter;import java.security.AccessControlException;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( name="SecurityServlet", urlPatterns = { "/security" } )public class SecurityServlet extends HttpServlet { private static final long serialVersionUID = 1L; public SecurityServlet() { super(); // TODO Auto-generated constructor stub } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); out.println("任意其他用户就可以看到的数据一<br>"); try { request.authenticate(response); out.println("<h1>必须由容器验证通过的用户才可以看到的数据</h1><br>"); } catch (AccessControlException e) { e.printStackTrace(); } out.println("任意其他用户就可以看到的数据二<br>"); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub doGet(request, response); }}
authenticate()方法会检查用户是否已经通过了容器验证,否则根据web.xml中的设置,要求进行身份验证,若通过验证,则可显示接下来的内容。
login()在调用时则可以提供用户名称、密码,利用容器设置的身份验证信息来进行验证。例如,以下Servlet只有在提供的username、password请求参数正确时,才可以看到相应的数据。
package cc.openhome;import java.io.IOException;import java.io.PrintWriter;import java.security.AccessControlException;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(name = "SecuityLoginServlet", urlPatterns = { "/securityLogin" })public class SecuityLoginServlet extends HttpServlet { private static final long serialVersionUID = 1L; public SecuityLoginServlet() { super(); // TODO Auto-generated constructor stub } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); out.println("任意其他用户就可以看到的数据一<br>"); try { String user = request.getParameter("user"); String passwd = request.getParameter("passwd"); request.login(user, passwd); out.println("<h1>必须由容器验证通过的用户才可以看到的数据</h1><br>"); } catch (AccessControlException e) { e.printStackTrace(); } finally { request.logout(); } out.println("任意其他用户就可以看到的数据二<br>"); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub doGet(request, response); }}
在浏览器的URL地址栏需要输入user与passwd参数,若参数通过验证,则可显示接下来的内容。
- Web容器安全管理(下)——容器基本身份验证
- web容器安全管理
- Web容器安全管理(上)——Java EE的安全概念
- Servlet&JSP 第十章 Web容器安全管理
- STL容器(一) 基本序列容器
- 面试2-容器(基本容器、并发容器)
- web容器 & Servlet容器
- STL容器 — 顺序容器
- 基于容器的用户安全管理系统
- [基本实验] web容器解析漏洞
- web容器
- web 容器
- web容器
- Web容器
- web容器
- web 容器
- web容器
- Web容器
- migrate 和makemigrations 命令
- iOS tableViewCell点击后显示下拉菜单
- TP获取操作日志需要的值(ip、当前页面、全部get、post值)
- ETL的经验总结
- java-用加减法显示验证码
- Web容器安全管理(下)——容器基本身份验证
- 页面中字符串超出后的解决方案
- golang 使用defer、panic、recover的问题
- Android短信倒计时功能的实现
- Android设置禁止截屏
- for 循环嵌套性能
- 大型网站技术架构_读后感
- 括号匹配
- solr启动失败404的原因