How Tomcat works 10: Security
1. 概述:
WebApp的资源的访问限制可以通过web.xml文件来支撑。Servlet容器可以通过叫做Authenticator valve类来支持。Authenticator valve会调用context's realms的authenticate方法校验user
2. Realm类
(1)一个Realm通常附属于一个context, 一个container只能有一个realm【container.setRealm(realm)】
(2)默认下,Tomcat的默认认证信息存储在tomcat-users.xml中。当然也可以使用别的认证资源,如database
(3)Realm接口包含四个overload方法
public interface 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 cters[]);
public boolean hasRole(Principal principal, String role);
}
3. GenericPrincipal:
(1)是Principal接口的实现类
public class GenericPrincipal implements Principal {
protected Realm realm;
private String name;
private String password;
private String[] roles;
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);
}
}
}
public boolean hasRole(String role) {
if("*".equals(role)) {
return true;
}
if(role == null) {
return false;
}
return (Arrays.binarySearch(roles, role)>=0);
}
@Override
public String getName() {
return name;
}
}
4. LoginConfig类
(1)包含了realm name和authentication方法authentication name必须是其中之一:BASIC,DIGEST,FORM,CLIENT-CERT
(2)部署过程中,Tomcat启动时读取web.xml文件,若包含了login-config元素, tomcat将创建以恶LoginConfig对象
5. Authenticator接口
(1)代表一个认证接口,没有任何方法,仅仅是一个标记
(2)UML
6. Bootstrap:
在启动过程中,使用context.getLoginConfig()->然后使用反射查询相应的authenticator,添加对应的valve到pipeline
private synchronized void authenticatorConfig() {
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) {
}
}