未编译的 Java 程序可以运行,但会在 Coldfusion 从其 'jar' 文件中调用方法时产生错误?

Uncompiled Java program works, but produces an error, when Coldfusion calls a method from its 'jar' file?

项目:

将 JWT 转换为签名的加密 JWE

环境:

  1. Windows 10
  2. JDK1.8
  3. Apache Maven 3.5.4
  4. VSCode 1.25.1 具有所有必需的 Java 扩展
  5. 带有 JRE1.8 的 Adob​​e Coldfusion 11 应用程序服务器

依赖关系:

  1. nimbus-jose-jwt-6.0
  2. json-smart-2.3
  3. asm-1.0.2

问题:

首先,请理解我是Java的新手,但我对Coldfusion [CFML,喜欢PHP]有很好的理解。

当我从 VSCode 中 运行 我的程序时,我得到了序列化 JWT 字符串的预期结果。

当我尝试使用我的服务器端语言 Coldfusion [类似于 PHP] 从 'jar' 文件中访问该方法时,我从最后一行收到错误。

构造函数初始化成功,大部分方法调用执行。

每一行我都仔细测试过,绝大多数代码在外部调用'JwtSignEncrypt'class的'Encrypt()'方法时都能正常工作,但最后一行失败了以下错误。

The JWE object must be in an encrypted or decrypted state

堆栈跟踪的重要部分:

java.lang.IllegalStateException: The JWE object must be in an encrypted or decrypted state
at com.nimbusds.jose.JWEObject.ensureEncryptedOrDecryptedState(JWEObject.java:320)
at com.nimbusds.jose.JWEObject.serialize(JWEObject.java:456)
at com.chamika.jwt.JwtSignEncrypt.Encrypt(JwtSignEncrypt.java:153)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at coldfusion.runtime.StructBean.invoke(StructBean.java:508)
at coldfusion.runtime.CfJspPage._invoke(CfJspPage.java:2553)
at cftest412ecfm1275900201.runPage(C:\ColdFusion11\cfusion\wwwroot\establishmindfulness\unit-test\test41.cfm:129)
at coldfusion.runtime.CfJspPage.invoke(CfJspPage.java:246)
at coldfusion.tagext.lang.IncludeTag.handlePageInvoke(IncludeTag.java:736)
at coldfusion.tagext.lang.IncludeTag.doStartTag(IncludeTag.java:572)
at coldfusion.filter.CfincludeFilter.invoke(CfincludeFilter.java:65)
at coldfusion.filter.IpFilter.invoke(IpFilter.java:45)
at coldfusion.filter.ApplicationFilter.invoke(ApplicationFilter.java:466)
at coldfusion.filter.RequestMonitorFilter.invoke(RequestMonitorFilter.java:42)
at coldfusion.filter.MonitoringFilter.invoke(MonitoringFilter.java:40)
at coldfusion.filter.PathFilter.invoke(PathFilter.java:142)
at coldfusion.filter.LicenseFilter.invoke(LicenseFilter.java:30)
at coldfusion.filter.ExceptionFilter.invoke(ExceptionFilter.java:94)
at coldfusion.filter.ClientScopePersistenceFilter.invoke(ClientScopePersistenceFilter.java:28)
at coldfusion.filter.BrowserFilter.invoke(BrowserFilter.java:38)
at coldfusion.filter.NoCacheFilter.invoke(NoCacheFilter.java:58)
at coldfusion.filter.GlobalsFilter.invoke(GlobalsFilter.java:38)
at coldfusion.filter.DatasourceFilter.invoke(DatasourceFilter.java:22)
at coldfusion.filter.CachingFilter.invoke(CachingFilter.java:62)
at coldfusion.CfmServlet.service(CfmServlet.java:219)
at coldfusion.bootstrap.BootstrapServlet.service(BootstrapServlet.java:89)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at coldfusion.monitor.event.MonitoringServletFilter.doFilter(MonitoringServletFilter.java:42)
at coldfusion.bootstrap.BootstrapFilter.doFilter(BootstrapFilter.java:46)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at coldfusion.inspect.weinre.MobileDeviceDomInspectionFilter.doFilter(MobileDeviceDomInspectionFilter.java:121)
at coldfusion.bootstrap.BootstrapFilter.doFilter(BootstrapFilter.java:46)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:422)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1040)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:314)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)

JwtSignEncrypt.java

package com.chamika.jwt;

import java.util.*;

import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.*;
import com.nimbusds.jwt.*;


public class JwtSignEncrypt 
{

    String issuer;
    String subject;
    List<String> audience;
    Date expirationTime;
    Date notBeforeTime;
    Date issueTime;
    String jwtID;
    Map<String, Object> claim;

    public JwtSignEncrypt(final String iss, 
            final String sub,
            final String aud,
            final Date exp,
            final Date nbf,
            final Date iat,
            final String jti,
            Map<String, Object> cla) {
        if(iss != null) {        
            this.issuer = iss;
        }
        if(sub != null) {
            this.subject = sub;
        }
        if(aud != null) {
            List<String> items = Arrays.asList(aud.split("\s*,\s*")); 
            this.audience = items;
        }
        if(exp != null) {
            this.expirationTime = exp;
        }
        if(nbf != null) {
            this.notBeforeTime = nbf;
        }
        if(iat != null) {
            this.issueTime = iat;
        }
        if(jti != null) {
            this.jwtID = jti;
        }
        if(cla != null) {
            this.claim = cla;
        }
    }


    public String Encrypt(byte[] secretKeyEncoded) {

        String key = null;
        Object value = null;

        for (Map.Entry<String, Object> entry : claim.entrySet()) {
            key = entry.getKey();
            value = entry.getValue();
        }

        JWTClaimsSet claimsSet = new JWTClaimsSet.Builder().issuer(this.issuer).subject(this.subject).audience(this.audience).expirationTime(this.expirationTime).notBeforeTime(this.notBeforeTime).issueTime(this.issueTime).jwtID(this.jwtID).claim(key,value).build();

        String jweobject = "";
        JWSSigner signer;

        try {

            signer = new MACSigner(secretKeyEncoded);
            SignedJWT signedJWT = new SignedJWT(new JWSHeader(JWSAlgorithm.HS256), claimsSet);
            try {
                signedJWT.sign(signer);
            } catch (JOSEException e) {
                e.printStackTrace();
            }

            JWEObject jweObject = new JWEObject(
                new JWEHeader.Builder(JWEAlgorithm.DIR, EncryptionMethod.A256GCM)
                    .contentType("JWT") 
                    .build(),
                new Payload(signedJWT));

            try {
                jweObject.encrypt(new DirectEncrypter(secretKeyEncoded));

            } catch (KeyLengthException e) {
                e.printStackTrace();
            } catch (JOSEException e) {
                e.printStackTrace();
            }

            jweobject = jweObject.serialize();

        } catch (KeyLengthException e) {
            e.printStackTrace();
        }

        return jweobject;
    }

}

java 文件中的以下行发生错误:

jweObject.encrypt(new DirectEncrypter(secretKeyEncoded));

Java Coldfusion onRequestStart 方法中的加载程序:

  <cfset request.lckchamikajwtlibinit = true />

  <cfif NOT StructKeyExists(APPLICATION,"chamikajwtlib") OR request.appreload>
    <cftry>
      <cflock name="chamikajwtlib" type="exclusive" timeout="#request.writelocktimeout#">
        <cfset local.jbClasschamikajwt = "#request.filepathasset#\lib\chamika-jwt-sign-encrypt\chamika-jwt-sign-encrypt-1.0.2.jar" />
        <cfset local.javaloader = createObject('component','com.javaloader.JavaLoader') />
        <cfset application.chamikajwtlib = local.javaloader.init([local.jbClasschamikajwt]) />
      </cflock>
      <cfcatch>
        <cfset request.lckchamikajwtlibinit = false />
      </cfcatch>
    </cftry>
  </cfif>

  <cfif request.lckchamikajwtlibinit>
    <cflock NAME="chamikajwtliblck" TIMEOUT="#request.readlocktimeout#" TYPE="READONLY">
      <cfset request.chamikaJwtSignEncryptJar= application.chamikajwtlib />
    </cflock>
  <cfelse>
    <cfset request.chamikaJwtSignEncryptJar= "" />
  </cfif>

test.cfm

<cfscript>

  local = {};
  local.loader = request.chamikaJwtSignEncryptJar;
  local.issuer = JavaCast("string","https://openid.net");
  local.subject = JavaCast("string","Charles Robertson");
  local.audience = "https://app-one.com,https://app-two.com";
  local.expirationTime = createObject("java","java.util.Date").init().getTime() + 60 * 1000;
  local.expirationTime = createObject("java","java.util.Date").init(local.expirationTime);
  local.currentDateTime = createObject("java","java.util.Date").init();
  local.notBeforeTime = local.currentDateTime;
  local.issueTime = local.currentDateTime;
  local.jwtID = JavaCast("string",CreateUUID());
  local.claim = createObject("java", "java.util.LinkedHashMap").init();
  local.json = {forename="Charles",surname='Robertson'};
  local.claim['json'] = SerializeJson(local.json);
  local.JwtSignEncrypt = local.loader.create("com.chamika.jwt.JwtSignEncrypt").init(local.issuer,local.subject,local.audience,local.expirationTime,local.notBeforeTime,local.issueTime,local.jwtID,local.claim);

  local.keyGen = local.loader.create("javax.crypto.KeyGenerator").getInstance("AES");
  local.keyGen.init(256);
  local.secretKeyEncoded = local.keyGen.generateKey().getEncoded();

  local.jweString = local.JwtSignEncrypt.Encrypt(local.secretKeyEncoded);

  writeDump(var=local.jweString);

</cfscript>

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.chamika.jwt</groupId>
  <artifactId>chamika-jwt-app</artifactId>
  <packaging>jar</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>chamika-jwt-app</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>com.nimbusds</groupId>
      <artifactId>nimbus-jose-jwt</artifactId>
      <version>6.0</version>
    </dependency>
    <dependency>
        <groupId>net.minidev</groupId>
        <artifactId>json-smart</artifactId>
        <version>2.3</version>
    </dependency>
    <dependency>
    <groupId>net.minidev</groupId>
        <artifactId>asm</artifactId>
        <version>1.0.2</version>
    </dependency>
  </dependencies>
</project>

我已经在 'jar' 中正确地包含了所有必需的库。当我打包 'jar' 时,我还需要采取其他步骤吗?我正在使用一个 'pom.xml' 文件,有 3 个依赖项。出于某种原因,当我 运行:

nvm package

不包含依赖项。 因此,我转而使用 'jarsplice' 来捆绑依赖项。所有依赖项都可以正确、独立、外部访问。

问题:

  1. 为什么我在尝试生成已签名的文件时出现错误 加密的 JWT,当使用 Coldfusion 调用“.jar”中的方法时 文件?
  2. 为什么我的依赖项没有包含在打包的“.jar”中 文件?

更新:

这是我的 git 回购:

https://bitbucket.org/charlesrobertson/chamika-jwt-app/src/master/

我的 java class 基于官方文档片段:

https://connect2id.com/products/nimbus-jose-jwt/examples/signed-and-encrypted-jwt

  1. Why I am getting the error, when I try to generate the signed encrytped JWT, when using Coldfusion to call a method in the '.jar' file?

我在 CF11 上遇到了同样的错误。问题是 try/catch 代码基本上吞下了错误,因为 CF 不在屏幕上显示 e.printStackTrace() 的输出。它被发送到默认日志文件。所以您甚至不知道发生了异常 - 除非您检查 CF 日志。

错误处理完全取决于应用程序,但我的想法是如果该方法不能对错误做任何有用的事情,不妨让它冒泡并让调用者决定如何处理它。无论如何,一旦我摆脱了所有 try/catch 并向所有方法添加了 throws XYZException,就像这样:

public String Encrypt(byte[] secretKeyEncoded) throws KeyLengthException, JOSEException {
    // ...
    jweObject.encrypt(new DirectEncrypter(secretKeyEncoded));
    // ...
}

...我可以在屏幕上看到错误消息。 无法创建 AES/GCM/NoPadding 密码:非法密钥大小。 这意味着您无法创建 256 位(或更高)密钥,除非您安装了 Unlimited JCE files .解决方案是下载并安装 Unlimited JCE 文件并重新启动 CF。在那之后,jar 工作正常(有 3 个依赖项)。

  • 下载适用于您的 java 版本的文件。例如,对于 java 8 - jce_policy-8.zip
  • 备份 <java-home>\lib\security
  • 中现有的 local_policy.jarUS_export_policy.jar 文件
  • 解压缩文件并将新的 local_policy.jarUS_export_policy.jar 复制到 <java-home>\lib\security
  • 重新启动 CF 服务器(必需)
  1. Why are my dependencies not being included in the packaged '.jar' file? ​

Maven 不包括依赖项,除非它是 specified in the pom.xml,它不适用于此项目。我个人避免这样做。将所有东西打包到一个大的 uber jar 中很方便,但更难处理依赖项中的版本更改。我只是单独加载相关的罐子。