嵌入式Tomcat,通过代码进行基本认证

时间:2016-09-19 11:24:27

标签: java tomcat servlets basic-authentication

我想把两个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/*访问。它不是..实际上我很困惑。我希望有人可以帮助我..

2 个答案:

答案 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阀门。