How Tomcat works 之The Application 应用
来源:互联网 发布:成都和北京旅游 知乎 编辑:程序博客网 时间:2024/05/16 12:54
这里我想说 这里的翻译 基本上是按照原文翻译的,但是一些语句 我在理解的情况 尽可能的以简单的中文翻译出来。其实想到翻译 是因为看到国人的写的自己不懂 不理解 看了原版发现 竟然这么简单 因此下了决心 再加上近期在搞Tomcat这方面的开发 所以就顺便翻译了 希望各位看客 尽情拍砖 。最后还是想说 国人喜欢将简单地东西复杂化 比如数据结构 看了国文版的 简单蛋疼 原版的 一目了然。哎!也许是我语文功底不行。闲话少说。
这章的应用使用了几个与安全约束相关的Catalina类。这章与前一章都安排了SimpleWrapper类,SimplePipeline类和SimpleWrapperValue类。除此之外,SimpleContextConfig类与第九章的相似,但是这章新增了AuthentictorConfig方法,用来将一个BasicAUthentictor实例添加到StandardContext中。两章的应用都使用了两个Servlet:PrimitiveServlet和ModernServlet。
在第一个应用中有两个类,分别是Bootstrap1和SimpleRealm。第二个应用程序使用BootStrap2和SimpleUserDatabaseRealm。每个类将在下面的章节中解释。
ex10.pyrmont.core.SimpleContextConfig类
这一章节的SimpleContextConfig类与第九章的大致相似,由SimpleContext实例设置配置属性为true。只不过这章的SimpleContextConfig类的增加由lifeCycleEvent方法调用的AuthentictorConfig方法。这个方法实例化BasicAuthentictor类并作为一个Value添加到StandardContext实例的管道中。
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) {
}
}
}
AuthentictorConfig方法开始检查是否有与context关联的安全约束。如果没有就返回null。代码如下:
// Does this Context require an Authenticator?
SecurityConstraint constraints[] = context.findConstraints();
if ((constraints == null) || (constraints.length == 0))
return;
如果有安全约束,AuthentictorConfig方法就检查context是否有一个LonginConfig对象。如果没有就创建一个。
LoginConfig loginConfig = context.getLoginConfig();
if (loginConfig == null) {
loginConfig = new LoginConfig("NONE", null, null, null);
context.setLoginConfig(loginConfig);
}
这个方法继续检查这个StandardContext对象的管道中的基本值或者另外其他的Value是否是一个管道。由于一个上下文只能有一个校验器,值中的一个将作为校验器返回。
// 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是否与一个context关联。如果没有realm关联,如果没有,就没必要安装一个校验器,因为用户不能被再一次校验。
// Has a Realm been configured for us to authenticate against?
if (context.getRealm() == null) {
return;
}
在这点上,authentictorConfig方法将动态加载BasicAuthentictor类,并创建这个类的实例,将它作为一个Value添加到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) { }
}
ex10.pyrmont.realm.SimpleRealm类
这节演示了一个realm类如何工作的。这个类用于第一个应用程序中并包含了两个指定的用户名和密码。完整代码如下:
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 userl = new User("ken", "blackcomb");
user1.addRole("manager");
user1.addRole("programmer");
User user2 = new User("cindy", "bamboo");
user2.addRole("programmer");
users.add(userl);
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方法,创建了两个用户。在内部,User是一个内部类。方法解释这里不细说,看了代码就都知道了。
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);
这个类提供了四个重载校验方法之一实现。代码如下:
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());
}
这个校验方法被一个校验器调用。如果传递的用户名和密码是一个无效用户就返回null值。否则,返回一个Principal对象表示用户。
ex10.pyrmont.realm.SimpleUserDatabaseRealm类
SimpleUserDatabaseRealm类代表一个更加复杂的realm。它不存储用户列表。相反,它读取conf目录下的tomcat-users.xml文件并加载内容到内存中。对应于这个list的校验器也创建了。在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="rolel" password="tomcat" roles="rolel"/>
<user username="both" password="tomcat" roles="tomcat,rolel"/>
<user username="admin" password="password" roles="admin,manager"/>
</tomcat-users>
SimpleUserDatabaseRealm类 代码如下:
package ex10.pyrmont.realm;
// modification of org.apache.catalina.realm.UserDatabaseRealm
import 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?
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));
}
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方法并将路径传递给包含用户列表的xml文档。这个方法实例化了MemoryUserDatabase类并执行读和解析xml文档。
ex10.pyrmont.startup.Bootstrap1 类
BootStrap1类启动了第一个应用程序。代码如下:
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) {
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();
}
}
}
这个类的主要方法是创建两个SimpleWrapper对象并调用了setName和setServletClass方法。对于第一个SimpleWrapper对象,你将Primitive方法传递给它的setName方法并将PrimitiveServlet传递给它的setServletClass方法。第二个SimpleWrapper对象取得modern和ModernServlet。
稍后又创建了一个StandardContext对象,设置它的路径和文档base,并增加了SimpleContextConfig类型的监听器。之后在StandardContext对象中安装了一个BasicAuthentictor。下一步,它为StandardContext添加了一个加载器和两个Servlet映射。大多数代码与第九章相似,新的代码如下:
// add constraint
SecurityCollection securityCollection = new SecurityCollection();
securityCollection.addPattern("/");
securityCollection.addMethod("GET");
main方法创建了一个SecurityCollection对象并调用了它的addPattern方法和addMethod方法。addPattern方法指明了安全约束将适用于哪个URL。addMethod方法增加一个方法将由受制于这个约束。addMethod方法获取GET,GET的HTTP请求将受制于这个约束。
下一步,main方法实例化了SecurityConstraint类并将其添加到集合中。它也设置角色能够访问受限资源。通过传递Manager,Manager角色的用户可以看到这些资源。注意SimpleRealm类仅有一个用户有Manager角色,ken。他的密码是blackcomb。
SecurityConstraint constraint = new SecurityConstraint();
constraint.addCollection(securityCollection);
constraint.addAuthRole("manager");
下一步,main方法创建了一个LoginConfig对象和SimpleRealm对象。
LoginConfig loginConfig = new LoginConfig();
loginConfig.setRealmName("Simple Realm");
// add realm
Realm realm = new SimpleRealm();
稍后,它使realm,constraint和loginConfig对象与StandardContext对象关联。
context.setRealm(realm);
context.addConstraint(constraint);
context.setLoginConfig(loginConfig);
下一步,启动context。这里已经在上一章节中讲过。
结果,对PrimitiveServlet和ModernServlet的访问受限。如果用户访问上述的Servlet,他必须使用basic校验器。只有在输入正确的用户名和密码,用户才被允许访问。
ex10.pyrmont.startup.BootStrap2类
这个类开启了第二个应用,与第一个应用相似,除了它使用了SimpleUserDatabase的实例作为与StandardContext相关的realm。为了访问PrimitiveServlet和ModernServlet,用户名和密码必须分别对应正确的用户名和密码。
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) {
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.addMethodf"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();
}
}
}
总结
安全在Servlet编程中是一个重要的主题。Servlet使用Security满足安全需求。,与Security相关的对象,比如principle,roles,安全约束,登陆配置等等。在这一章节中学会了Servlet容器如何解决这个问题。
- How Tomcat works 之The Application 应用
- How Tomcat works之第十一章之The ServletConfig 对象
- How Tomcat works之第十一章 Allocate The Servlet
- How Tomcat works 之 StandardWrapper 标准包装
- How Tomcat works之StandardContext 标准上下文
- How Tomcat works之 Host and Engine
- How Tomcat Works之ex01时序图
- How the Application Server's Web Container Works
- How Tomcat Works 1
- How Tomcat Works 2
- Books - How Tomcat works
- How Tomcat Works 5
- How Tomcat Works 9
- How Tomcat Works 6
- How Tomcat Works 8
- How Tomcat Works 11
- How Tomcat Works 12
- How Tomcat Works 13
- weblogic管理4——命令行管理工具WLST
- 【vijos】P1082丛林探险DFS+剪枝版本
- coredata 及 Magical Record
- xslt描述xml研究
- 烦到爆的tomcat404
- How Tomcat works 之The Application 应用
- JavaScript 指南 - 使用对象
- leetcode Max Points on a Line
- 2011-2012年寒假(HDOJ学习记录)--QQ日志迁移
- google IP
- swift网络数据的请求
- ODBC 数据库编程(VC)
- 活动安排问题(贪心版)
- 【大话QT之十一】题外篇:万能脚本助Web执行底层Linux命令