Chapter 10 covers web application security constraints for restricting access to certain contents. You will learn entities related to security such as principals, roles, login config, authenticators, etc. You will also write two applications that install an authenticator valve in the StandardContext object and uses basic authentication to authenticate users.
主体和之前的一样,新加的内容是安全相关的东西,更具体来说,是授权相关。可以根据配置的账户密码信息限制用户访问。这个功能现在应该挺鸡肋了,因为一般的 App 都是将这部分功能坐在内部的 login service 中的,哪里会通过这种方式作授权啊,除非买现成的但是不提供授权服务,这也太蠢了吧。。。
publicinterfaceRealm{ 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[]); }
同时这个接口还包含 public boolean hasRole(Principal principal, String role); 方法。这个接口有一个抽象实现 org.apache.catalina.realm.RealmBase 还有几个具体实现都在同一个包下:JDBCRealm, JNDIRealm, MemoryRealm, and UserDatabaseRealm。默认使用的是 MemoryRealm,当 server 启动时,他会读取 tomcat-users.xml。
GenericPrincipal
java.security.Principal 代表 Principal 这个概念,具体实现为 org.apache.catalina.realm.GenericPrincipal。GenericPrincipal 必须关联一个 realm, 构造函数如下
publicGenericPrincipal(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); } }
Principal 中也包含 hasRole() 方法,你可以传入 * 作为参数检测是否包含任意 role 的意思。
LoginConfig
login config 包含 realm name 由 org.apache.catalina.deploy.LoginConfig 这个 final class 表示. LoginConfig 包含 realm 和 authentication 的信息,auth name 必须是 BASIC, DIGEST, FORM, or CLIENT-CERT。
privatesynchronizedvoidauthenticatorConfig(){ // 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) { } } }
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; } returnnull; }
privatevoidcreateUserDatabase(){ User user1 = new User("ken", "blackcomb"); user1.addRole("manager"); user1.addRole("programmer"); User user2 = new User("cindy", "bamboo"); user2.addRole("programmer");
protected UserDatabase database = null; protectedstaticfinal 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) { returnnull; }
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)); }