我想把两个servlet添加到嵌入式tomcat中。在这种情况下,其中一个servlet应该由基本身份验证“保护”。我想通过代码添加安全约束。关于这个Link,这应该不是很难。
我构建了一个测试场景:
项目:EmbeddedTomcatTest
-Source Packages
--tomcat.test
---ServletOne.java
---ServletTwo.java
---StartEmbeddedTomcat.java (contains main method)
-Test Packages
-Other Sources
--src/main/resources
---Tomcat-users.xml
-Project Files
--pom.xml
我的pom的依赖关系如下:
<dependencies>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>8.5.4</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
</dependencies>
到目前为止,我添加了一个带有两个servlet的嵌入式tomcat,这些servlet可以通过不同的url模式访问,工作正常。
public class StartEmbeddedTomcat {
private static final String AUTH_ROLE = "test";
public static void main(String[] args) throws LifecycleException {
Tomcat tomcat = new Tomcat();
tomcat.setPort(8080);
// adding a context
Context ctx = tomcat.addContext("/", new File(".").getAbsolutePath());
// Login Config
LoginConfig config = new LoginConfig();
config.setAuthMethod("BASIC");
// adding constraint with role "test"
SecurityConstraint constraint = new SecurityConstraint();
constraint.addAuthRole(AUTH_ROLE);
// add constraint to a collection with pattern /second
SecurityCollection collection = new SecurityCollection();
collection.addPattern("/second");
constraint.addCollection(collection);
ctx.setLoginConfig(config);
// does the context need a auth role too?
ctx.addSecurityRole(AUTH_ROLE);
ctx.addConstraint(constraint);
// add servlet with pattenr /first and /second
Tomcat.addServlet(ctx, "one", new ServletOne());
ctx.addServletMapping("/first", "one");
Tomcat.addServlet(ctx, "two", new ServletTwo());
ctx.addServletMapping("/second", "two");
// add tomcat users to realm
String path = "tomcat-users.xml";
URL uri = ServletOne.class.getClassLoader().getResource(path);
MemoryRealm realm = new MemoryRealm();
realm.setPathname(uri.toString());
tomcat.getEngine().setRealm(realm);
tomcat.start();
tomcat.getServer().await();
}
}
tomcat-users.xml
不是很特别。
<tomcat-users>
<role rolename="manager-gui"/>
<role rolename="test"/>
<user name="admin" password="" roles="manager-gui"/>
<user name="test" password="test" roles="test"/>
</tomcat-users>
我认为servlet的代码并不重要,因为它们只响应“Im servlet one”或“Im servlet two”。
在我看来,整个申请或至少每个/second
的请求都应受到保护。我做错了什么?还有什么我要做的吗?
---- ---编辑
今天我发现这个堆栈溢出question。我在我的测试场景中添加了noauthCTX.setAltDDName("Path\\to\\web.xml");
。 web.xml看起来像:
<web-app>
<display-name>Test Service</display-name>
<servlet>
<servlet-name>ServletOne</servlet-name>
<servlet-class>tomcat.test.test.ServletOne</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>ServletOne</servlet-name>
<url-pattern>/servletOne/*</url-pattern>
</servlet-mapping>
</web-app>
我认为我的servlet也可以通过localhost:8080/servletOne/*
访问。它不是..实际上我很困惑。我希望有人可以帮助我..
答案 0 :(得分:0)
问题在于您正在尝试保护上下文。上下文是不可保证的,即
tomcat.addContext("/", new File(".").getAbsolutePath());
必须更改为
tomcat.addWebapp("/", new File(".").getAbsolutePath());
您将不得不处理一些异常处理,因为添加新的网络应用程序可以抛出ServletException
,但是它应该随后按预期工作。
如果您不想加载默认值(即MIME类型映射,静态内容servlet,JSP ......),您可以简单地覆盖getDefaultWebXmlListener
方法。
tomcat = new Tomcat() {
@Override
public LifecycleListener getDefaultWebXmlListener() {
return event -> {};
}
};
答案 1 :(得分:0)
回答这个问题可能有点晚了,但对其他人来说可能有用。最近我在尝试使用SpringBoot配置嵌入式Tomcat的安全性时遇到了类似的问题,安全约束根本不起作用,我的Realm根本没有被调用。 问题原来是在org.apache.catalina.authenticator.SSLAuthenticator中,这个阀门没有添加到Tomcat管道中,因此没有检查SecurityConstraints。
添加tomcatFactory.addContextValves(new SSLAuthenticator());
解决了这个问题。
如果需要基本认证,则阀门类是
org.apache.catalina.authenticator.BasicAuthenticator
嵌入式tomcat上的双向SSL和安全领域的完整工作配置:
Java Config
@Configuration
public class EmbeddedTomcatConfiguration {
private static final String LOGIN_CONFIG_AUTH_METHOD = "CLIENT-CERT";
@Bean
public RealmBase createPkiRealm() {
//Create your custom realm or use some standard one here
return realm;
}
@Bean
public EmbeddedServletContainerCustomizer createTomcatCustomizer(RealmBase pkiRealm) {
return (container) -> {
TomcatEmbeddedServletContainerFactory tomcatFactory = (TomcatEmbeddedServletContainerFactory) container;
//tomcatFactory.addAdditionalTomcatConnectors(createSslConnector()); //connector is configured in application.properties
tomcatFactory.addContextCustomizers(createTomcatContextCustomizer(pkiRealm));
tomcatFactory.addEngineValves(new SingleSignOn());
tomcatFactory.addContextValves(new SSLAuthenticator()); //this is where PKI realm is invoked
};
}
private TomcatContextCustomizer createTomcatContextCustomizer(RealmBase pkiRealm) {
return (context) -> {
context.setRealm(pkiRealm);
context.setLoginConfig(createLoginConfig());
context.addSecurityRole("new_role");
context.addConstraint(createSecurityConstraint());
};
}
private LoginConfig createLoginConfig() {
LoginConfig config = new LoginConfig();
config.setAuthMethod(LOGIN_CONFIG_AUTH_METHOD);
return config;
}
private SecurityConstraint createSecurityConstraint() {
SecurityConstraint securityConstraint = new SecurityConstraint();
securityConstraint.setDisplayName("Employee certificate required");
SecurityCollection securityCollection = new SecurityCollection("sec_collection");
securityCollection.addPattern("/test/*");
securityConstraint.addCollection(securityCollection);
securityConstraint.addAuthRole("new_role");
securityConstraint.setAuthConstraint(true);
securityConstraint.setUserConstraint("CONFIDENTIAL");
return securityConstraint;
}
}
带有新SSL连接器配置的application.properties
server.port=8443
server.ssl.enabled=true
server.ssl.client-auth=need
server.ssl.key-store=classpath:certs/serverkeystore.p12
server.ssl.key-store-password=changeit
server.ssl.key-store-type=PKCS12
server.ssl.key-alias=serverkey
server.ssl.trust-store=classpath:certs/servertruststore.jks
server.ssl.trust-store-password=changeit
server.ssl.trust-store-type=jks
使用独立的Tomcat和用xml编写的相同配置,不需要额外的SSLAuthenticator阀门。