tomcat8.0 and tomcat8.5.6 WebappClassLoaderBase

2019-07-31 16:05发布

问题:

We update tomcat to 8.5.6 from 8.0.32 recently, and we meet a AccessControlException when try to load /opt/apache-tomcat-8.5.6_1/webapps/example/WEB-INF/classes/com/sun/xml/internal/ws/runtime/config/jaxb.properties, and I debug the source code between tomcat 8.5.6 and 8.0.32, it's different in org.apache.catalina.loader.WebappClassLoaderBase.findResource

Tomcat8.0

public URL findResource(final String name) {

    if (log.isDebugEnabled())
        log.debug("    findResource(" + name + ")");

    checkStateForResourceLoading(name);

    URL url = null;

    String path = nameToPath(name);

    ResourceEntry entry = resourceEntries.get(path);
    if (entry == null) {
        if (securityManager != null) {
            PrivilegedAction<ResourceEntry> dp =
                new PrivilegedFindResourceByName(name, path);
            entry = AccessController.doPrivileged(dp);
        } else {
            entry = findResourceInternal(name, path);
        }
    }
    if (entry != null) {
        url = entry.source;
        entry.webResource = null;
    }

    if ((url == null) && hasExternalRepositories) {
        url = super.findResource(name);
    }

    if (log.isDebugEnabled()) {
        if (url != null)
            log.debug("    --> Returning '" + url.toString() + "'");
        else
            log.debug("    --> Resource not found, returning null");
    }
    return url;
}

Tomcat8.5.6

public URL findResource(final String name) {

    if (log.isDebugEnabled())
        log.debug("    findResource(" + name + ")");

    checkStateForResourceLoading(name);

    URL url = null;

    String path = nameToPath(name);

    WebResource resource = resources.getClassLoaderResource(path);
    if (resource.exists()) {
        url = resource.getURL();
        trackLastModified(path, resource);
    }

    if ((url == null) && hasExternalRepositories) {
        url = super.findResource(name);
    }

    if (log.isDebugEnabled()) {
        if (url != null)
            log.debug("    --> Returning '" + url.toString() + "'");
        else
            log.debug("    --> Resource not found, returning null");
    }
    return url;
}

As you can see, tomcat8.0 load resource by AccessController.doPrivileged, but in tomcat8.5.6, it load the resource directly, I think that's why I got a Exception

java.security.AccessControlException: access denied 
("java.io.FilePermission" 
"/opt/apache-tomcat-8.5.6_1/webapps/example/WEB-INF/classes/com/sun/xml/internal/ws/runtime/config/jaxb.properties" 
"read")

java.lang.IllegalStateException: MASM0003: Default [ jaxws-tubes-default.xml ] configuration file was not loaded
        at com.sun.xml.internal.ws.assembler.MetroConfigLoader.init(MetroConfigLoader.java:133)
        at com.sun.xml.internal.ws.assembler.MetroConfigLoader.<init>(MetroConfigLoader.java:104)

this file is loaded by MetroConfigLoader,

private static JAXBContext createJAXBContext() throws Exception {
        return isJDKInternal()?(JAXBContext)AccessController.doPrivileged(new PrivilegedExceptionAction<JAXBContext>() {
            public JAXBContext run() throws Exception {
                return JAXBContext.newInstance(MetroConfig.class.getPackage().getName());
            }
        }, createSecurityContext()):JAXBContext.newInstance(MetroConfig.class.getPackage().getName());
    }

    private static AccessControlContext createSecurityContext() {
        PermissionCollection perms = new Permissions();
        perms.add(new RuntimePermission("accessClassInPackage.com.sun.xml.internal.ws.runtime.config"));
        perms.add(new ReflectPermission("suppressAccessChecks"));
        return new AccessControlContext(new ProtectionDomain[]{new ProtectionDomain((CodeSource)null, perms)});
    }

Does anyone meet same issue? or there is some other issues. thanks.

回答1:

After three days researching, now I use jaxws-rt instead of default implementation in JDK, and as you can read from the code in JDK:

private static JAXBContext createJAXBContext() throws Exception {
        return isJDKInternal()?(JAXBContext)AccessController.doPrivileged(new PrivilegedExceptionAction<JAXBContext>() {
            public JAXBContext run() throws Exception {
                return JAXBContext.newInstance(MetroConfig.class.getPackage().getName());
            }
        }, createSecurityContext()):JAXBContext.newInstance(MetroConfig.class.getPackage().getName());
}

If it's JDK internal, it will create the instance with specific privilege, and tomcat get resource by doPrivileged in tomcat8.0, but it's different in tomcat8.5, So it can't get the resource without privilege

java.security.AccessControlException: access denied ("java.io.FilePermission" 
"/opt/apache-tomcat-8.5.6_1/webapps/example/WEB-INF/classes/com/sun/xml/internal/ws/runtime/config/jaxb.properties" 
"read")

So I changed to external jaxws-rt, and it will create the instance directly. I just add jaxws-rt to pom.

 <dependency>
        <groupId>com.sun.xml.ws</groupId>
        <artifactId>jaxws-rt</artifactId>
        <version>2.2.10</version>
 </dependency>