How tomcat works——10 安全性
来源:互联网 发布:java while循环 编辑:程序博客网 时间:2024/06/01 09:41
概述
一些web 应用内容是受限的,只有在有特定权限的用户输入正确的用户名和密码后才能访问。Servlet 通过配置部署文件 web.xml 来对安全性提供技术支持。本章将介绍 container 如何支持安全性控制。
servlet 容器通过一个称为 authenticator 的阀门(valve)来支持安全认证。当 container 启动时,authenticator 被添加到container 的 pipeline中。如果忘记了pipeline工作机制,请阅读第6章。
在 wrapper阀门被调用之前,会先调用 authenticator阀门,用来对用户进行认证。若用户输入了正确的用户名密码,则 authenticator 会调用下一个阀门,否则会直接返回,不再继续执行剩余的阀门。由于验证失败,用户并不能看到请求的 servlet。
在用户验证时, authenticator 阀门调用的是上下文域(realm)内的authenticate()方法,将用户名和密码传递给它。领域(Realm)可以访问有效用户名和密码的集合。
本章会先介绍一些 servlet 编程中与安全相关的对象(realm,role,principal 等),然后通过一个应用程序来演示如何为servlet 添加基本的认证功能。
注意:这里假设大家已经熟悉 servlet 编程安全性的相关概念,包括: principals, roles, realms, login configuration等。如果对这些概念还不清楚可以阅读《Java for the Web with Senlets, JSP,and EJB》或者其它相关书籍。
10.1 领域(Realm)
域(Realm)是用于进行用户验证的一个组件。它验证一用户名&密码是否是合法。一个域跟一个上下文容器相联系,并且一个容器只有一个域。可以使用容器的 setRealm()方法来建立它们之间的联系。
一个域是如何验证一个用户的合法性的昵?一个域拥有所有的合法用户的用户名和密码或
者是可以访问到存储它们的地方。至于它们存放在哪里则取决于域的具体实现。在 Tomcat默认实现里,合法用户被存储在 tomcat-users.xml 文件里。但是,也可以使用域的其它实现,如关系数据库。
在 Catalina 中,一个域用接口 org.apache.catalina.Realm 表示。该接口最重要的方法是4个 重载authenticate()方法:
public Principal authenticate(String username, String credentials);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 certs[]);
第一个方法最常用。Realm 接口还有一个 getRole()方法,签名如下:
public boolean hasRole(Principal principal, String role);
另外,域还有 getContainer() 和 setContainer() 方法用于建立域与容器关联。
抽象类 org.apache.catalina.realm.RealmBase是一个域的基本实现。org.apache.catalina.realm 包中还提供了其它继承了 RealmBase的一些实现类,如:JDBCRealm, JNDIRealm, MemoryRealm和 UserDatabaseRealm。默认情况下使用的域是 MemoryRealm。当MemoryRealm首次启动时,它会读取tomcat-users.xml文件。在本章Demo中,将会自定义一简单的Realm来存储用户信息。
注意:在 Catalina 中,authenticator阀门调用相关域的 authenticate()方法来验证一个用户。
10.2 GenericPrincipal类
一个主体(principal)使用 java.security.Principal 接口来表示,Catalina中的实现类是 org.apache.catalina.realm.GenericPrincipal。一个GenericPrincipal 必须跟一个域相关联,如下是GenericPrincipal的2个构造函数:
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); }}
GenericPrincipal 必须拥有一个用户名和一个密码,此外还可选择性的传递一角色列表(List roles)给它。我们可以使用 hasRole()方法来检查一个 principal 是否有一个特定的角色,传递的参数为角色的字符串表示形式。如下是 Tomcat4 中的 hasRole() 方法:
public boolean hasRole(String role) { if (role == null) return (false); return (Arrays.binarySearch(roles, role) >= 0);}
Tomcat5 支持 servlet2.4 所以必须支持用*来匹配任何角色。
public boolean hasRole(String role) { if ("*".equals(role)) // Special 2.4 role meaning everyone return true; if (role == null) return (false); return (Arrays.binarySearch(roles, role) >= 0);}
10.3 LoginConfig类
一个登录配置包括一个域名,这里是通过org.apache.catalina.deploy.LoginConfig 不变类表示。LoginConfig 的实例封装了域名和验证要用的方法。可以通过LoginConfig实例的 getRealmName()方法来获得域名,可以使用 getAuthName()方法来验证用户。一个验证的名字必须是下面的之一:BASIC, DIGEST, FORM或者 CLIENT-CERT。如果用到的是基于表单(form)的验证,该 LoginConfig 对象还包括登录或者错误页面像对应的 URL。
Tomcat 在启动时,会先读取 web.xml。如果 web.xml 包括一个login-config元素,那么Tomcat 则会创建一 LoginConfig 对象并相应地设置它的属性。验证阀门调用 LoginConfig 的 getRealmName() 方法并将域名发送给浏览器显示登录表单。如果 getRealmName()名返回值为 null,则将发送服务器的名字和端口名给浏览器作为代替。图 10.1 是在Window XP 上 IE6 浏览器上显示的基本验证会话窗。
图10.1: The basic authentication dialog
10.4 验证器(Authenticator)
org.apache.catalina.Authenticator 接口用来表示一个验证器。该接口并没有方法,只是一个组件的标志器,这样就能使用 “instanceof”来检查一个组件是否为验证器。
Catalina 提供了 Authenticator 接口的基本实现:org.apache.catalina.authenticator.AuthenticatorBase 类。除了实现Authenticator 接口外,AuthenticatorBase 还继承了org.apache.catalina.valves.ValveBase 类。这就是说 AuthenticatorBase 也是一个阀门。可以在 org.apache.catalina.authenticator 包中找到该接口的几个类:BasicAuthenticator 用于基本验证, FormAuthenticator 用于基于表单的验证, DigestAuthentication 用于摘要(digest)验证, SSLAuthenticator 用于 SSL 验证。NonLoginAuthenticator 用于 Tomcat 没有指定验证元素的时候。NonLoginAuthenticator 类表示只是检查安全限制的验证器,但是不进行用户验证。
org.apache.catalina.authenticator 包中类的 UML 结构图如图 10.2 所示:
图10.2: Authenticator-related classes
一个验证器的主要工作是验证用户。因此AuthenticatorBase 类的 invoke 方法调用了抽象方法 authenticate()也不足为奇了,该方法的具体实现由子类完成。例如,在BasicAuthenticator中,authenticate()方法使用基本认证来认证用户。
10.5 安装验证器阀门
在部署文件中,只能出现一个 login-config 元素,login-config 元素包括了auth-method 元素用于定义验证方法。这也就是说一个上下文容器只能有一个LoginConfig 对象来使用一个 authentication 的实现类。
具体AuthenticatorBase 的子类哪一个在上下文中被用作验证阀门,这依赖于部署文件中auth-method 元素的值。表 10.1 为 auth-method 元素的值,可以用于确定验证器。
表10.1: The authenticator implementation class
如果没有使用 auth-method 值,则认为 LoginConfig 对象的 auth-method 属性值为NONE,那么NonLoginAuthenticator类将被使用。
因为验证器类只在运行时知道,所以类是动态加载的。 StandardContext类使用org.apache.catalina.startup.ContextConfig类来配置StandardContext实例的许多设置。 此配置包括实例化验证器类并将该实例与上下文相关联。本章应用Demo附带着一个简单的上下文配置类:ex10.pyrmont.core.SimpleContextConfig。 正如你所见,这个类的实例负责动态加载BasicAuthenticator类,实例化它,并将其安装为StandardContext实例中的一个阀门。
注意:我们将在第15章讨论org.apache.catalina.startup.ContextConfig类。
10.6 应用Demo
本章附带的应用程序中使用了几个与安全约束相关的Catalina类。还使用了类似于第9章中的SimplePipeline,SimpleWrapper和SimpleWrapperValve类。此外,SimpleContextConfig类与第9章中的SimpleContextConfig类也相似,除了它具有authenticatorConfig()方法,该方法向StandardContext添加一个BasicAuthenticator实例 。 这2个应用Demo还使用了PrimitiveServlet和ModernServlet。 这些类在2个附带的应用Demo中使用。
第1个Demo中使用ex10.pyrmont.startup.Bootstrap1和ex10.pyrmont.realm.SimpleRealm 2个类;第二个Demo中使用了ex10.pyrmont.startup.Bootstrap2 ex10.pyrmont.realm.SimpleUserDatabaseRealm。每个类将在下面子章节中介绍。
10.6.1 ex10.pyrmont.core.SimpleContextConfig类
Listing10.1中的SimpleContextConfig类与第9章中的SimpleContextConfig类似。org.apache.catalina.core.StandardContext实例需要将其配置属性设置为true。 但是,本章中的SimpleContextConfig类添加了从lifeCycleEvent()方法调用的authenticatorConfig()方法。 authenticatorConfig()方法实例化BasicAuthenticator类,并将其作为阀门添加到StandardContext实例管道中。
Listing 10.1: The SimpleContextConfig class
package ex10.pyrmont.core;import org.apache.catalina.Authenticator;import org.apache.catalina.Context;import org.apache.catalina.Lifecycle;import org.apache.catalina.LifecycleEvent;import org.apache.catalina.LifecycleListener;import org.apache.catalina.Pipeline;import org.apache.catalina.Valve;import org.apache.catalina.core.StandardContext;import org.apache.catalina.deploy.SecurityConstraint;import org.apache.catalina.deploy.LoginConfig;public class SimpleContextConfig implements LifecycleListener { private Context context; public void lifecycleEvent(LifecycleEvent event) { if (Lifecycle.START_EVENT.equals(event.getType())) { context = (Context) event.getLifecycle(); authenticatorConfig(); context.setConfigured(true); } } private synchronized void authenticatorConfig() { // Does this Context require an Authenticator? SecurityConstraint constraints[] = context.findConstraints(); if ((constraints == null) || (constraints.length == 0)) return; LoginConfig loginConfig = context.getLoginConfig(); if (loginConfig == null) { loginConfig = new LoginConfig("NONE", null, null, null); context.setLoginConfig(loginConfig); } // 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; } // Has a Realm been configured for us to authenticate against? if (context.getRealm() == null) { return; } // 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) { } }}
开始,authenticatorConfig()方法检查在相关联的上下文中是否存在安全约束。如果没有,则该方法直接返回而不安装验证器。
// Does this Context require an Authenticator?SecurityConstraint constraints[] = context.findConstraints();if ((constraints == null) || (constraints.length == 0)) return;
如果存在安全约束,则继续检测上下文中是否存在LoginConfig对象,如果不存在,则创建一个:
LoginConfig loginConfig = context.getLoginConfig();if (loginConfig == null) { loginConfig = new LoginConfig("NONE", null, null, null); context.setLoginConfig(loginConfig);}
然后,authenticatorConfig()方法检查StandardContext对象管道中的基本阀门或附加阀门是否是验证器。 由于上下文只能有一个验证器,所以如果其中一个阀门是认证器,则authenticatorConfig()方法将返回。
// 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; }
然后它检查领域(Realm)是否已与上下文相关联。 如果没有发现领域,则不需要安装验证器,因为用户不能被认证:
// Has a Realm been configured for us to authenticate against?if (context.getRealm() == null) { return;}
此时,authenticatorConfig()方法将动态加载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) {}
10.6.2 ex10.pyrmont.realm.SimpleRealm类
Listing 10.2中的SimpleRealm类演示了领域是如何工作。这个类在本章的第一个Demo中使用,包含两个硬编码的用户名和密码。
Listing 10.2: The SimpleRealm Class
package ex10.pyrmont.realm;import java.beans.PropertyChangeListener;import java.security.Principal;import java.security.cert.X509Certificate;import java.util.ArrayList;import java.util.Iterator;import org.apache.catalina.Container;import org.apache.catalina.Realm;import org.apache.catalina.realm.GenericPrincipal;public class SimpleRealm implements Realm { public SimpleRealm() { createUserDatabase(); } private Container container; private ArrayList users = new ArrayList(); public Container getContainer() { return container; } public void setContainer(Container container) { this.container = container; } public String getInfo() { return "A simple Realm implementation"; } public void addPropertyChangeListener(PropertyChangeListener listener) { } 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()); } public Principal authenticate(String username, byte[] credentials) { return null; } public Principal authenticate(String username, String digest, String nonce, String nc, String cnonce, String qop, String realm, String md5a2) { return null; } public Principal authenticate(X509Certificate certs[]) { return null; } public boolean hasRole(Principal principal, String role) { if ((principal == null) || (role == null) || !(principal instanceof GenericPrincipal)) return (false); GenericPrincipal gp = (GenericPrincipal) principal; if (!(gp.getRealm() == this)) return (false); boolean result = gp.hasRole(role); return result; } public void removePropertyChangeListener(PropertyChangeListener listener) { } private User getUser(String username, String password) { Iterator iterator = users.iterator(); while (iterator.hasNext()) { User user = (User) iterator.next(); if (user.username.equals(username) && user.password.equals(password)) return user; } return null; } private void createUserDatabase() { 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); } class User { public User(String username, String password) { this.username = username; this.password = password; } public String username; public ArrayList roles = new ArrayList(); public String password; public void addRole(String role) { roles.add(role); } public ArrayList getRoles() { return roles; } }}
SimpleRealm类实现了Realm接口。 在构造函数中调用createUserDatabase()方法创建2个用户。 在内部,用户由内部类User表示。 第一个用户具有用户名ken和密码blackcomb。 这个用户有两个角色:manager和programmer。 第二个用户的用户名和密码分别为cindy和bamboo。 这个用户拥有programmer的角色。 然后,这2个用户被添加到变量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);
SimpleRealm类提供了四种重载验证方法中的1个实现:
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()); }
此authenticate()方法由验证器调用。 如果用户名和密码作为参数传递的用户不是有效的用户,则返回null。否则,它返回表示用户的Principal对象。
10.6.3 ex10.pyrmont.realm.SimpleUserDatabaseRealm类
SimpleUserDatabaseRealm类表示更复杂的领域。 它没在代码中存储用户列表。相反,它读取conf目录中的tomcat-users.xml文件,并将内容加载到内存中。然后针对该列表进行认证。 在随附zip文件的conf目录中,可以找到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="password" roles="admin,manager"/></tomcat-users>
类SimpleUserDatabaseRealm 代码如Listing 10.3:
Listing 10.3: The SimpleUserDatabaseRealm class
package ex10.pyrmont.realm;// modification of org.apache.catalina.realm.UserDatabaseRealmimport java.security.Principal;import java.util.ArrayList;import java.util.Iterator;import org.apache.catalina.Group;import org.apache.catalina.Role;import org.apache.catalina.User;import org.apache.catalina.UserDatabase;import org.apache.catalina.realm.GenericPrincipal;import org.apache.catalina.realm.RealmBase;import org.apache.catalina.users.MemoryUserDatabase;public class SimpleUserDatabaseRealm extends RealmBase { protected UserDatabase database = null; protected static final String name = "SimpleUserDatabaseRealm"; protected String resourceName = "UserDatabase"; public Principal authenticate(String username, String credentials) { // Does a user with this username exist? User user = database.findUser(username); if (user == null) { return (null); } // Do the credentials specified by the user match? // FIXME - Update all realms to support encoded passwords boolean validated = false; if (hasMessageDigest()) { // Hex hashes should be compared case-insensitive validated = (digest(credentials).equalsIgnoreCase(user.getPassword())); } else { validated = (digest(credentials).equals(user.getPassword())); } if (!validated) { return null; } ArrayList combined = new ArrayList(); Iterator roles = user.getRoles(); while (roles.hasNext()) { Role role = (Role) roles.next(); String rolename = role.getRolename(); if (!combined.contains(rolename)) { combined.add(rolename); } } Iterator groups = user.getGroups(); while (groups.hasNext()) { Group group = (Group) groups.next(); roles = group.getRoles(); while (roles.hasNext()) { Role role = (Role) roles.next(); String rolename = role.getRolename(); if (!combined.contains(rolename)) { combined.add(rolename); } } } return (new GenericPrincipal(this, user.getUsername(), user.getPassword(), combined)); } // ------------------------------------------------------ Lifecycle Methods /** * Prepare for active use of the public methods of this Component. * * @exception LifecycleException if this component detects a fatal error * that prevents it from being started */ protected Principal getPrincipal(String username) { return (null); } protected String getPassword(String username) { return null; } protected String getName() { return this.name; } public void createDatabase(String path) { database = new MemoryUserDatabase(name); ((MemoryUserDatabase) database).setPathname(path); try { database.open(); } catch (Exception e) { } }}
在实例化SimpleUserDatabaseRealm类之后,必须调用createDatabase()方法。 createDatabase()方法实例化org.apache.catalina.users.MemoryUserDatabase读取并解析XML文档。
10.6.4 ex10.pyrmont.startup.Bootstrap1类
Bootstrap1在本章第一个Demo中使用,代码如Listing 10.4:
Listing 10.4: The Bootstrap1 Class
package ex10.pyrmont.startup;import ex10.pyrmont.core.SimpleWrapper;import ex10.pyrmont.core.SimpleContextConfig;import ex10.pyrmont.realm.SimpleRealm;import org.apache.catalina.Connector;import org.apache.catalina.Context;import org.apache.catalina.Lifecycle;import org.apache.catalina.LifecycleListener;import org.apache.catalina.Loader;import org.apache.catalina.Realm;import org.apache.catalina.Wrapper;import org.apache.catalina.connector.http.HttpConnector;import org.apache.catalina.core.StandardContext;import org.apache.catalina.deploy.LoginConfig;import org.apache.catalina.deploy.SecurityCollection;import org.apache.catalina.deploy.SecurityConstraint;import org.apache.catalina.loader.WebappLoader;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")); Connector connector = new HttpConnector(); Wrapper wrapper1 = new SimpleWrapper(); wrapper1.setName("Primitive"); wrapper1.setServletClass("PrimitiveServlet"); Wrapper wrapper2 = new SimpleWrapper(); wrapper2.setName("Modern"); wrapper2.setServletClass("ModernServlet"); Context context = new StandardContext(); // StandardContext's start method adds a default mapper context.setPath("/myApp"); context.setDocBase("myApp"); LifecycleListener listener = new SimpleContextConfig(); ((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(); securityCollection.addPattern("/"); securityCollection.addMethod("GET"); SecurityConstraint constraint = new SecurityConstraint(); constraint.addCollection(securityCollection); constraint.addAuthRole("manager"); LoginConfig loginConfig = new LoginConfig(); loginConfig.setRealmName("Simple Realm"); // add realm Realm realm = new SimpleRealm(); context.setRealm(realm); context.addConstraint(constraint); context.setLoginConfig(loginConfig); 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(); } }}
在Bootstrap1的main()方法中创建了PrimitiveServlet和ModernServlet对应的SimpleWrapper。
然后创建设置StandardContext,添加SimpleContextConfig监听器等基本如第9章一样。如下代码是新的:
// add constraintSecurityCollection securityCollection = new SecurityCollection();securityCollection.addPattern("/");securityCollection.addMethod("GET");
main()方法创建一个SecurityCollection对象并调用其addPattern()和addMethod()方法。addPattern()方法指定安全性的URL约束。addMethod()方法添加受此限制的方法约束。addMethod()方法获取GET,因此HTTP请求的GET方式将受此安全约束的制约。
接下来,main()方法实例化SecurityConstraint类并将其添加到集合中。它还设置了可以访问受限资源的角色。通过manager,那些拥有manager角色的用户将能够查看资源。注意在SimpleRealm类只有用户ken具有manager角色,他的密码是blackcomb。
SecurityConstraint constraint = new SecurityConstraint();constraint.addCollection(securityCollection);constraint.addAuthRole("manager");
接下来,创建了LoginConfig和SimpleRealm对象:
LoginConfig loginConfig = new LoginConfig();loginConfig.setRealmName("Simple Realm");// add realmRealm realm = new SimpleRealm();
然后,将realm, constraint和loginConfig对象和StandardContext相关联:
context.setRealm(realm);context.addConstraint(constraint);context.setLoginConfig(loginConfig);
接下来,启动context。这部分已在前面几章讨论过。
实际上,当前对PrimitiveServlet和ModernServlet的访问是受限的。 如果是用户请求任何servlet,他/她必须使用basic验证认证。只有在他/她键入正确的用户名和密码(在此,ken和blackcomb),他/她将被允许访问。
10.6.5 ex10.pyrmont.startup.Boo tstrap2类
Bootstrap2类启动第2个应用Demo。除了它使用SimpleUserDatabase实例作为领域关联到StandardContext外,几乎与Bootstrap1类类似。 要访问PrimitiveServlet和ModernServlet,正确的用户名和密码分别是:admin和password。
Listing 10.5: The Bootstrap2 class
package ex10.pyrmont.startup;import ex10.pyrmont.core.SimpleWrapper;import ex10.pyrmont.core.SimpleContextConfig;import ex10.pyrmont.realm.SimpleUserDatabaseRealm;import org.apache.catalina.Connector;import org.apache.catalina.Context;import org.apache.catalina.Lifecycle;import org.apache.catalina.LifecycleListener;import org.apache.catalina.Loader;import org.apache.catalina.Realm;import org.apache.catalina.Wrapper;import org.apache.catalina.connector.http.HttpConnector;import org.apache.catalina.core.StandardContext;import org.apache.catalina.deploy.LoginConfig;import org.apache.catalina.deploy.SecurityCollection;import org.apache.catalina.deploy.SecurityConstraint;import org.apache.catalina.loader.WebappLoader;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("PrimitiveServlet"); Wrapper wrapper2 = new SimpleWrapper(); wrapper2.setName("Modern"); wrapper2.setServletClass("ModernServlet"); Context context = new StandardContext(); // StandardContext's start method adds a default mapper context.setPath("/myApp"); context.setDocBase("myApp"); LifecycleListener listener = new SimpleContextConfig(); ((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(); securityCollection.addPattern("/"); securityCollection.addMethod("GET"); SecurityConstraint constraint = new SecurityConstraint(); constraint.addCollection(securityCollection); constraint.addAuthRole("manager"); LoginConfig loginConfig = new LoginConfig(); loginConfig.setRealmName("Simple User Database Realm"); // add realm Realm realm = new SimpleUserDatabaseRealm(); ((SimpleUserDatabaseRealm) realm).createDatabase("conf/tomcat-users.xml"); context.setRealm(realm); context.addConstraint(constraint); context.setLoginConfig(loginConfig); 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(); } }}
10.6.6 运行Demo
在 windows 下运行第一个Demo,可以在工作目录下面如下运行该程序:
java -classpath ./lib/servlet.jar;./lib/commons-collections.jar;./ex10.pyrmont.startup.Bootstrap1
在 Linux 下,使用冒号分开两个库:
java -classpath ./lib/servlet.jar:./lib/commons-collections.jar:./ex10.pyrmont.startup.Bootstrap1
在 windows 下运行第二个Demo,可以在工作目录下面如下运行该程序:
java-classpath ./lib/servlet.jar;./lib/commons-collections.jar;./lib/commons-digester.jar;./lib/commons-logging.jar;./ex10.pyrmont.startup.Bootstrap2
在 Linux 下,使用冒号分开两个库:
java-classpath ./lib/servlet.jar:./lib/commons-collections.jar:./lib/commons-digester.jar:./lib/commons-logging.jar :./ex10.pyrmont.startup.Bootstrap2
在二个Demo中,调用PrimitiveServlet和ModernServlet,可以分别使用下面的 URL 来请求:
http://localhost:8080/Primitive
http://localhost:8080/Modern
10.7 小结
安全性是servlet编程和servlet规范中的一个重要主题,通过提供安全相关的对象来满足安全的需要,例如主体(principal),角色(roles),安全约束(securityConstraint),登录配置(login Config)等。在本章中,我们已经学习了解到servlet容器如何解决安全性问题。
- How tomcat works——10 安全性
- How tomcat works——引言
- How tomcat works——3 连接器
- How tomcat works——5 容器
- How tomcat works——6 生命周期
- How tomcat works——11 StandardWrapper
- How tomcat works——12 StandardContext
- How tomcat works——15 Digester
- How tomcat works——18 部署
- How tomcat works——序言
- 【How Tomcat Works】深入剖析Tomcat——引言
- How tomcat works——4 Tomcat中默认连接器
- How tomcat works——7 日志记录器
- How tomcat works——8 类加载器(Loader)
- How tomcat works——9 session管理
- How tomcat works——13 Host 和 Engine
- How tomcat works——14 Server和 Service
- How tomcat works——16 关闭钩子
- POJ P2524 Ubiquitous Religions
- BottomSheetBehavior的坑
- gym 101194 china final Problem H. Great Cells(数学,想法题,好题)
- 畅通工程
- 关于股权,写给技术人的合伙攻略
- How tomcat works——10 安全性
- 图像分割 1.基于阈值的算法
- 初识Vue.js
- 微信小程序开发文档详细讲解
- LeetCode 42. Trapping Rain Water
- 1
- matplotlib.pyplot介绍
- STM32学习之路-AIRCR寄存器PRIGROUP位的配置<NIVC
- C#三层架构详解--以系统登录为例