Repository: cxf Updated Branches: refs/heads/master e8a038340 -> f5d2a0332
[CXF-5311] Actually making JWS out streaming work, more refactoring will follow Project: http://git-wip-us.apache.org/repos/asf/cxf/repo Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/f5d2a033 Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/f5d2a033 Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/f5d2a033 Branch: refs/heads/master Commit: f5d2a0332c7a40d7b528fb1d32d10fe9ebb74fc2 Parents: e8a0383 Author: Sergey Beryozkin <sberyoz...@talend.com> Authored: Thu Jul 10 22:00:01 2014 +0100 Committer: Sergey Beryozkin <sberyoz...@talend.com> Committed: Thu Jul 10 22:00:01 2014 +0100 ---------------------------------------------------------------------- .../cxf/common/util/Base64OutputStream.java | 90 ++++++++++++++++++++ .../jws/AbstractJwsSignatureProvider.java | 12 +-- .../rs/security/oauth2/jws/JwsOutputStream.java | 40 +-------- .../oauth2/jwt/jaxrs/JwsWriterInterceptor.java | 9 +- .../oauth2/utils/Base64UrlOutputStream.java | 31 +++++++ .../jaxrs/security/jwt/JAXRSJweJwsTest.java | 4 +- 6 files changed, 140 insertions(+), 46 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cxf/blob/f5d2a033/core/src/main/java/org/apache/cxf/common/util/Base64OutputStream.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/cxf/common/util/Base64OutputStream.java b/core/src/main/java/org/apache/cxf/common/util/Base64OutputStream.java new file mode 100644 index 0000000..6ba8e95 --- /dev/null +++ b/core/src/main/java/org/apache/cxf/common/util/Base64OutputStream.java @@ -0,0 +1,90 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cxf.common.util; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; + +public class Base64OutputStream extends FilterOutputStream { + private byte[] lastChunk; + private boolean flushed; + private boolean urlSafe; + public Base64OutputStream(OutputStream out, boolean urlSafe) { + super(out); + this.urlSafe = urlSafe; + } + + @Override + public void write(int value) throws IOException { + byte[] bytes = ByteBuffer.allocate(Integer.SIZE / 8).putInt(value).array(); + write(bytes, 0, bytes.length); + } + + @Override + public void write(byte b[], int off, int len) throws IOException { + encodeAndWrite(b, off, len, false); + } + + private void encodeAndWrite(byte[] b, int off, int len, boolean finalWrite) throws IOException { + byte[] theChunk = lastChunk; + int lenToEncode = len; + if (theChunk != null) { + theChunk = newArray(theChunk, 0, theChunk.length, b, off, len); + lenToEncode = theChunk.length; + off = 0; + } else { + theChunk = b; + } + int rem = finalWrite ? 0 : lenToEncode % 3; + Base64Utility.encodeAndStream(theChunk, off, lenToEncode - rem, urlSafe, out); + + if (rem > 0) { + lastChunk = newArray(theChunk, lenToEncode - rem, rem); + } else { + lastChunk = null; + } + } + + @Override + public void flush() throws IOException { + if (flushed) { + return; + } + try { + Base64Utility.encodeAndStream(lastChunk, 0, lastChunk.length, urlSafe, out); + lastChunk = null; + } catch (Exception ex) { + throw new SecurityException(); + } + flushed = true; + } + private byte[] newArray(byte[] src, int srcPos, int srcLen) { + byte[] buf = new byte[srcLen]; + System.arraycopy(src, srcPos, buf, 0, srcLen); + return buf; + } + private byte[] newArray(byte[] src, int srcPos, int srcLen, byte[] src2, int srcPos2, int srcLen2) { + byte[] buf = new byte[srcLen + srcLen2]; + System.arraycopy(src, srcPos, buf, 0, srcLen); + System.arraycopy(src2, srcPos2, buf, srcLen, srcLen2); + return buf; + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/f5d2a033/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jws/AbstractJwsSignatureProvider.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jws/AbstractJwsSignatureProvider.java b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jws/AbstractJwsSignatureProvider.java index dc63a1e..b78c63b 100644 --- a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jws/AbstractJwsSignatureProvider.java +++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jws/AbstractJwsSignatureProvider.java @@ -21,9 +21,9 @@ package org.apache.cxf.rs.security.oauth2.jws; import java.io.OutputStream; import java.util.Set; -import org.apache.cxf.common.util.Base64Utility; import org.apache.cxf.rs.security.oauth2.jwt.JwtHeaders; import org.apache.cxf.rs.security.oauth2.jwt.JwtTokenReaderWriter; +import org.apache.cxf.rs.security.oauth2.utils.Base64UrlUtility; public abstract class AbstractJwsSignatureProvider implements JwsSignatureProvider { private Set<String> supportedAlgorithms; @@ -48,20 +48,20 @@ public abstract class AbstractJwsSignatureProvider implements JwsSignatureProvid @Override public JwsOutputStream createJwsStream(OutputStream os, String contentType) { - JwtHeaders headers = new JwtHeaders(); + JwtHeaders headers = prepareHeaders(null); if (contentType != null) { headers.setContentType(contentType); } - headers = prepareHeaders(headers); JwsSignatureProviderWorker worker = createJwsSignatureWorker(headers); + JwsOutputStream jwsStream = new JwsOutputStream(os, worker); try { byte[] headerBytes = new JwtTokenReaderWriter().headersToJson(headers).getBytes("UTF-8"); - Base64Utility.encodeAndStream(headerBytes, 0, headerBytes.length, os); - os.write(new byte[]{'.'}); + Base64UrlUtility.encodeAndStream(headerBytes, 0, headerBytes.length, jwsStream); + jwsStream.write(new byte[]{'.'}); } catch (Exception ex) { throw new SecurityException(ex); } - return new JwsOutputStream(os, worker); + return jwsStream; } protected abstract JwsSignatureProviderWorker createJwsSignatureWorker(JwtHeaders headers); http://git-wip-us.apache.org/repos/asf/cxf/blob/f5d2a033/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jws/JwsOutputStream.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jws/JwsOutputStream.java b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jws/JwsOutputStream.java index 8c7734d..26268d1 100644 --- a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jws/JwsOutputStream.java +++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jws/JwsOutputStream.java @@ -26,7 +26,6 @@ import java.nio.ByteBuffer; import org.apache.cxf.rs.security.oauth2.utils.Base64UrlUtility; public class JwsOutputStream extends FilterOutputStream { - private byte[] lastNonEncodedDataChunk; private boolean flushed; private JwsSignatureProviderWorker signature; public JwsOutputStream(OutputStream out, JwsSignatureProviderWorker signature) { @@ -47,29 +46,8 @@ public class JwsOutputStream extends FilterOutputStream { } catch (Throwable ex) { throw new SecurityException(); } - encodeAndWrite(b, off, len, false); + out.write(b, off, len); } - - private void encodeAndWrite(byte[] encryptedChunk, int off, int len, boolean finalWrite) throws IOException { - byte[] theChunk = lastNonEncodedDataChunk; - int lenToEncode = len; - if (theChunk != null) { - theChunk = newArray(theChunk, 0, theChunk.length, encryptedChunk, off, len); - lenToEncode = theChunk.length; - off = 0; - } else { - theChunk = encryptedChunk; - } - int rem = finalWrite ? 0 : lenToEncode % 3; - Base64UrlUtility.encodeAndStream(theChunk, off, lenToEncode - rem, out); - - if (rem > 0) { - lastNonEncodedDataChunk = newArray(theChunk, lenToEncode - rem, rem); - } else { - lastNonEncodedDataChunk = null; - } - } - @Override public void flush() throws IOException { if (flushed) { @@ -77,22 +55,12 @@ public class JwsOutputStream extends FilterOutputStream { } try { byte[] finalBytes = signature.sign(); - out.write('.'); - encodeAndWrite(finalBytes, 0, finalBytes.length, true); + out.write(new byte[]{'.'}); + Base64UrlUtility.encodeAndStream(finalBytes, 0, finalBytes.length, out); } catch (Exception ex) { throw new SecurityException(); } flushed = true; } - private byte[] newArray(byte[] src, int srcPos, int srcLen) { - byte[] buf = new byte[srcLen]; - System.arraycopy(src, srcPos, buf, 0, srcLen); - return buf; - } - private byte[] newArray(byte[] src, int srcPos, int srcLen, byte[] src2, int srcPos2, int srcLen2) { - byte[] buf = new byte[srcLen + srcLen2]; - System.arraycopy(src, srcPos, buf, 0, srcLen); - System.arraycopy(src2, srcPos2, buf, srcLen, srcLen2); - return buf; - } + } http://git-wip-us.apache.org/repos/asf/cxf/blob/f5d2a033/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/jaxrs/JwsWriterInterceptor.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/jaxrs/JwsWriterInterceptor.java b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/jaxrs/JwsWriterInterceptor.java index c672e29..e44dec7 100644 --- a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/jaxrs/JwsWriterInterceptor.java +++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/jaxrs/JwsWriterInterceptor.java @@ -32,6 +32,7 @@ import org.apache.cxf.jaxrs.utils.JAXRSUtils; import org.apache.cxf.rs.security.oauth2.jws.JwsCompactProducer; import org.apache.cxf.rs.security.oauth2.jws.JwsOutputStream; import org.apache.cxf.rs.security.oauth2.jwt.JwtHeaders; +import org.apache.cxf.rs.security.oauth2.utils.Base64UrlOutputStream; @Priority(Priorities.JWS_WRITE_PRIORITY) public class JwsWriterInterceptor extends AbstractJwsWriterProvider implements WriterInterceptor { @@ -48,10 +49,12 @@ public class JwsWriterInterceptor extends AbstractJwsWriterProvider implements W } } if (useJwsOutputStream) { - JwsOutputStream cos = getInitializedSigProvider().createJwsStream(actualOs, ctString); - ctx.setOutputStream(cos); + JwsOutputStream jwsStream = getInitializedSigProvider().createJwsStream(actualOs, ctString); + Base64UrlOutputStream base64Stream = new Base64UrlOutputStream(jwsStream); + ctx.setOutputStream(base64Stream); ctx.proceed(); - cos.flush(); + base64Stream.flush(); + jwsStream.flush(); } else { CachedOutputStream cos = new CachedOutputStream(); ctx.setOutputStream(cos); http://git-wip-us.apache.org/repos/asf/cxf/blob/f5d2a033/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/Base64UrlOutputStream.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/Base64UrlOutputStream.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/Base64UrlOutputStream.java new file mode 100644 index 0000000..549b062 --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/Base64UrlOutputStream.java @@ -0,0 +1,31 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cxf.rs.security.oauth2.utils; + +import java.io.OutputStream; + +import org.apache.cxf.common.util.Base64OutputStream; + +public class Base64UrlOutputStream extends Base64OutputStream { + + public Base64UrlOutputStream(OutputStream out) { + super(out, true); + } + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/f5d2a033/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jwt/JAXRSJweJwsTest.java ---------------------------------------------------------------------- diff --git a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jwt/JAXRSJweJwsTest.java b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jwt/JAXRSJweJwsTest.java index 15f40dd..e9bbc3b 100644 --- a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jwt/JAXRSJweJwsTest.java +++ b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jwt/JAXRSJweJwsTest.java @@ -86,7 +86,9 @@ public class JAXRSJweJwsTest extends AbstractBusClientServerTestBase { jweWriter.setUseJweOutputStream(true); providers.add(jweWriter); providers.add(new JweClientResponseFilter()); - providers.add(new JwsWriterInterceptor()); + JwsWriterInterceptor jwsWriter = new JwsWriterInterceptor(); + jwsWriter.setUseJwsOutputStream(true); + providers.add(jwsWriter); providers.add(new JwsClientResponseFilter()); bean.setProviders(providers); bean.getProperties(true).put("rs.security.encryption.out.properties", SERVER_JWEJWS_PROPERTIES);