tomcat(10)安全性
【0】README0.0)本文部分描述转自“深入剖析tomcat”,旨在学习 tomcat(10)安全性 的基本知识;0.1)servlet技术支持通过配置部署描述器(web.xml)文件来对这些内容进行访问控制;(干货——web.xml又名配置部署描述器)0.2)servlet容器是通过一个名为验证器的阀来支持安全限制的,该阀会被添加到Context的管道中,并且会先于 Wra
·
【0】README
0.0)本文部分描述转自“深入剖析tomcat”,旨在学习 tomcat(10)安全性 的基本知识;
0.1)servlet技术支持通过配置部署描述器(web.xml)文件来对这些内容进行访问控制;(干货——web.xml又名 配置部署描述器)
0.2)servlet容器是通过一个名为验证器的阀来支持安全限制的,该阀会被添加到Context的管道中,并且会先于 Wrapper阀的调用;(干货——引入了验证器阀)
0.3)调用验证器阀:
case1)用户输入了正确的username 和 pwd:验证器阀会调用后续的阀;case2)用户输入错误:验证器阀会返回,不会调用后续的阀;
0.4)验证器阀会调用Context容器的领域对象的 authenticate()方法,传入用户输入的username 和 pwd,来对用户进行身份验证;(干货——引入了领域对象)
0.5)for source code , please visit https://github.com/pacosonTang/HowTomcatWorks/tree/master/chapter10
【1】领域(Realm,用来对用户进行身份验证的组件)
1)intro to 领域对象:该对象用来对用户进行身份验证的组件;
1.1)领域对象通常与一个Context容器相关联(setRealm方法来将领域对象与Context容器相关联);
1.2)领域对象如何验证用户身份呢?它保存了所有有效用户的username 和 pwd对,或者它会访问存储这些dta的存储器;(在tomcat中,有效info 存储在tomcat-user.xml文件中,现在你知道tomcat-user.xml文件的作用是什么了吧)
private void createUserDatabase() {// SimpleRealm.createUserDatabase() 创建了users 列表;
User user1 = new User("ken", "blackcomb");
user1.addRole("manager");
user1.addRole("programmer");
User user2 = new User("cindy", "bamboo");
user2.addRole("programmer");
users.add(user1);
users.add(user2);
}
2)领域对象:是 org.apache.catalina.Realm 接口的实例,该接口的 authenticate方法最为重要,方法签名如下:
public Principal authenticate (String username, String credentials); // generally used
public Principal authenticate (String username, byte[] credentials);
public Principal authenticate (String username, String digest, String nonce, String nc, String cnonce, String qop, String realm, String md5a2);
public Principal authenticate (X509Certificate cents[]);;
3)在catalina中, Realm接口的基本实现形式是org.apache.catalina.realm.RealmBase类;其UML类图如下:
3.1)default case:会使用 MemoryRealm 类的实例作为验证用的领域对象;(干货——MemoryRealm 类的实例作
为验证默认用的领域对象)
3.2)当第一次调用 MemoryRealm实例时,它会读取 tomcat-user.xml 文档的内容;
Attention)在catalina中, 验证器阀会调用附加到其中的领域对象的 authenticate()方法来验证用户身份;
【2】GenericPrincipal类(实现接口 java.security.Principal)
public class GenericPrincipal implements Principal { //org.apache.catalina.realm.GenericPrincipal,代表一个用户(封装了username,password,role,realm)
public GenericPrincipal(Realm realm, String name, String password) {
this(realm, name, password, null);
}
public GenericPrincipal(Realm realm, String name, String password,
List roles) {
super();
this.realm = realm;
this.name = name;
this.password = password;
if (roles != null) {
this.roles = new String[roles.size()];
this.roles = (String[]) roles.toArray(this.roles);
if (this.roles.length > 0)
Arrays.sort(this.roles);
}
}
protected String name = null;
public String getName() {
return (this.name);
}
protected String password = null;
public String getPassword() {
return (this.password);
}
protected Realm realm = null;
public Realm getRealm() {
return (this.realm);
}
protected String roles[] = new String[0];
public String[] getRoles() {
return (this.roles);
}
public boolean hasRole(String role) { // highlight line.
if (role == null)
return (false);
return (Arrays.binarySearch(roles, role) >= 0);
}
public String toString() {
StringBuffer sb = new StringBuffer("GenericPrincipal[");
sb.append(this.name);
sb.append("]");
return (sb.toString());
}
}
1)intro to GenericPrincipal:
1.1)GenericPrincipal实例:必须始终与一个领域对象相关联;
1.2)GenericPrincipal实例:必须有一个用户名和密码对;且,该用户名和密码对所对应的角色列表是可选的;
1.3)调用hasRole()方法:传入1个字符串形式的角色名来检查该主体对象是否拥有该指定角色;
【3】LoginConfig类(org.apache.catalina.deploy.LoginConfig)
1)intro to LoginConfig:登录配置是 LoginConfig类的实例,其中包含一个领域对象的名字,其实例封装了领域对象名和所要使用的身份验证方法;(getRealmName()方法用来获取领域对象的名字);
2)getAuthName()方法获取身份验证方法的名字:名字范围是,BASIC, DIGEST, FORM 或 CLIENT-CERT;
3)实际部署中:tomcat在启动时需要读取 web.xml 文件的内容;
step1)如果web.xml 文件包含 login-config 元素的配置,则tomcat会创建一个 LoginConfig对象,并设置其相应属性;(干货——tomcat创建一个 LoginConfig对象的条件)step2)验证器阀会调用 LoginConfig.getRealmName() 获取领域对象名,并将领域对象名发送到 browser,显示在登录对话框中;case2.1)如果getReamlName()方法的返回值是null,则会将服务器名和相应端口发送给 browser;
看个荔枝)下图展示了 XP 系统中使用 IE6 进行基本身份验证的登录对话框
public final class LoginConfig { //org.apache.catalina.deploy.LoginConfig
public LoginConfig() {
super();
}
public LoginConfig(String authMethod, String realmName,
String loginPage, String errorPage) {
super();
setAuthMethod(authMethod);
setRealmName(realmName);
setLoginPage(loginPage);
setErrorPage(errorPage);
}
// ------------------------------------------------------------- Properties
private String authMethod = null;
public String getAuthMethod() {
return (this.authMethod);
}
public void setAuthMethod(String authMethod) {
this.authMethod = authMethod;
}
private String errorPage = null;
public String getErrorPage() {
return (this.errorPage);
}
public void setErrorPage(String errorPage) {
this.errorPage = RequestUtil.URLDecode(errorPage);
}
private String loginPage = null;
public String getLoginPage() {
return (this.loginPage);
}
public void setLoginPage(String loginPage) {
this.loginPage = RequestUtil.URLDecode(loginPage);
}
private String realmName = null;
public String getRealmName() {
return (this.realmName);
}
public void setRealmName(String realmName) {
this.realmName = realmName;
}
public String toString() {
StringBuffer sb = new StringBuffer("LoginConfig[");
sb.append("authMethod=");
sb.append(authMethod);
if (realmName != null) {
sb.append(", realmName=");
sb.append(realmName);
}
if (loginPage != null) {
sb.append(", loginPage=");
sb.append(loginPage);
}
if (errorPage != null) {
sb.append(", errorPage=");
sb.append(errorPage);
}
sb.append("]");
return (sb.toString());
}
}
【4】Authenticator接口(org.apache.catalina.Authenticator)
1)intro to Authenticator:验证器接口只是起到了标记作用,这样其他组件就可以使用 instanceof 来检查某个组件是否是一个验证器;(干货——验证器接口只是起到了标记作用)
2)org.apache.catalina.Authenticator的UML类图如下:
对上图的分析(Analysis):
A1)BasicAuthenticator:用来支持基本的身份验证;A2)FormAuthenticator:提供了基于表单的身份验证;A3)DigestAuthenticator:提供了基于信息摘要的身份验证;A4)SSLAuthenticator:用于对SSL 进行身份验证;A5)当tomcat 用户没有指定验证方法名时,NonLoginAuthenticator类用于对来访者的身份进行验证。NonLoginAuthenticator类实现的验证器只会检查安全限制,而不会涉及用户身份的验证;
Attention)
A1)验证器的重要工作:是对用户进行身份验证;(干货——验证器的重要工作是对用户进行身份验证)A2)当看到 AuthenticatorBase.invoke() 方法调用 authenticate() 抽象方法时:后者的实现依赖于子类;(而authenticate()方法会使用基本身份验证来验证用户的身份信息);
public class BasicAuthenticator
extends AuthenticatorBase { <span style="font-family: SimSun;">//org.apache.catalina.authenticator.BasicAuthenticator,这里仅以BasicAuthenticator 为例po出 source code.</span>
// ----------------------------------------------------- Instance Variables
protected static final Base64 base64Helper = new Base64();
protected static final String info =
"org.apache.catalina.authenticator.BasicAuthenticator/1.0";
public String getInfo() {
return (this.info);
}
public boolean authenticate(HttpRequest request,
HttpResponse response,
LoginConfig config)
throws IOException {
// Have we already authenticated someone?
Principal principal =
((HttpServletRequest) request.getRequest()).getUserPrincipal();
String ssoId = (String) request.getNote(Constants.REQ_SSOID_NOTE);
if (principal != null) {
if (debug >= 1)
log("Already authenticated '" + principal.getName() + "'");
// Associate the session with any existing SSO session
if (ssoId != null)
associate(ssoId, getSession(request, true));
return (true);
}
// Is there an SSO session against which we can try to reauthenticate?
if (ssoId != null) {
if (debug >= 1)
log("SSO Id " + ssoId + " set; attempting reauthentication");
if (reauthenticateFromSSO(ssoId, request))
return true;
}
// Validate any credentials already included with this request
HttpServletRequest hreq =
(HttpServletRequest) request.getRequest();
HttpServletResponse hres =
(HttpServletResponse) response.getResponse();
String authorization = request.getAuthorization();
String username = parseUsername(authorization);
String password = parsePassword(authorization);
principal = context.getRealm().authenticate(username, password);
if (principal != null) {
register(request, response, principal, Constants.BASIC_METHOD,
username, password);
return (true);
}
// Send an "unauthorized" response and an appropriate challenge
String realmName = config.getRealmName();
if (realmName == null)
realmName = hreq.getServerName() + ":" + hreq.getServerPort();
hres.setHeader("WWW-Authenticate",
"Basic realm=\"" + realmName + "\"");
hres.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
// hres.flushBuffer();
return (false);
}
// ------------------------------------------------------ Protected Methods
protected String parseUsername(String authorization) {
if (authorization == null)
return (null);
if (!authorization.toLowerCase().startsWith("basic "))
return (null);
authorization = authorization.substring(6).trim();
// Decode and parse the authorization credentials
String unencoded =
new String(base64Helper.decode(authorization.getBytes()));
int colon = unencoded.indexOf(':');
if (colon < 0)
return (null);
String username = unencoded.substring(0, colon);
// String password = unencoded.substring(colon + 1).trim();
return (username);
}
protected String parsePassword(String authorization) {
if (authorization == null)
return (null);
if (!authorization.startsWith("Basic "))
return (null);
authorization = authorization.substring(6).trim();
// Decode and parse the authorization credentials
String unencoded =
new String(base64Helper.decode(authorization.getBytes()));
int colon = unencoded.indexOf(':');
if (colon < 0)
return (null);
// String username = unencoded.substring(0, colon).trim();
String password = unencoded.substring(colon + 1);
return (password);
}
}
【5】安装验证器阀
1)一个Context实例:只能有一个 LoginConfig实例和利用一个验证类的实现;
2)也就是说:在部署描述器(tomcat-user.xml)中,login-config元素只出现一次;login-config元素包含一个auth-method 元素来指定身份验证方法;
3)使用AuthenticatorBase类的哪个子类作为Context实例中的验证器阀:依赖于 部署描述器中 auth-method元素;
4)下表给出了 auth-method 元素的值和对应的验证器的类名:
对上表的分析(Analysis):
A1)若没有设置 auth-method元素:则 LoginConfig 对象 的 atuh-method属性的值默认为 NONE,使用 NonLoginAuthenticator 进行安全验证;A2)由于使用的验证器类是在运行时才确定的,故该类是动态载入的;A3)StandardContext容器使用 org.apahce.catalina.startup.ContextConfig类来对 StandardContext 实例的属性进行设置:这些设置包括 实例化一个验证器类,并将该实例与Context实例相关联;
【6】应用程序
【6.1】SimpleContextConfig类
1)intro:public class SimpleContextConfig implements LifecycleListener ,仅仅是个监听器而已,不要惊慌;
2)SimpleContextConfig.authenticatorConfig()方法:该方法实例化BasicAuthenticator类,并将其作为阀添加到 StandardContext实例的管道中;(干货——注意其实例化BasicAuthenticator类的技巧)
// Identify the class name of the Valve we should configure
String authenticatorName = "org.apache.catalina.authenticator.BasicAuthenticator";
// Instantiate and install an Authenticator of the requested class
Valve authenticator = null;
try {
Class authenticatorClass = Class.forName(authenticatorName);
authenticator = (Valve) authenticatorClass.newInstance();
((StandardContext) context).addValve(authenticator);
System.out.println("Added authenticator valve to Context");
}
catch (Throwable t) {
}
3)下面对SimpleContextConfig.authenticatorConfig()方法的调用过程 进行 detailed analysis:
step1)先检查在相关联的Context容器是否有安全限制,若没有直接返回,而不会安装验证器;// Does this Context require an Authenticator? SecurityConstraint constraints[] = context.findConstraints(); if ((constraints == null) || (constraints.length == 0)) return;
step2)若当前Context容器有一个或多个安全限制,authenticatorConfig() 方法会检查该Context实例是否有 LoginConfig对象。若没有,则它会创建一个新的 LoginConfig实例:LoginConfig loginConfig = context.getLoginConfig(); if (loginConfig == null) { loginConfig = new LoginConfig("NONE", null, null, null); context.setLoginConfig(loginConfig); }
step3)检查管道中的基础阀或附加阀是否是验证器。因为一个Context实例只能有一个验证器,所以当发现某个阀是验证器后,直接返回:// Has an authenticator been configured already? Pipeline pipeline = ((StandardContext) context).getPipeline(); if (pipeline != null) { Valve basic = pipeline.getBasic(); if ((basic != null) && (basic instanceof Authenticator)) return; Valve valves[] = pipeline.getValves(); for (int i = 0; i < valves.length; i++) { if (valves[i] instanceof Authenticator) return; } } else { // no Pipeline, cannot install authenticator valve return; }
step4)查找当前Context实例是否有与之关联的领域对象(Realm)。如果没有领域对象,就不需要安装验证器了,因为用户是无法通过身份验证的;// Has a Realm been configured for us to authenticate against? if (context.getRealm() == null) { return; }
step5)若找到了领域对象,则会动态载入 BasicAuthenticator类,创建该类的一个实例,并将其作为阀添加到 StandardContext实例中;// Identify the class name of the Valve we should configure String authenticatorName = "org.apache.catalina.authenticator.BasicAuthenticator"; // Instantiate and install an Authenticator of the requested class Valve authenticator = null; try { Class authenticatorClass = Class.forName(authenticatorName); authenticator = (Valve) authenticatorClass.newInstance(); ((StandardContext) context).addValve(authenticator); System.out.println("Added authenticator valve to Context"); } catch (Throwable t) { } }
【6.2】SimpleRealm类(简单领域对象,领域对象是用来对用户身份验证的组件)
1)SimpleRealm类实现了Realm接口;
2)在构造函数中,它调用了createUserDatabase()方法创建两个用户,并将这两个用户添加到users中;(干货——创建用户,并设置角色,功能同tomcat-users.xml 的内容)
public class SimpleRealm implements Realm {
public SimpleRealm() {
createUserDatabase(); //highlight line.
}
private void createUserDatabase() { // there are 2 roles.
User user1 = new User("ken", "blackcomb");
user1.addRole("manager"); // manager role.
user1.addRole("programmer"); // programmer role.
User user2 = new User("cindy", "bamboo");
user2.addRole("programmer");
users.add(user1);
users.add(user2);
// private ArrayList users = new ArrayList();
}
}
3)再看其 authenticate() 验证方法:该方法由验证器调用,若用户提供的用户名或密码无效,则返回null,否则返回一个代表该用户的 Principal对象;
public Principal authenticate(String username, String credentials) {
System.out.println("SimpleRealm.authenticate()");
if (username==null || credentials==null)
return null;
User user = getUser(username, credentials);
if (user==null)
return null;
return new GenericPrincipal(this, user.username, user.password, user.getRoles()); // highlight line.
}
public class GenericPrincipal implements Principal { // 该类封装了用户的一些信息,如username,pass,role,realm等info;
public GenericPrincipal(Realm realm, String name, String password) {
this(realm, name, password, null);
}
public GenericPrincipal(Realm realm, String name, String password,
List roles) {
super();
this.realm = realm;
this.name = name;
this.password = password;
if (roles != null) {
this.roles = new String[roles.size()];
this.roles = (String[]) roles.toArray(this.roles);
if (this.roles.length > 0)
Arrays.sort(this.roles);
}
}
protected String name = null;
public String getName() {
return (this.name);
}
protected String password = null;
public String getPassword() {
return (this.password);
}
protected Realm realm = null;
public Realm getRealm() {
return (this.realm);
}
protected String roles[] = new String[0];
public String[] getRoles() {
return (this.roles);
}
public boolean hasRole(String role) {
if (role == null)
return (false);
return (Arrays.binarySearch(roles, role) >= 0);
}
public String toString() {
StringBuffer sb = new StringBuffer("GenericPrincipal[");
sb.append(this.name);
sb.append("]");
return (sb.toString());
}
}
【6.3】SimpleUserDatabaseRealm(它是SimpleRealm的变体类)
1)intro:SimpleUserDatabaseRealm类表示一个复杂一点的领域对象,它并不将用户列表存储到对象自身中。相反,它会读取conf 目录下的 tomcat-users.xml文件,将内容载入内存,然后依据该列表进行身份验证。(干货——读取conf 目录下的 tomcat-users.xml文件,将内容载入内存,然后依据该列表进行身份验证)
// tomcat-users.xml 的源码如下:
<?xml version='1.0' encoding='utf-8'?>
<tomcat-users>
<role rolename="tomcat"/>
<role rolename="role1"/>
<role rolename="manager"/>
<role rolename="admin"/>
<user username="tomcat" password="tomcat" roles="tomcat"/>
<user username="role1" password="tomcat" roles="role1"/>
<user username="both" password="tomcat" roles="tomcat,role1"/>
<user username="admin" password="admin" roles="admin,manager"/>
</tomcat-users>
2)在实例化SimpleUserDatabaseRealm类后, 必须调用它的 createDatabase()方法,并向包含用户列表的xml 文档传递路径。
// 以下代码是 Bootstrap2.java 中的源码
Realm realm = new SimpleUserDatabaseRealm();
String filedir = new File(System.getProperty("user.dir")).getParent() + File.separator +
"conf" + File.separator + "tomcat-users.xml";
((SimpleUserDatabaseRealm) realm).createDatabase(filedir); // highlight line.
public void createDatabase(String path) { // SimpleUserDatabaseRealm.createDatabase().
database = new MemoryUserDatabase(name);
((MemoryUserDatabase) database).setPathname(path);
try {
database.open(); // highlight line.
}
catch (Exception e) {
}
}
public void open() throws Exception { // org.apache.catalina.users.MemoryUserDatabase.open().
synchronized (groups) {
synchronized (users) {
// Erase any previous groups and users
users.clear();
groups.clear();
roles.clear();
// Construct a reader for the XML input file (if it exists)
File file = new File(pathname);
if (!file.isAbsolute()) {
file = new File(System.getProperty("catalina.base"),
pathname);
}
if (!file.exists()) {
return;
}
FileInputStream fis = new FileInputStream(file);
// Construct a digester to read the XML input file
Digester digester = new Digester();
digester.addFactoryCreate
("tomcat-users/group",
new MemoryGroupCreationFactory(this));
digester.addFactoryCreate
("tomcat-users/role",
new MemoryRoleCreationFactory(this));
digester.addFactoryCreate
("tomcat-users/user",
new MemoryUserCreationFactory(this));
// Parse the XML input file to load this database
try {
digester.parse(fis);
fis.close();
} catch (Exception e) {
try {
fis.close();
} catch (Throwable t) {
;
}
throw e;
} } } }
【6.4】Bootstrap1.java(应用程序1)
1)Bootstrap1.java 的源代码
public final class Bootstrap1 {
public static void main(String[] args) {
//invoke: http://localhost:8080/Modern or http://localhost:8080/Primitive
System.setProperty("catalina.base", System.getProperty("user.dir"));
System.out.println("user.dir = " + System.getProperty("user.dir"));
Connector connector = new HttpConnector();
Wrapper wrapper1 = new SimpleWrapper();
wrapper1.setName("Primitive");
wrapper1.setServletClass("servlet.PrimitiveServlet");
Wrapper wrapper2 = new SimpleWrapper();
wrapper2.setName("Modern");
wrapper2.setServletClass("servlet.ModernServlet");
Context context = new StandardContext(); // highlight line.
// StandardContext's start method adds a default mapper
context.setPath("/myApp");
context.setDocBase("myApp");
LifecycleListener listener = new SimpleContextConfig();// highlight line.
((Lifecycle) context).addLifecycleListener(listener);
context.addChild(wrapper1);
context.addChild(wrapper2);
// for simplicity, we don't add a valve, but you can add
// valves to context or wrapper just as you did in Chapter 6
Loader loader = new WebappLoader();
context.setLoader(loader);
// context.addServletMapping(pattern, name);
context.addServletMapping("/Primitive", "Primitive");
context.addServletMapping("/Modern", "Modern");
// add ContextConfig. This listener is important because it configures
// StandardContext (sets configured to true), otherwise StandardContext
// won't start
// add constraint
SecurityCollection securityCollection = new SecurityCollection();// highlight line.
securityCollection.addPattern("/");// highlight line.
securityCollection.addMethod("GET");// highlight line.
SecurityConstraint constraint = new SecurityConstraint();// highlight line.
constraint.addCollection(securityCollection);// highlight line.
constraint.addAuthRole("manager");// highlight line.
LoginConfig loginConfig = new LoginConfig();// highlight line.
loginConfig.setRealmName("Simple Realm");
// add realm
Realm realm = new SimpleRealm();// highlight line.
context.setRealm(realm);// highlight line.
context.addConstraint(constraint);// highlight line.
context.setLoginConfig(loginConfig);// highlight line.
connector.setContainer(context);
// add a Manager
Manager manager = new StandardManager();
context.setManager(manager);
try {
connector.initialize();
((Lifecycle) connector).start();
((Lifecycle) context).start();
// make the application wait until we press a key.
System.in.read();
((Lifecycle) context).stop();
}
catch (Exception e) {
e.printStackTrace();
}
}
}
对以上代码的分析(Analysis)
A1)创建StandardContext对象:设置其path属性和 documentBase属性,并添加一个 SimpleContextConfig 类的监听器。该监听器会把一个 BasicAuthenticator 对象安装到 StandardContext 对象中;A2)创建SecurityColleciton对象:并调用其addPattern和 addMethod方法,addPattern方法指定某个url 要遵循哪个安全限制, 而addMethod方法会指定该安全限制要使用哪种验证方法;在addMethod()方法中设置为GET, 则使用GET 方法提交的http 请求会遵循安全限制;A3)创建 SecurityManager对象:并将其添加到 安全限制集合中,且还设置了哪种角色可以访问这些受限资源。A4)创建LoginConfig对象 和 SimpleRealm对象;A5)将领域对象,安全限制对象,登录配置对象 与 StandardContext实例相关联;A6)接着,启动Context实例。。。。。。。
Supplement-补充)
S1)本文还是给出了如何验证用户合法性的调用过程
S2)结合S1中的第一张图,本文关联性地给出了 server处理http 客户端请求的调用过程
2)打印结果
<pre name="code" class="java">E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\src>java -cp .;lib/servlet.jar;lib/catalina_4_1_24.jar;lib/catalina-5.5.4.jar;lib/naming-common
jar;lib/commons-collections.jar;lib/naming-resources.jar;lib/;lib/catalina.jar;E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\webroot com.tomc
t.chapter10.startup.Bootstrap1
HttpConnector Opening server socket on all host IP addresses
HttpConnector[8080] Starting background thread
WebappLoader[/myApp]: Deploying class repositories to work directory E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\src\work\_\_\myApp
Starting Wrapper Primitive
Starting Wrapper Modern
Added authenticator valve to Context
StandardManager[/myApp]: Seeding random number generator class java.security.SecureRandom
StandardManager[/myApp]: Seeding of random number generator has been completed
StandardManager[/myApp]: IOException while loading persisted sessions: java.io.EOFException
// 这是从文件中加载 session对象到内存,由于没有相关文件,所以加载失败,抛出异常,但这不会影响我们访问servlet,大家不要惊慌;
java.io.EOFException
at java.io.ObjectInputStream$PeekInputStream.readFully(Unknown Source)
at java.io.ObjectInputStream$BlockDataInputStream.readShort(Unknown Source)
at java.io.ObjectInputStream.readStreamHeader(Unknown Source)
at java.io.ObjectInputStream.<init>(Unknown Source)
at org.apache.catalina.util.CustomObjectInputStream.<init>(CustomObjectInputStream.java:103)
at org.apache.catalina.session.StandardManager.load(StandardManager.java:408)
at org.apache.catalina.session.StandardManager.start(StandardManager.java:655)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:3570)
at com.tomcat.chapter10.startup.Bootstrap1.main(Bootstrap1.java:84)
StandardManager[/myApp]: Exception loading sessions from persistent storage
java.io.EOFException
at java.io.ObjectInputStream$PeekInputStream.readFully(Unknown Source)
at java.io.ObjectInputStream$BlockDataInputStream.readShort(Unknown Source)
at java.io.ObjectInputStream.readStreamHeader(Unknown Source)
at java.io.ObjectInputStream.<init>(Unknown Source)
at org.apache.catalina.util.CustomObjectInputStream.<init>(CustomObjectInputStream.java:103)
at org.apache.catalina.session.StandardManager.load(StandardManager.java:408)
at org.apache.catalina.session.StandardManager.start(StandardManager.java:655)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:3570)
at com.tomcat.chapter10.startup.Bootstrap1.main(Bootstrap1.java:84)
SimpleRealm.authenticate()
SimpleRealm.authenticate()
ModernServlet -- init
SimpleRealm.authenticate()
SimpleRealm.authenticate()
SimpleRealm.authenticate()
SimpleRealm.authenticate()
SimpleRealm.authenticate()
SimpleRealm.authenticate()
init
SimpleRealm.authenticate()
from service
from service
【6.5】Bootstrap2.java(应用程序2)
1)Bootstrap2的源程序和 Bootstrap1.java 的源程序差不多;
public final class Bootstrap2 {
public static void main(String[] args) {
//invoke: http://localhost:8080/Modern or http://localhost:8080/Primitive
System.setProperty("catalina.base", System.getProperty("user.dir"));
Connector connector = new HttpConnector();
Wrapper wrapper1 = new SimpleWrapper();
wrapper1.setName("Primitive");
wrapper1.setServletClass("servlet.PrimitiveServlet");
Wrapper wrapper2 = new SimpleWrapper();
wrapper2.setName("Modern");
wrapper2.setServletClass("servlet.ModernServlet");
Context context = new StandardContext(); // highlight line.
// StandardContext's start method adds a default mapper
context.setPath("/myApp");
context.setDocBase("myApp");
LifecycleListener listener = new SimpleContextConfig();// highlight line.
((Lifecycle) context).addLifecycleListener(listener);
context.addChild(wrapper1);
context.addChild(wrapper2);
// for simplicity, we don't add a valve, but you can add
// valves to context or wrapper just as you did in Chapter 6
Loader loader = new WebappLoader();
context.setLoader(loader);
// context.addServletMapping(pattern, name);
context.addServletMapping("/Primitive", "Primitive");
context.addServletMapping("/Modern", "Modern");
// add ContextConfig. This listener is important because it configures
// StandardContext (sets configured to true), otherwise StandardContext
// won't start
// add constraint
SecurityCollection securityCollection = new SecurityCollection();// highlight line.
securityCollection.addPattern("/");// highlight line.
securityCollection.addMethod("GET");// highlight line.
SecurityConstraint constraint = new SecurityConstraint();// highlight line.
constraint.addCollection(securityCollection);// highlight line.
constraint.addAuthRole("manager");// highlight line.
constraint.addAuthRole("tomcat");// highlight line.注意这里的角色,必要要和tomcat-users.xml 中的users 列表相对应,如果要设置某个用户有权限访问servlet资源,则需要添加其所属的角色;
LoginConfig loginConfig = new LoginConfig();// highlight line.
loginConfig.setRealmName("Simple User Database Realm");
// add realm
Realm realm = new SimpleUserDatabaseRealm();// highlight line.
String filedir = new File(System.getProperty("user.dir")).getParent() + File.separator +
"conf" + File.separator + "tomcat-users.xml";
((SimpleUserDatabaseRealm) realm).createDatabase(filedir); // highlight line.设置tomcat-users.xml 的文件路径 以载入其内容到内存进行身份验证工作;
context.setRealm(realm);// highlight line.
context.addConstraint(constraint);// highlight line.
context.setLoginConfig(loginConfig);// highlight line.
connector.setContainer(context);
try {
connector.initialize();
((Lifecycle) connector).start();
((Lifecycle) context).start();
// make the application wait until we press a key.
System.in.read();
((Lifecycle) context).stop();
}
catch (Exception e) {
e.printStackTrace();
}
}}
// tomcat-users.xml 的源码如下:
<?xml version='1.0' encoding='utf-8'?>
<tomcat-users>
<role rolename="tomcat"/>
<role rolename="role1"/>
<role rolename="manager"/>
<role rolename="admin"/>
<user username="tomcat" password="tomcat" roles="tomcat"/>
<user username="role1" password="tomcat" roles="role1"/>
<user username="both" password="tomcat" roles="tomcat,role1"/>
<user username="admin" password="admin" roles="admin,manager"/>
</tomcat-users>
2)它们唯一的差别在于以不同的方式获取用户列表:Bootstrap1.java 是在对象中创建用户列表保存在 ArrayList中;而Bootstrap2.java 通过读取conf目录下的 tomcat-users.xml文件,将其内容载入到内存中;(干货——Bootstrap1.java 和 Bootstrap2.java的唯一差别)(干货——你现在知道 tomcat-users.xml 的作用了,以及如何利用它进行安全性访问的限制)
E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\src>java -cp .;lib/servlet.jar;lib/catalina_4_1_24.jar;lib/catalina-5.5.4.jar;lib/naming-common.
jar;lib/commons-collections.jar;lib/naming-resources.jar;lib/;lib/catalina.jar;lib\commons-digester.jar;lib\commons-logging.jar;E:\bench-cluster\cloud
-data-preprocess\HowTomcatWorks\webroot com/tomcat/chapter10/startup/Bootstrap2
HttpConnector Opening server socket on all host IP addresses
HttpConnector[8080] Starting background thread
WebappLoader[/myApp]: Deploying class repositories to work directory E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\src\work\_\_\myApp
Starting Wrapper Primitive
Starting Wrapper Modern
Added authenticator valve to Context
StandardManager[/myApp]: Seeding random number generator class java.security.SecureRandom
StandardManager[/myApp]: Seeding of random number generator has been completed
StandardManager[/myApp]: IOException while loading persisted sessions: java.io.EOFException // 这是从文件中加载 session对象到内存,由于没有相关文件,所以加载失败,抛出异常,但这不会影响我们访问servlet,大家不要惊慌;
java.io.EOFException
at java.io.ObjectInputStream$PeekInputStream.readFully(Unknown Source)
at java.io.ObjectInputStream$BlockDataInputStream.readShort(Unknown Source)
at java.io.ObjectInputStream.readStreamHeader(Unknown Source)
at java.io.ObjectInputStream.<init>(Unknown Source)
at org.apache.catalina.util.CustomObjectInputStream.<init>(CustomObjectInputStream.java:103)
at org.apache.catalina.session.StandardManager.load(StandardManager.java:408)
at org.apache.catalina.session.StandardManager.start(StandardManager.java:655)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:3570)
at com.tomcat.chapter10.startup.Bootstrap2.main(Bootstrap2.java:84)
StandardManager[/myApp]: Exception loading sessions from persistent storage
java.io.EOFException
at java.io.ObjectInputStream$PeekInputStream.readFully(Unknown Source)
at java.io.ObjectInputStream$BlockDataInputStream.readShort(Unknown Source)
at java.io.ObjectInputStream.readStreamHeader(Unknown Source)
at java.io.ObjectInputStream.<init>(Unknown Source)
at org.apache.catalina.util.CustomObjectInputStream.<init>(CustomObjectInputStream.java:103)
at org.apache.catalina.session.StandardManager.load(StandardManager.java:408)
at org.apache.catalina.session.StandardManager.start(StandardManager.java:655)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:3570)
at com.tomcat.chapter10.startup.Bootstrap2.main(Bootstrap2.java:84)
ModernServlet -- init
3)打印结果
更多推荐
已为社区贡献7条内容
所有评论(0)