【问题标题】:CXF Logging request & response with content filtering or masking soap fieldsCXF 使用内容过滤或屏蔽肥皂字段记录请求和响应
【发布时间】:2014-06-06 09:33:24
【问题描述】:

我想通过内容过滤记录来自某个特定端点的所有传入请求和响应。 IE。当我有这样的要求时:

<soap:Envelope xmlns:soap="http://www.w3.org/2001/12/soap-envelope">
<soap:Body>
  <m:ProcessPhoto xmlns:m="http://www.w3schools.com/photos">
    <m:Name>Apples</m:Name>
    <m:Description>Photo with some apples in it</m:Description>
    <!-- large encoded binary below -->
    <m:Photo>anVzdCBhIHJhbmRvbSB0ZXh0DQpqdXN0IGEgcmFuZG9tIHRleHQNCmp1c3QgYSByYW5kb20gdGV4dA0KanVzdCBhIHJhbmRvbSB0ZXh0DQpqdXN0IGEgcmFuZG9tIHRleHQNCmp1c3QgYSByYW5kb20gdGV4dA0KanVzdCBhIHJhbmRvbSB0ZXh0DQp3b3csIGkgZGlkbid0IHRob3VnaHQgdGhhdCBhbnlvbmUgd291bGQgYmUgaW50ZXJlc3RlZCBpbiBkZWNvZGluZyB0aGlzLiBjb25ncmF0cyE=</m:Photo>
  </m:ProcessPhoto>
</soap:Body>
</soap:Envelope>

我想过滤它,让它在日志中看起来像这样

<soap:Envelope xmlns:soap="http://www.w3.org/2001/12/soap-envelope">
<soap:Body>
  <m:ProcessPhoto xmlns:m="http://www.w3schools.com/photos">
    <m:Name>Apples</m:Name>
    <m:Description>Photo with some apples in it</m:Description>
    <m:Photo>hidden</m:Photo>
  </m:ProcessPhoto>
</soap:Body>
</soap:Envelope>

或完全移除 m:Photo 元素。

我发现 CXF 有一些 LoggingInInterceptor 和 LoggingOutInterceptor,我可以编写我自己的拦截器来做到这一点。然而,这需要做一些工作,所以我的问题是:你知道更好的开箱即用解决方案吗?

【问题讨论】:

    标签: java logging soap cxf filtering


    【解决方案1】:

    我编写了一个开源库,旨在有效地漂亮地打印 SOAP 消息:xml-formatter-components-cxf。功能包括:

    • XML 的高性能重新格式化
    • 高级过滤选项
      • 最大文本和/或 CDATA 节点大小
      • 在文本和/或 CDATA 节点中重新格式化 XML
      • 元素和/或属性内容的匿名化
      • 删除子树

    完成配置reference for spring.

    编辑:上面的库已经被重构为更多generic library,这对于大多数普通用例来说可能更好,即没有用 XML 包装的 XML。

    【讨论】:

    • 只支持 SOAP?是否有计划支持 JSON 消息或任何其他格式?
    • 我正在考虑制作一个 JSON 等价物,更符合编辑中链接的库的路线。
    • 迟到总比没有好,我已经创建了github.com/skjolber/json-log-filter
    • 完美,太棒了! .... 为什么我又需要这个? xD 我想我最终得到了一个自制的解决方案,但感谢您的更新!
    【解决方案2】:

    我遇到了类似的问题,我需要在输入请求中屏蔽密码。我对现有的 LogginInterceptor 和覆盖的格式方法做了一个小改动,并添加了我的方法来屏蔽密码。这是一个例子

    public class CustomLogInInterceptor extends LoggingInInterceptor {
    
        @Override
        protected String formatLoggingMessage(LoggingMessage loggingMessage) {
    
            String str = loggingMessage.toString();
    
            String output = maskPasswords(str);
            return(output);
        }
    
    
        private String maskPasswords(String str) {
    
                    final String[] keys = { "password", "passwords" };
                    for (String key : keys) {
                        int beginIndex = 0;
                        int lastIndex = -1;
                        boolean emptyPass = false;
                        while (beginIndex != -1
                                && (beginIndex = StringUtils.indexOfIgnoreCase(str, key,
                                        beginIndex)) > 0) {
    
                            beginIndex = StringUtils.indexOf(str, ">", beginIndex);
                            if (beginIndex != -1) {
                                char ch = str.charAt(beginIndex - 1);
                                if (ch == '/') {
                                    emptyPass = true;
                                }
                                if (!emptyPass) {
                                    lastIndex = StringUtils.indexOf(str, "<", beginIndex);
                                    if (lastIndex != -1) {
                                        String overlay = "*";
                                        String str2 = StringUtils.substring(str,
                                                beginIndex + 1, lastIndex);
                                        if (str2 != null && str2.length() > 1) {
                                            overlay = StringUtils.rightPad(overlay,
                                                    str2.length(), "*");
                                            str = StringUtils.overlay(str, overlay,
                                                    beginIndex + 1, lastIndex);
                                        }
                                    }
                                }
                                if (emptyPass) {
                                    emptyPass = false;
                                    lastIndex = beginIndex + 1;
                                } else {
                                    if (lastIndex != -1) {
                                        lastIndex = StringUtils
                                                .indexOf(str, ">", lastIndex);
                                    }
                                }
                            }
                            beginIndex = lastIndex;
                        }
                    }
                    return str;
    
                }
    
    }
    

    并在我的 cxf-bean.xml 中添加了自定义 cxf 日志记录 bean

    <bean id="kpInInterceptor" class="com.kp.util.CustomLogInInterceptor" />
    
    <cxf:bus>
            <cxf:inInterceptors>
                <ref bean="kpInInterceptor" />
            </cxf:inInterceptors>
            <cxf:inFaultInterceptors>
                <ref bean="kpInInterceptor" />
            </cxf:inFaultInterceptors>
    </cxf:bus>
    

    注意我使用 Apache commons-lang3 jar 进行字符串操作。同样,您也可以用于响应


    更新

    使用模式并使用属性使键可配置。

    拦截器

    public class CustomLogInInterceptor extends LoggingInInterceptor {
    
        private static final String MASK_PATTERN = "<\\s*{}\\s*>(.*)</\\s*{}\\s*>|<\\s*name\\s*>\\s*{}\\s*</\\s*name\\s*>\\s*<\\s*value\\s*>(.*)<";
    
        private Pattern pattern;
    
        private String[] keys;
    
        public void init() {
            StringBuilder builder = new StringBuilder();
            for (String str : keys) {
                builder.append(MASK_PATTERN.replace("{}", str));
                builder.append("|");
            }
            builder.setLength(builder.length()-1);
            pattern = Pattern.compile(builder.toString(), Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
        }
    
        public void setKeys(String[] keys) {
            this.keys = keys;
        }
    
    
        @Override
        protected String formatLoggingMessage(LoggingMessage loggingMessage) {
    
            String output = maskPasswords(loggingMessage.toString());
            return(output);
        }
    
    
        private String maskPasswords(String str) {
    
            Matcher matcher = pattern.matcher(str);
            final StringBuilder builder = new StringBuilder(str);
            while (matcher.find()) {
    
                int group = 1;
                while (group <= matcher.groupCount()) {
                    if (matcher.group(group) != null) {
                        for (int i = matcher.start(group); i < matcher.end(group); i++) {
                            builder.setCharAt(i, '*');
                        }
                    }
                    group++;
                }
            }
            return builder.toString();
    
        }
    
    }
    

    创建 Bean

    <bean id="kpInInterceptor" class="com.kp.util.CustomLogInInterceptor" init-method="init">
        <property name="keys">
            <array value-type="java.lang.String">
                <value>password</value>
                <value>accountId</value>
            </array>
        </property>
    </bean>
    

    示例输入

    <?xml version="1.0" encoding="UTF-8"?>
    <test>
        <hello>adffas</hello>
        <vsdsd>dfsdf</vsdsd>
        <password>sdfsfs</password>
        <sdfsfsf>sdfsfsf</sdfsfsf>
        <password>3434</password>
        <name>password</name>
        <value>sdfsfs</value>
        <password />
        <name>password</name>
        <value />
        <accountId>123456</accountId>
        <hello>
            <inner1>
                <password>
                    <password>sdfsfs</password>
                </password>
            </inner>
        </hello>
    </test>
    

    还有输出

    <?xml version="1.0" encoding="UTF-8"?>
    <test>
        <hello>adffas</hello>
        <vsdsd>dfsdf</vsdsd>
        <password>******</password>
        <sdfsfsf>sdfsfsf</sdfsfsf>
        <password>****</password>
        <name>password</name>
        <value>******</value>
        <password />
        <name>password</name>
        <value />
        <accountId>******</accountId>
        <hello>
            <inner1>
                <password>
                    <password>******</password>
                </password>
            </inner>
        </hello>
    </test>
    

    【讨论】:

    • 我会改写这个方法:String transform(String originalLogString) 它的 Javadoc 明确表示它是用于过滤消息的重写方法。
    • 自 org.apache.cxf.interceptor.LoggingInInterceptor 在 v3.2 中被弃用以来,是否有使用新的 org.apache.cxf.ext.logging.LoggingInInterceptor 的更新方法?
    • 有没有人有办法使用新版本的 cxf 做到这一点?
    • 是的,你可以在这里得到它:gist.github.com/poznachowski/0bfc5ab19bb8e29e6a60e77a8c22c533 用法很简单:loggingFeature.setSender(ContentFilteringSlf4jEventSender.of(INFO, Set.of("ns2:password")));
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-28
    • 1970-01-01
    • 2020-05-01
    • 2017-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多