1

一段时间以来,当我尝试升级到 Grails 3 时,我一直在尝试镜像我在 Grails 2 中的实现。

我需要使用“client-cert”身份验证方法支持基于 X509 证书的身份验证,也就是说,我只想在请求受保护的资源后提示输入证书。请参阅下面 Application.groovy 中的当前实现。

@Bean
EmbeddedServletContainerCustomizer containerCustomizer() throws Exception {

    return new EmbeddedServletContainerCustomizer() {
        @Override
        public void customize(ConfigurableEmbeddedServletContainer container) {
            TomcatEmbeddedServletContainerFactory tomcat = (TomcatEmbeddedServletContainerFactory) container
            tomcat.addConnectorCustomizers(
                    new TomcatConnectorCustomizer() {
                        @Override
                        public void customize(Connector connector) {
                            connector.setPort(8443)
                            connector.setSecure(true)
                            connector.setScheme("https")

                            Http11NioProtocol proto = (Http11NioProtocol) connector.getProtocolHandler()
                            proto.setMinSpareThreads(5)
                            proto.setSSLEnabled(true)
                            proto.setClientAuth("false")

                            proto.setKeystoreFile("/tmp/keys/app.jks")
                            proto.setKeystorePass("changeit")
                            proto.setKeystoreType("JKS")
                            proto.setKeyAlias("ssl_server")
                            proto.setTruststoreFile("/tmp/keys/app.jts")
                            proto.setTruststoreType("JKS")
                            proto.setTruststorePass("changeit")
                        }
                    })
            tomcat.addContextCustomizers(new TomcatContextCustomizer() {
                @Override
                public void customize(Context context) {
                    context.setPath("/myapp")
                    SecurityConstraint sc = new SecurityConstraint()
                    SecurityCollection securityCollection = new SecurityCollection()
                    securityCollection.setName("Protected")
                    securityCollection.addPattern("/*")

                    sc.addCollection(securityCollection)

                    sc.addAuthRole("mySecureConnection")
                    sc.setUserConstraint("CONFIDENTIAL")
                    context.addConstraint(sc)
                    context.addSecurityRole("mySecureConnection")
                    context.setRealm(new MySecurityRealm())

                    LoginConfig loginConfig = new LoginConfig()
                    loginConfig.setAuthMethod("CLIENT-CERT")
                    loginConfig.setRealmName("MySecurityRealm")
                    context.setLoginConfig(loginConfig)
                    sc.setAuthConstraint(true)

                }
            });
        }
    }

但是,无论我尝试使用多少种不同的方式来削减它,应用程序都不会在访问时请求证书(它应该基于我上面的全部捕获模式)。请注意,当clientAuth设置为 true 时,此机制会按预期工作;

proto.setClientAuth("true")

但这意味着总是要求一个证书,这最终不是我想要的(我打算更新上面的模式)。任何帮助将非常感激。

4

1 回答 1

0

在返回 Grails 3.3.x 以应用有效的方法之前,我自己在 vanilla tomcat 和 Spring Boot 级别进行了概念验证之后,我自己就开始工作了。我认为拼图中最重要的部分可能是添加了一个 tomcat 阀门组件(显然使用 SSLAuthenticator 实现),这是我能够设法让浏览器提示输入证书的唯一方法。然后,这要求我使用自定义领域从证书中检索主体(我目前不知道其他方法)。代码如下;

@Bean
public EmbeddedServletContainerFactory servletContainer() {
    final TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory();
    tomcat.addContextValves(new SSLAuthenticator());

    tomcat.addContextCustomizers(new TomcatContextCustomizer() {
        @Override
        public void customize(Context ctx) {

            String AUTH_ROLE = "mySecureRole";
            ctx.addSecurityRole(AUTH_ROLE);
            ctx.setRealm(new MySecurityRealm())

            LoginConfig config = new LoginConfig();
            config.setAuthMethod("CLIENT-CERT");
            config.setRealmName("MySecurityRealm");
            ctx.setLoginConfig(config);

            SecurityConstraint constraint = new SecurityConstraint();
            constraint.addAuthRole(AUTH_ROLE);
            SecurityCollection collection = new SecurityCollection();
            collection.addPattern("/secure");
            constraint.addCollection(collection);
            ctx.addConstraint(constraint);
        }
    })

    tomcat.addAdditionalTomcatConnectors(createConnector());
    return tomcat;
}


private Connector createConnector() {
    Connector connector = new Connector(TomcatEmbeddedServletContainerFactory.DEFAULT_PROTOCOL);

    connector.setPort(8443);
    connector.setSecure(true);
    connector.setScheme("https");

    Http11NioProtocol proto = (Http11NioProtocol) connector.getProtocolHandler();
    proto.setMinSpareThreads(5);
    proto.setSSLEnabled(true);
    proto.setClientAuth("false");
    proto.setSSLProtocol("all");

    proto.setKeystoreFile("/path/store.jks");
    proto.setKeystorePass("changeit");
    proto.setKeystoreType("JKS");
    proto.setKeyAlias("ssl_server");
    proto.setTruststoreFile("/path/store.jts");
    proto.setTruststoreType("JKS");
    proto.setTruststorePass("changeit");

    proto.setSSLVerifyDepth(2);
    return connector;
}

为了完整起见,我将保留连接器详细信息,但当然所有重要的事情都发生在上下文定制器中。现在,当我访问此 Web 应用程序时,不会提示我输入证书。这只发生在我访问正是我需要的/secure路径时。

于 2018-05-22T11:40:09.367 回答