Spring - save/restore 数据库中的 HttpCookie(作为字符串)出现问题:最后一个单元没有足够的有效位
Spring - save/restore HttpCookie (as a String) in a database gives problem: Last unit does not have enough valid bits
我的应用程序需要保存和恢复 HttpCookiein/from 数据库。因此,我尝试通过以下代码将 encode/decode HttpCookie 对象转换为字符串。结果在某些情况下是错误消息:最后一个单元没有足够的有效位。
是的,我阅读了 post 有关错误的信息,但那是关于通过缓冲区读取和转换缓冲区。 这个不一样,因为流读取是在1 go!
在某些情况下,此代码会给出上述错误消息。我该如何解决这个问题?
public class SerializableHttpCookie implements Serializable {
private static final long serialVersionUID = 6374381323722046732L;
private transient HttpCookie cookie;
private Field fieldHttpOnly; // needed for a workaround
...
public String encode2(HttpCookie cookie) {
this.cookie = cookie;
ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
ObjectOutputStream outputStream = new ObjectOutputStream(os);
outputStream.writeObject(this);
} catch (IOException e) {
logger.error( "IOException in encodeCookie", e);
return null;
}
return Base64.getUrlEncoder().encodeToString( os.toByteArray());
}
public HttpCookie decode2(String encodedCookie) {
byte[] bytes = Base64.getUrlDecoder().decode(encodedCookie);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream( bytes);
HttpCookie cookie = null;
try {
ObjectInputStream objectInputStream = new ObjectInputStream( byteArrayInputStream);
cookie = ((SerializableHttpCookie) objectInputStream.readObject()).cookie;
} catch (IOException e) {
logger.error( "IOException in decodeCookie", e);
} catch (ClassNotFoundException e) {
logger.error( "ClassNotFoundException in decodeCookie", e);
}
return cookie;
}
ReadObject 和 WriteObject 是:
private void writeObject(ObjectOutputStream out) throws IOException {
out.writeObject(cookie.getName());
out.writeObject(cookie.getValue());
out.writeObject(cookie.getComment());
out.writeObject(cookie.getCommentURL());
out.writeObject(cookie.getDomain());
out.writeLong(cookie.getMaxAge());
out.writeObject(cookie.getPath());
out.writeObject(cookie.getPortlist());
out.writeInt(cookie.getVersion());
out.writeBoolean(cookie.getSecure());
out.writeBoolean(cookie.getDiscard());
out.writeBoolean(getHttpOnly());
}
private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException {
String name = (String) in.readObject();
String value = (String) in.readObject();
cookie = new HttpCookie(name, value);
cookie.setComment((String) in.readObject());
cookie.setCommentURL((String) in.readObject());
cookie.setDomain((String) in.readObject());
cookie.setMaxAge(in.readLong());
cookie.setPath((String) in.readObject());
cookie.setPortlist((String) in.readObject());
cookie.setVersion(in.readInt());
cookie.setSecure(in.readBoolean());
cookie.setDiscard(in.readBoolean());
setHttpOnly(in.readBoolean());
}
我使用了如下不同的方法也导致了错误(在计算中)。错误被标记为注释。
private String byteArrayToHexString(byte[] bytes) {
StringBuilder sb = new StringBuilder(bytes.length * 2);
for (byte element : bytes) {
int v = element & 0xff;
if (v < 16) {
sb.append('0');
}
sb.append(Integer.toHexString(v));
}
return sb.toString();
}
private byte[] hexStringToByteArray(String hexString) {
int len = hexString.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character
.digit(hexString.charAt(i + 1), 16)); // ERROR: hexString.charAt(i+1) out of range
}
return data;
}
编码和序列化
另一种方法是调用 Answer 中的代码。唉,我在解码字符串时遇到了同样的错误。
new SerializableHttpCookie2().serializeAndEncode(cookie)));
和
HttpCookie cookie = new SerializableHttpCookie2().decodeAndDeserialize(encodedCookie);
使用 commons-codec 库:
public String serializeAndEncode(final HttpCookie cookie) throws IllegalAccessException, IllegalArgumentException {
final String serialized = this.serialize(cookie);
return new String( Hex.encodeHex(serialized.getBytes()));
}
和
public HttpCookie decodeAndDeserialize(final String string)
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
// final String decoded = this.decode(string);
String decoded;
try {
decoded = new String(Hex.decodeHex(string.toCharArray()));
} catch ( Exception e) {
return null;
}
return this.deserialize(decoded);
}
也许下面的class可以解决您的问题
因为HttpCookie
没有实现Serializable
,值是通过反射读取和写回的。
im 使用 java 12,在我的例子中反射触发警告,导致代码访问 private final
字段。警告说:
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by HttpCookieDeSerializer (file:...) to field java.net.HttpCookie.name
WARNING: Please consider reporting this to the maintainers of HttpCookieDeSerializer
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
class:
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.HttpCookie;
import java.util.Base64;
public class HttpCookieDeSerializer {
// TODO: need to be changed?
private final String fieldValueDelimiter = "===";
// TODO: need to be changed?
private final String fieldValuePairDelimiter = "###";
public HttpCookieDeSerializer() {
super();
}
public String decode(final String string) {
return new String(Base64.getUrlDecoder().decode(string));
}
public HttpCookie decodeAndDeserialize(final String string)
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
final String decoded = this.decode(string);
// TODO: remove sysout
System.out.println(decoded);
return this.deserialize(decoded);
}
public HttpCookie deserialize(final String decoded)
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
final String name = this.preGet(decoded, "name");
final String value = this.preGet(decoded, "value");
final HttpCookie cookie = new HttpCookie(name, value);
final String[] fieldsAndValues = decoded.split("(" + this.fieldValuePairDelimiter + ")");
for (final String fieldAndValue : fieldsAndValues) {
final String[] fieldAndValueSplitted = fieldAndValue.split("(" + this.fieldValueDelimiter + ")");
final Field field = HttpCookie.class.getDeclaredField(fieldAndValueSplitted[0]);
if (Modifier.isFinal(field.getModifiers())) {
// ???
// continue;
}
field.setAccessible(true);
final Class<?> type = field.getType();
if (String.class.equals(type)) {
field.set(cookie, this.convertNullStringToNullObject(fieldAndValueSplitted[1]));
} else if (Long.TYPE.equals(type)) {
field.setLong(cookie, Long.parseLong(fieldAndValueSplitted[1]));
} else if (Integer.TYPE.equals(type)) {
field.setInt(cookie, Integer.parseInt(fieldAndValueSplitted[1]));
} else if (Boolean.TYPE.equals(type)) {
field.setBoolean(cookie, Boolean.parseBoolean(fieldAndValueSplitted[1]));
}
}
return cookie;
}
public String encode(final String string) {
return Base64.getUrlEncoder().encodeToString(string.getBytes());
}
public String serialize(final HttpCookie cookie) throws IllegalAccessException, IllegalArgumentException {
final StringBuilder builder = new StringBuilder();
final Field[] fields = HttpCookie.class.getDeclaredFields();
boolean first = true;
for (final Field field : fields) {
if (Modifier.isStatic(field.getModifiers())) {
continue;
}
if (!first) {
builder.append(this.fieldValuePairDelimiter);
}
builder.append(field.getName());
builder.append(this.fieldValueDelimiter);
final Class<?> type = field.getType();
field.setAccessible(true);
if (String.class.equals(type)) {
builder.append((String) field.get(cookie));
} else if (Long.TYPE.equals(type)) {
builder.append(Long.toString(field.getLong(cookie)));
} else if (Integer.TYPE.equals(type)) {
builder.append(Integer.toString(field.getInt(cookie)));
} else if (Boolean.TYPE.equals(type)) {
builder.append(Boolean.toString(field.getBoolean(cookie)));
}
first = false;
}
final String serialized = builder.toString();
return serialized;
}
public String serializeAndEncode(final HttpCookie cookie) throws IllegalAccessException, IllegalArgumentException {
final String serialized = this.serialize(cookie);
// TODO: remove sysout
System.out.println(serialized);
return this.encode(serialized);
}
private Object convertNullStringToNullObject(final String string) {
if ("null".equals(string)) {
return null;
}
return string;
}
private String preGet(final String decoded, final String fieldName) {
final String[] fieldsAndValues = decoded.split("(" + this.fieldValuePairDelimiter + ")");
for (final String fieldAndValue : fieldsAndValues) {
if (fieldAndValue.startsWith(fieldName + this.fieldValueDelimiter)) {
return fieldAndValue.split("(" + this.fieldValueDelimiter + ")")[1];
}
}
return null;
}
public static void main(final String[] args) {
final HttpCookieDeSerializer hcds = new HttpCookieDeSerializer();
try {
final HttpCookie cookie = new HttpCookie("myCookie", "first");
final String serializedAndEncoded = hcds.serializeAndEncode(cookie);
// TODO: remove sysout
System.out.println(serializedAndEncoded);
final HttpCookie other = hcds.decodeAndDeserialize(serializedAndEncoded);
// TODO: remove sysout
System.out.println(cookie.equals(other));
} catch (final Throwable t) {
t.printStackTrace();
}
}
}
在我看来,没有必要 encode/and 或十六进制 serialization-string。
但如果你想这样做,我建议使用 apache commons-codec 库的 org.apache.commons.codec.binary.Hex
因为它是一个经过测试且稳定的库,没有 runtime-dependencies,大小为 ~331kb
尽管如此,我还是尝试了以下序列化和反序列化的可能性
- 没有base64-encoding/hexing
- 使用 base64 编码
- 用咒语
- 使用 base-64 编码和十六进制
对我来说,所有的可能性都很好。
我的应用程序需要保存和恢复 HttpCookiein/from 数据库。因此,我尝试通过以下代码将 encode/decode HttpCookie 对象转换为字符串。结果在某些情况下是错误消息:最后一个单元没有足够的有效位。
是的,我阅读了 post 有关错误的信息,但那是关于通过缓冲区读取和转换缓冲区。 这个不一样,因为流读取是在1 go!
在某些情况下,此代码会给出上述错误消息。我该如何解决这个问题?
public class SerializableHttpCookie implements Serializable {
private static final long serialVersionUID = 6374381323722046732L;
private transient HttpCookie cookie;
private Field fieldHttpOnly; // needed for a workaround
...
public String encode2(HttpCookie cookie) {
this.cookie = cookie;
ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
ObjectOutputStream outputStream = new ObjectOutputStream(os);
outputStream.writeObject(this);
} catch (IOException e) {
logger.error( "IOException in encodeCookie", e);
return null;
}
return Base64.getUrlEncoder().encodeToString( os.toByteArray());
}
public HttpCookie decode2(String encodedCookie) {
byte[] bytes = Base64.getUrlDecoder().decode(encodedCookie);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream( bytes);
HttpCookie cookie = null;
try {
ObjectInputStream objectInputStream = new ObjectInputStream( byteArrayInputStream);
cookie = ((SerializableHttpCookie) objectInputStream.readObject()).cookie;
} catch (IOException e) {
logger.error( "IOException in decodeCookie", e);
} catch (ClassNotFoundException e) {
logger.error( "ClassNotFoundException in decodeCookie", e);
}
return cookie;
}
ReadObject 和 WriteObject 是:
private void writeObject(ObjectOutputStream out) throws IOException {
out.writeObject(cookie.getName());
out.writeObject(cookie.getValue());
out.writeObject(cookie.getComment());
out.writeObject(cookie.getCommentURL());
out.writeObject(cookie.getDomain());
out.writeLong(cookie.getMaxAge());
out.writeObject(cookie.getPath());
out.writeObject(cookie.getPortlist());
out.writeInt(cookie.getVersion());
out.writeBoolean(cookie.getSecure());
out.writeBoolean(cookie.getDiscard());
out.writeBoolean(getHttpOnly());
}
private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException {
String name = (String) in.readObject();
String value = (String) in.readObject();
cookie = new HttpCookie(name, value);
cookie.setComment((String) in.readObject());
cookie.setCommentURL((String) in.readObject());
cookie.setDomain((String) in.readObject());
cookie.setMaxAge(in.readLong());
cookie.setPath((String) in.readObject());
cookie.setPortlist((String) in.readObject());
cookie.setVersion(in.readInt());
cookie.setSecure(in.readBoolean());
cookie.setDiscard(in.readBoolean());
setHttpOnly(in.readBoolean());
}
我使用了如下不同的方法也导致了错误(在计算中)。错误被标记为注释。
private String byteArrayToHexString(byte[] bytes) {
StringBuilder sb = new StringBuilder(bytes.length * 2);
for (byte element : bytes) {
int v = element & 0xff;
if (v < 16) {
sb.append('0');
}
sb.append(Integer.toHexString(v));
}
return sb.toString();
}
private byte[] hexStringToByteArray(String hexString) {
int len = hexString.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character
.digit(hexString.charAt(i + 1), 16)); // ERROR: hexString.charAt(i+1) out of range
}
return data;
}
编码和序列化
另一种方法是调用 Answer 中的代码。唉,我在解码字符串时遇到了同样的错误。
new SerializableHttpCookie2().serializeAndEncode(cookie)));
和
HttpCookie cookie = new SerializableHttpCookie2().decodeAndDeserialize(encodedCookie);
使用 commons-codec 库:
public String serializeAndEncode(final HttpCookie cookie) throws IllegalAccessException, IllegalArgumentException {
final String serialized = this.serialize(cookie);
return new String( Hex.encodeHex(serialized.getBytes()));
}
和
public HttpCookie decodeAndDeserialize(final String string)
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
// final String decoded = this.decode(string);
String decoded;
try {
decoded = new String(Hex.decodeHex(string.toCharArray()));
} catch ( Exception e) {
return null;
}
return this.deserialize(decoded);
}
也许下面的class可以解决您的问题
因为HttpCookie
没有实现Serializable
,值是通过反射读取和写回的。
im 使用 java 12,在我的例子中反射触发警告,导致代码访问 private final
字段。警告说:
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by HttpCookieDeSerializer (file:...) to field java.net.HttpCookie.name
WARNING: Please consider reporting this to the maintainers of HttpCookieDeSerializer
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
class:
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.HttpCookie;
import java.util.Base64;
public class HttpCookieDeSerializer {
// TODO: need to be changed?
private final String fieldValueDelimiter = "===";
// TODO: need to be changed?
private final String fieldValuePairDelimiter = "###";
public HttpCookieDeSerializer() {
super();
}
public String decode(final String string) {
return new String(Base64.getUrlDecoder().decode(string));
}
public HttpCookie decodeAndDeserialize(final String string)
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
final String decoded = this.decode(string);
// TODO: remove sysout
System.out.println(decoded);
return this.deserialize(decoded);
}
public HttpCookie deserialize(final String decoded)
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
final String name = this.preGet(decoded, "name");
final String value = this.preGet(decoded, "value");
final HttpCookie cookie = new HttpCookie(name, value);
final String[] fieldsAndValues = decoded.split("(" + this.fieldValuePairDelimiter + ")");
for (final String fieldAndValue : fieldsAndValues) {
final String[] fieldAndValueSplitted = fieldAndValue.split("(" + this.fieldValueDelimiter + ")");
final Field field = HttpCookie.class.getDeclaredField(fieldAndValueSplitted[0]);
if (Modifier.isFinal(field.getModifiers())) {
// ???
// continue;
}
field.setAccessible(true);
final Class<?> type = field.getType();
if (String.class.equals(type)) {
field.set(cookie, this.convertNullStringToNullObject(fieldAndValueSplitted[1]));
} else if (Long.TYPE.equals(type)) {
field.setLong(cookie, Long.parseLong(fieldAndValueSplitted[1]));
} else if (Integer.TYPE.equals(type)) {
field.setInt(cookie, Integer.parseInt(fieldAndValueSplitted[1]));
} else if (Boolean.TYPE.equals(type)) {
field.setBoolean(cookie, Boolean.parseBoolean(fieldAndValueSplitted[1]));
}
}
return cookie;
}
public String encode(final String string) {
return Base64.getUrlEncoder().encodeToString(string.getBytes());
}
public String serialize(final HttpCookie cookie) throws IllegalAccessException, IllegalArgumentException {
final StringBuilder builder = new StringBuilder();
final Field[] fields = HttpCookie.class.getDeclaredFields();
boolean first = true;
for (final Field field : fields) {
if (Modifier.isStatic(field.getModifiers())) {
continue;
}
if (!first) {
builder.append(this.fieldValuePairDelimiter);
}
builder.append(field.getName());
builder.append(this.fieldValueDelimiter);
final Class<?> type = field.getType();
field.setAccessible(true);
if (String.class.equals(type)) {
builder.append((String) field.get(cookie));
} else if (Long.TYPE.equals(type)) {
builder.append(Long.toString(field.getLong(cookie)));
} else if (Integer.TYPE.equals(type)) {
builder.append(Integer.toString(field.getInt(cookie)));
} else if (Boolean.TYPE.equals(type)) {
builder.append(Boolean.toString(field.getBoolean(cookie)));
}
first = false;
}
final String serialized = builder.toString();
return serialized;
}
public String serializeAndEncode(final HttpCookie cookie) throws IllegalAccessException, IllegalArgumentException {
final String serialized = this.serialize(cookie);
// TODO: remove sysout
System.out.println(serialized);
return this.encode(serialized);
}
private Object convertNullStringToNullObject(final String string) {
if ("null".equals(string)) {
return null;
}
return string;
}
private String preGet(final String decoded, final String fieldName) {
final String[] fieldsAndValues = decoded.split("(" + this.fieldValuePairDelimiter + ")");
for (final String fieldAndValue : fieldsAndValues) {
if (fieldAndValue.startsWith(fieldName + this.fieldValueDelimiter)) {
return fieldAndValue.split("(" + this.fieldValueDelimiter + ")")[1];
}
}
return null;
}
public static void main(final String[] args) {
final HttpCookieDeSerializer hcds = new HttpCookieDeSerializer();
try {
final HttpCookie cookie = new HttpCookie("myCookie", "first");
final String serializedAndEncoded = hcds.serializeAndEncode(cookie);
// TODO: remove sysout
System.out.println(serializedAndEncoded);
final HttpCookie other = hcds.decodeAndDeserialize(serializedAndEncoded);
// TODO: remove sysout
System.out.println(cookie.equals(other));
} catch (final Throwable t) {
t.printStackTrace();
}
}
}
在我看来,没有必要 encode/and 或十六进制 serialization-string。
但如果你想这样做,我建议使用 apache commons-codec 库的 org.apache.commons.codec.binary.Hex
因为它是一个经过测试且稳定的库,没有 runtime-dependencies,大小为 ~331kb
尽管如此,我还是尝试了以下序列化和反序列化的可能性
- 没有base64-encoding/hexing
- 使用 base64 编码
- 用咒语
- 使用 base-64 编码和十六进制
对我来说,所有的可能性都很好。