您的位置:首页 > 运维架构 > Tomcat

Tomcat容器管理安全的几种验证方式

2016-12-14 14:59 791 查看
https://my.oschina.net/itblog/blog/678845?fromerr=mnnjJA0O

摘要: 本文介绍如何使用容器(这里指tomcat)来进行安全管理。

当访问服务器中受保护的资源时,容器管理的验证方法可以控制确认用户身份的方式。Tomcat支持四种容器管理的安全防护,它们是:

BASIC(基本验证):通过HTTP验证,需要提供base64编码文本的用户口令
DIGEST(摘要验证):通过HTTP验证,需要提供摘要编码字符串的用户口令
FORM(表单验证):在网页的表单上要求提供密码
CLIENT-CERT(客户端证书验证):以客户端证书来确认用户的身份

 基本验证

当web.xml文件中的auth-method元素设置为BASIC时,表明应用使用的是基本验证,每次浏览器请求受保护的Web应用资源时,Tomcat都会使用HTTP基本验证向浏览器索取用户名和密码(以页面弹窗的方式)。使用这种验证方法,所有的密码都会以base64编码的文本在网络上传输。

先看下项目结构(我用Maven管理的依赖):



其中,protect/protect.jsp是被保护的,需要授权访问。

说明:本文提到的tomcat-users.xml,server.xml等文件,如果是在Eclipse中启动tomcat,则这些文件在Eclipse中的Servers工程下对应的tomcat下,如图:



本文提到的web.xml是指项目自己的web.xml,而非Servers项目下Tomcat中的web.xml

web.xml

<security-constraint>
<web-resource-collection>
<http-method>GET</http-method>
<web-resource-name>tomcat protect page</web-resource-name>
<!-- /protect目录下的所有资源是受保护的 -->
<url-pattern>/protect/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<!-- 这里的member要与tomcat-user.xml中配置的role一致 -->
<role-name>member</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<!-- 验证方式,可选的值为: "BASIC", "DIGEST", "FORM", "CLIENT-CERT" -->
<auth-method>BASIC</auth-method>
<!-- 使用的Realm名字,注意这里不能有空格 -->
<realm-name>MyConstraints</realm-name>
</login-config>

tomcat-user.xml(注意如果是在Eclipse中启动tomcat,这个tomcat-user.xml在Eclipse中的Servers工程下)

<role rolename="member"/>
<!-- member角色下有一个叫alvis的用户,密码为pwd -->
<user username="alvis" password="pwd" roles="member"/>

重启tomcat后,访问protect目录下的资源,情况是这样的:



输入账户alvis,密码pwd后,访问成功(当然,非protect目录下的资源是可以直接访问的):



摘要验证

当web.xml文件中的auth-method元素设置为DIGEST时,表明应用使用的是摘要验证。还是上面的例子,看配置:

web.xml和基本验证一样,只是auth-method修改为DIGEST,此处不赘述。

server.xml中的UserDatabaseRealm(如果tomcat使用的是其他Realm,也一样的)里增加digest属性:



接下来,要生成tomcat可识别的MD5密码。方式有两种,正如官网描述:

To calculate the digested value of a cleartext password, two convenience techniques are supported:

If you are writing an application that needs to calculate digested passwords dynamically, call the static 
Digest()
method of the 
org.apache.catalina.realm.RealmBase
 class, passing the cleartext password
and the digest algorithm name as arguments. This method will return the digested password.
If you want to execute a command line utility to calculate the digested password, simply execute
CATALINA_HOME/bin/digest.[bat|sh] -a {algorithm} {cleartext-password}

and the digested version of this cleartext password will be returned to standard output.

方式一:用代码来生成:

import org.apache.catalina.realm.RealmBase;

public class T {
public static void main(String[] args) {
//参数1:要加密的字符串;参数2:加密算法;参数3:字符串的编码
String base = RealmBase.Digest("alvis:MyConstraints:pwd", "MD5", null);
System.out.println(base);
}
}

由于RealmBase类在catalina.jar包中,如果项目中没有这个类,可在项目上右键-->Java Build Path--> Libraries-->Add Library-->选择Server Runtime-->选择Apache Tomcat V8.0(其实7.0也行),如图:



方式二:用脚本来生成:

在tomcat/bin目录下有个digest.sh(Linux系统)或digest.bat(Windows系统)脚本,运行这个脚本,传入摘要算法和参数即可,这里我在Windows系统上运行,如图:



这里的-a指定摘要算法为MD5,要特别注意这里的参数是:{用户名}:{Realm名}:{密码明文}。用户名就是tomcat-users.xml中配置的<user>名字(这里为alvis),Realm名是在web.xml中配置的<realm-name>(这里为MyConstraints),密码明文即该用户用于登录的密码(我这里设为pwd)。

只有这样的参数加密后的密码,在tomcat-users.xml中配置才有效,否则是登录不了的。由于我是参考《Tomcat权威指南(第二版)》的步骤做的,之前试了很久都不知道为什么登录不了,结果在官网找到答案,是这么描述的:

If using digested passwords with DIGEST authentication, the cleartext used to generate the digest is different and the digest must use the MD5 algorithm. In the examples above 
{cleartext-password}
 must
be replaced with
 
{username}:{realm}:{cleartext-password}
. For example, in a development environment this might take the form 
testUser:Authentication required:testPassword
. The value for 
{realm}
 is
taken from the 
<realm-name>
 element of the web application's 
<login-config>
. If not specified in web.xml, the default value of 
Authentication required
 is used.

大意是说,如果使用DIGEST方式验证,用于生成摘要的明文必须被替换为这种格式。实践出真知,所以还是不能完全看书啊,动手实践才是实在的。

然后就是在tomcat-users.xml中配置生成的密码(通过下方的截图,可以比较password跟上方digest.bat脚本生成的密码是否一致):



之后重启tomcat,效果自然是跟使用基本验证的效果一样了。

表单验证

当web.xml文件中的auth-method元素设置为FORM时,表明应用使用的是表单验证。当用户请求Web应用程序受保护的资源时,表单验证会跳转至配置的登录页面。当登录失败时,还需要一个验证失败的页面,还是上面的例子,看配置:

web.xml

<security-constraint>
<web-resource-collection>
<http-method>GET</http-method>
<web-resource-name>tomcat member part</web-resource-name>
<url-pattern>/protect/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>member</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>FORM</auth-method>
<realm-name>MyConstraints</realm-name>
<form-login-config>
<form-login-page>/form/login.html</form-login-page>
<form-error-page>/form/error.html</form-error-page>
</form-login-config>
</login-config>

这里的form/login.html是用于登录的页面,而form/error.html则是验证失败后跳转到的页面(这两个页面在上方的工程结构图中已经有了)。

login.html

<html>
<body>
<h2>Login Page.</h2>

<form method="post" action="j_security_check" name="loginForm">
<input type="text" name="j_username" /><br>
<input type="password" name="j_password" /><br>
<input type="submit" value="Login" />
</form>

</body>
</html>

注意:这里form的action="j_security_check",账号的name="j_username"和密码的name="j_password"都是不可变的,否则配置的验证规则不起作用。

server.xml中,要去掉Realm中添加的“digest=MD5”这个属性:



tomcat-users.xml中使用明文保存密码:



效果(仅在访问protect目录下的资源时才出现Login Page):



输入错误的账号和密码,跳转至form/error.html页面:



输入正确的账号和密码,跳转至受保护的页面:



客户端证书验证

待续

 

Demo下载:

链接: http://pan.baidu.com/s/1gfnqVdT 密码: pubw

参考页面:

https://tomcat.apache.org/tomcat-7.0-doc/realm-howto.html

==========================================================

HTTP 验证 Tomcat中进行基本验证 (Basic Authentication) 和摘要验证 (Digest Authentication)

HTTP 验证

HTTP 协议提供验证机制来保护资源。当一个请求要求取得受保护的资源时,网页服务器回应一个 401 Unauthorized error 错误码。这个回应包含一个指定了验证方法和领域的 WWW-Authenticate 头信息。把这个领域想像成一个存储着用户名和密码的数据库,它将被用来标识受保护资源的有效的用户。比如,你试着去访问某个网站上标识为“Personal Files”的资源,服务器响应可能是:WWW-Authenticate:
Basic realm="Personal Files" (假设验证方法是 Basic)。

验证方法

现在有几种用于网络应用的验证方法,其中最广泛使用的是基本验证 (Basic Authentication) 和摘要验证 (Digest Authentication)。

当用户想要访问有限的资源时,使用基本验证方法的网页服务器会要求浏览器询显示一个对话框,并要求用户输入用户名和密码。如果用户输入的用户名和密码正确,服务器就允许他访问这些资源;否则,在接连三次尝试失败之后,会显示一个错误消息页面。这个方法的缺点是用户名和密码都是用 Base64 编码 (全是可读文本) 之后传输过去的。也就是说,这个验证方法的安全程度只是和 Telnet 一样,并不是非常安全。

数据验证方法不会在网络中传输密码,而是生成一些数字 (根据密码和其它一些需要的数据产生的) 来代替密码,而这些数字是经过 MD5 (Message Digest Algorithm) 加密的。生成的值在网络有随着服务器需要用来校难密码的其它信息一起传输。这个方法明显更为安全。

基于表单的验证方法和基本验证方法类似,只是服务器使用你自定义的登录页面来代替了标准的登录对话框。

最后,客户证书验证使用 SLL (Secure Socket Layer,安全套接层) 和客户证明。

在 Tomcat 下保护资源

你可以在 tomcat-users.xml 文件中写一个用户及其角色的列表。这个文件在 TOMCAT_HOME (你安装 Tomcat 的目录) 下的 conf 目录中。这个文件默认包含了三个用户 (tomcat、role1、both) 的定义。下面一段 XML 代码是我添加了两个新用户 (qusay 和 reader) 之后的 tomcat-users.xml:

<tomcat-users>
<user name="tomcat" password="tomcat" roles="tomcat" />
<user name="role1" password="tomcat" roles="role1" />
<user name="both" password= "tomcat" roles="tomcat,role1" />
<user name="qusay" password="guesswhat" roles="author" />
<user name="reader" password="youguess" roles="reader" />
</tomcat-users>

新添加的两个用户 (qusay 和 reader) 的 roles 分别设置为 author 和 reader。角色属性非常重要,因为当你创建安全规则的时候,每个受限制的资源都是与可访问它的角色相关联 (稍后你会看到)。

下面做个实验 (假设你已经安装并配置好了 Tomcat)。为你期望的页应用程序建立一个目录。可以按下列步骤做好准备:

1. 在你安装了 Tomcat 的目录下,有一个目录是 webapps。在这个目录下建立一个目录 (如:learn)。 
2. 在第一步建立的目录下建立一个子目录,命名为 chapter。 
3. 在 chapter 目录中,建立一个 HTML 文件,内容自定,文件名为 index.html。 
4. 在第一步建立的目录下建立一个名为 WEB-INF 的子目录。 
5. 在 WEB-INF 目录中创建一个名为 web.xml 的文件,该文件内容如下: 

6. <?xml version="1.0" encoding="ISO-8859-1"?>
7.
8. <!DOCTYPE web-app
9. PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
10. "http://java.sun.com/dtd/web-app_2_3.dtd">
11.
12. <web-app>
13.
14. <description>
15. Learning Web Programming
16. </description>
17.
18. <security-constraint>
19. <web-resource-collection>
20. <web-resource-name>
21. Restricted Area
22. </web-resource-name>
23. <url-pattern>/chapter/*</url-pattern>
24. </web-resource-collection>
25. <auth-constraint>
26. <role-name>tomcat</role-name>
27. <role-name>author</role-name>
28. <role-name>reader</role-name>
29. </auth-constraint>
30. </security-constraint>
31.
32. <login-config>
33. <auth-method>BASIC</auth-method>
34. <realm-name>Authenticate yourself</realm-name>
35. </login-config>
36.
</web-app>

web.xml 配置描述

web.xml 是描述配置的文件,这里集中说明一下安全相关的配置元素。

<security-constraint>:这个元素限制对一个或者多个资源的访问,可以在配置信息中出现多次。上面的配置信息中,它限制了 chapter 目录 (http://localhost:8080/learn/chapter) 下所有资源的访问。<security-constraint> 包含了下列元素:
o <web-resource-collection>:这个元素用于标识你想限制访问的资源。你可以定义 URL 模式 和 HTTP 方法 (用 <http-method> 元素定义 HTTP 方法)。如果没有定义 HTTP 方法,那么限制将应用于所有方法。在上面的应用中,我想限制访问的资源是 http://localhost:8080/learn/chapter/*,也就是 chapter
目录下的所有文档。
o <auth-constraint>:这个元素可以访问上面定义的受限资源的用户角色。在上面的应用中,tomcat、author 和 erader 这三个角色可以访问这些资源。 

<login-config>:这个元素用于指定验证方法。它包含下列元素:
o <auth-method>:指定验证方法。它的值可能是下列值集中的一个:BASIC (基本验证)、DIGEST (摘要验证)、FORM (基于表单的验证) 或者 CLIENT-CERT (客户证书验证)。
o <realm-name>:如果选用 BASIC 方法进行验证的时候,标准登录对话框中的一个描述名称。 

示例

上述配置中使用了 BASIC 验证方法。下面我们做个实验:启动你的 Tomcat 服务器并向它发送到 http://localhost:8080/learn/chapter 的请求。这时候,就会有像图 3 所示那样的对话框提示你输入用户和密码:

图 3:HTTP 基本验证 (Basic Authentication)

输入一个用户及其密码 (你可以看看 tomcat-users.xml 文件),这个用户的角色应该在配置 (web.xml) 中存在。如果你输入的用户名和密码正确,你就能访问到那些资源;否则,你还可以再试两次。

使用摘要验证来实验:

· 关闭你的 Tomcat 服务器。 
· 修改你的配置文件 (web.xml),把 BASIC 换成 DIGEST。 
· 重新启动你的 Tomcat 服务器。 
· 打开一个新的浏览器窗口。 
· 在地址栏中输入 http://localhost:8080/learn/chapter 并回车。 

你会看到类似的对话框。从图 4 你可以看到,这个登录对话框是安全的,因为使用了摘要验证。

图 4:HTTP 摘要验证 (Digest Authentication)

服务器在幕后的回复

当使用基本验证方法保护资源的时候,服务器发回类似于图 5 所示的响应信息:

图 5:服务器回复 (基本验证)

如果是使用的摘要验证方法来保护的资源,服务器发回的响应信息就像图 6 所示的那样:

图 6:服务器回复 (摘要验证)

Java 支持 HTTP 验证

J2SE (1.2 或者更高版本) 通过 Authenticator 类为验证提供了本地支持。你所要做的只是继承这个类并实现它的 getPasswordAuthentication 方法。这个方法取得用户名和密码并用它们生成一个 PasswordAuthentication 对象返回。完成之后,你还得使用 Authenticator.setDefault 方法注册你的 Authenticator
实例。现在,只要你想访问受保护的资源,就会调用 getPasswordAuthentication。Authenticator 类管理着所有低层的详细资料。它不受 HTTP 的限制,可以应用于所有网络连接,这不能不说是一个好消息。

示例代码 6 中是实现 Authenticator 的一个示例。正如你所看到的,在请求验证的时候,getPasswordAuthentication 方法会弹出一个登录对话框。

示例代码 6:AuthImpl.java

import java.net.*;
import java.awt.*;
import javax.swing.*;

public class AuthImpl extends Authenticator {
protected PasswordAuthentication getPasswordAuthentication() {
JTextField username = new JTextField();
JTextField password = new JPasswordField();
JPanel panel = new JPanel(new GridLayout(2,2));
panel.add(new JLabel("User Name"));
panel.add(username);
panel.add(new JLabel("Password") );
panel.add(password);
int option= JOptionPane.showConfirmDialog(null,
new Object[] {"Site: "+getRequestingHost(),
"Realm: "+getRequestingPrompt(),panel},
"Enter Network Password",
JOptionPane.OK_CANCEL_OPTION,
JOptionPane.PLAIN_MESSAGE);
if ( option == JOptionPane.OK_OPTION ) {
String user = username.getText();
char pass[] = password.getText().toCharArray();
return new PasswordAuthentication(user, pass);
} else {
return null;
}
}
}

示例代码 7 用来做测试的代码。我做的第一件事情就是用 Authenticator.setDefault 让我的 Authenticator 实例开始运行。我不必去解析任何服务器返回的信息以检查是否需要验证,因为 Authenticator 类非常聪明,它知道是否需要验证。

示例代码 7:BasicReader.java

import java.io.*;
import java.net.*;

public class BasicReader {
public static void main(String argv[]) throws Exception {
Authenticator.setDefault(new AuthImpl());
if (argv.length != 1) {
System.err.println("Usage: java BasicReader <site>");
System.exit(1);
}
URL url = new URL(argv[0]);
URLConnection connection = url.openConnection();
BufferedReader in= new BufferedReader(new InputStreamReader(connection.getInputStream()));

String line;
StringBuffer sb = new StringBuffer();
while ((line = in.readLine()) != null) {
sb.append(line);
}
in.close();
System.out.println(sb.toString());
System.exit(0);
}
}

运行下面的命令,用 Tomcat 来进行测试:

prompt> java BasicReader http://localhost:8080/learn/chapter
如果你进入的站点需要验证 (它也这样做了),那会像图 7 那样的对话框就会显示出来。

图 7:Java 处理 HTTP 验证

一旦你输入了正确的用户名和密码,服务器就会允许你访问。BasicReader 会读取被请求页面的 HTML 内容并显示在你的控制台窗口。

特别注意:在 Tomcat 4.0 中使用摘要验证时你可能会遇到问题,这是由 J2SE 1.4.0 和 J2SE 1.4.1 的一个 BUG 引起的。不过这个问题已经在 J2SE 1.4.2 中解决了。详情情看这里。 

总结

这篇文章是一篇教程,它介绍了 java.net 包的高层 API。这些 API 使你可以快速简捷地建立有用的网络应用程序,如 StockReader。这里也讨论了 HTTP 验证,并用实例演示了如何使用你自己的验证方案。URL 和 URLConnection 还有一些优势没有在文中提到,它们包括:自动重定向、自动管理保持的连接等。不过现在你已经从文中获得基础知识,可以自己解决这些问题了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: