《how tomcat work》 搬运工 Chapter 10: Security
security是当请求一些需要用户认证的content时起作用的,security通过Authenticator类来实现。Authenticator是继承了ValueBase的,在content的pipeline时触发认证,如果失败就不会进行下一步操作。而Authenticator则是通过realm来认证用户和检查用户权限。
Realm
realm就只是和container联系在一起,通过container的setRealm方法。
realm接口的一些认证的方法
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[]);
GenericPrincipal
GenericPrincipal则是和Realm联系在一起,在GenericPrincipal的构造函数给自己的Realm对象赋值。
GenericPrincipal的作用是检查认证过的用户是否有特定的role角色。
LoginConfig
LoginConfig是配置所用realm的名字和authentication的名字(有 BASIC, DIGEST, FORM, or CLIENT-CERT)用来显示给用户。
如果是form-based authentication类型,还会有登陆页和错误页的url。
Authenticator
Authenticator的类型有5种BasicAuthenticator,FormAuthenticator,DigestAuthenticator, SSLAuthenticator,NonLoginAuthenticator
因为Authenticator只能设置一次,所以会在Context中的ContextConfig里去读取配置文件和设置Authenticator。
具体代码实现。
首先是SimpleContextConfig模拟ContextConfig去设置Authenticator
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) { } } }
然后是SimpleRealm,这里仅实现了一个认证的方法
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; } } }
启动时的函数需要给context添加constraint(这个应该是某种约束,书里没有说),loginconfig,realm。
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);