【问题标题】:Embedded Tomcat with basic authentication via code通过代码进行基本身份验证的嵌入式 Tomcat
【发布时间】:2017-01-27 01:48:55
【问题描述】:

我想在嵌入式 tomcat 中添加两个 servlet。在这种情况下,其中一个 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/* 访问。这不是..实际上我很困惑。我希望有人可以帮助我..

【问题讨论】:

    标签: java tomcat servlets basic-authentication


    【解决方案1】:

    回答这个问题可能有点晚了,但它可能对其他人有用。最近我在尝试使用 SpringBoot 为嵌入式 Tomcat 配置安全性时遇到了类似的问题,安全性约束根本不起作用,我的 Realm 根本没有被调用。 问题出在 org.apache.catalina.authenticator.SSLAuthenticator 中,这个阀门没有添加到 Tomcat 管道中,因此没有检查 SecurityConstraints。

    添加tomcatFactory.addContextValves(new SSLAuthenticator()); 解决了这个问题。

    如果需要Basic认证valve类是

    org.apache.catalina.authenticator.BasicAuthenticator

    嵌入式 tomcat 上双向 SSL 和安全领域的完整工作配置:

    Java 配置

    @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 阀门。

    【讨论】:

      【解决方案2】:

      问题是您正在尝试保护上下文。上下文是不安全的,即,

      tomcat.addContext("/", new File(".").getAbsolutePath());

      必须改为

      tomcat.addWebapp("/", new File(".").getAbsolutePath());

      您将不得不处理一些异常处理,因为添加一个新的网络应用程序可能会抛出一个ServletException,但它应该会按照您的预期工作。

      如果您不想加载默认值(即 MIME 类型映射、静态内容 servlet、JSP...),您可以简单地覆盖 getDefaultWebXmlListener 方法。

      tomcat = new Tomcat() {
      
          @Override
          public LifecycleListener getDefaultWebXmlListener() {
              return event -> {};
          }
      };
      

      【讨论】:

        猜你喜欢
        • 2017-09-18
        • 2018-01-02
        • 1970-01-01
        • 2016-06-17
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多