【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.LoginConfigpublic 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 sessionif (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 requestHttpServletRequest 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 challengeString 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 Methodsprotected 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 credentialsString 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 credentialsString 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 configureString authenticatorName = "org.apache.catalina.authenticator.BasicAuthenticator";// Instantiate and install an Authenticator of the requested classValve 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 valvereturn;}

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 configureString authenticatorName = "org.apache.catalina.authenticator.BasicAuthenticator";// Instantiate and install an Authenticator of the requested classValve 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 usersusers.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 fileDigester 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 databasetry {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/PrimitiveSystem.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 mappercontext.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 6Loader 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 constraintSecurityCollection 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 realmRealm 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 ManagerManager 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.EOFExceptionat 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.EOFExceptionat 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/PrimitiveSystem.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 mappercontext.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 6Loader 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 constraintSecurityCollection 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 realmRealm 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.EOFExceptionat 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)打印结果

tomcat(10)安全性相关推荐

  1. 应用发布服务器_Apache Tomcat 10.0.0-M1 发布,开源 Web 应用服务器

    Apache Tomcat 10.0.0 发布了第一个里程碑版本.Tomcat 是 Java Servlet.JavaServer Pages.Java 表达式语言和 Java WebSocket 技 ...

  2. javaWeb 中Tomcat 10 jsp文件内置对象不能正常使用的问题(已解决)

    在这段时间学习javaweb时,因为是自己找的视频,下载了最新版本的Tomcat,Tomcat 10 ,然后就发现了很多问题,Tomcat 10 跟之前的版本 有很大的区别,以至于,10以下的版本的文 ...

  3. Tomcat 10 调用Servlet提示 500 实例化Servlet类异常

    最近在做Java EE的项目时把Tomcat服务器更新到了10.0.21,然后在测试调用Servlet中出现了如下异常 注意看红框框里面的内容 通过对错误信息的解读我们不难发现Tomcat 10版本的 ...

  4. Tomcat【环境搭建 02】Web端403 Access Denied You are not authorized to view this page解决方法(Tomcat 10.2.12 版本)

    1. 问题说明 启动Tomcat之后,我们常常是要打开Web端进行验证,其实Web端的功还有很多,但是有一些管理页面权限不足: 我们点击 Manager Status 或 Manager App 链接 ...

  5. psa name_Windows 10安全性PSA:启用自动商店更新

    psa name Microsoft sometimes distributes important security updates through the Microsoft Store. Tha ...

  6. tomcat(12)org.apache.catalina.core.StandardContext源码剖析

    [0]README 0)本文部分文字描述转自 "how tomcat works",旨在学习 "tomcat(12)StandardContext源码剖析" 的 ...

  7. tomcat(11)org.apache.catalina.core.StandardWrapper源码剖析

    [0]README 0.0)本文部分文字描述转自 "how tomcat works",旨在学习 "tomcat(11)StandardWrapper源码剖析" ...

  8. Apache Tomcat 安全性指南

    Apache Tomcat 在安全性方面拥有令人印象深刻的记录.根据官方 Apache Tomcat Wiki Pages,从未报告过由于对任何 Apache Tomcat 实例的恶意攻击而导致实际损 ...

  9. Tomcat 又爆出高危漏洞!Tomcat 8.5~10 中招…

    作者 | 栈长 来源 | Java技术栈 开源界最近很热闹啊,各个主流软件或框架漏洞频发,比如像 Struts2.FastJSON.Dubbo.Redis.Tomcat 等都存在各种各样的漏洞. 不要 ...

最新文章

  1. LIS ZOJ - 4028
  2. 判断一个请求是否为Ajax请求
  3. 矩阵的直接LU分解法
  4. android横竖屏切换不刷新,关于Android中使用BottomNavigationView切换横屏导致返回主页的问题...
  5. 解决Cygwin编译cocos2dx 遇到的 error: 'UINT64_C' was not declared in this scope 问题
  6. 这8种SQL用法,我用错吗?求答案!!!
  7. [Redis6]key键操作
  8. java 无锁框架_高性能无锁并发框架 Disruptor,太强了!
  9. 详述一条SQL引发的高CPU故障处理过程
  10. 关于MATLAB实现的数字信号处理(二)
  11. Exchange 2010与Exchange Online混合部署PART 3:混合准备
  12. lm358 pdf应用电路资料及引脚图
  13. bootstarp怎么使盒子到最右边_基于Android血统的NAS+盒子共生体——零刻GS-king X 开箱初体验...
  14. 【Pytorch】测试单张图片(调用transforms)
  15. win7连接远程桌面时出现黑屏的解决方法(亲测有效)——终于找到解决办法了
  16. 【程序设计训练】棋盘
  17. Ubuntu14.04安装有道词典
  18. 2020年总结 - 不纠结当下,不忧虑未来
  19. Shell脚本:循环for / while / until
  20. 在线查询IP及IP信息

热门文章

  1. Pool construction UVA - 1515 最小割模型
  2. [牛客] Tree-换根dp(预防加法取模导致等0)
  3. 【学习笔记】WQS二分详解及常见理解误区解释
  4. NOIP2018洛谷P5021:修建赛道
  5. 51nod1227-平均最小公倍数【杜教筛,欧拉函数】
  6. CF1039D-You Are Given a Tree【根号分治,贪心】
  7. P4780-Phi的反函数【dfs】
  8. P4655-[CEOI2017]Building Bridges【斜率优化dp,CDQ分治】
  9. 【KMP】周期长度和(luogu 3435/ybtoj KMP-3)
  10. 2、oracle数据库的用户和权限