解析JWT时遇到Java反射异常ClassCastExcepti

2017-07-03  本文已影响149人  晓笑

问题描述:

将一个复杂类(类成员变量有链表类)的实例化对象,转为符合JWT的Json串之后,再通过该json解析出该复杂类的对象。

场景:

我在使用JWT的时候,为了将用户身份在Token中进行记录,以减轻服务器查询数据库带来的消耗,定义了一个AuthoritiesVo类:

成员变量:

访问修饰符 变量类型 变量名
private int userid
private ArrayList<String> privil
private ArrayList<Integer> privilValue

其中,privil与privilValue的取值范围是AuthoritiesVo定义的一系列常量:

访问修饰符 静态 最终值 类型 变量名
public static final String ADMIN "ADMIN"
public static final String COMMON "COMMON"
public static final int ADMIN_VALUE 1
public static final int COMMON_VALUE 7

成员方法:

上述成员变量的setter和getter方法

JWT的背景说明:

JWT是一种用于双方之间传递安全信息的简洁的、URL安全的表述性声明规范。JWT作为一个开放的标准(RFC 7519),定义了一种简洁的,自包含的方法用于通信双方之间以Json对象的形式安全的传递信息。因为数字签名的存在,这些信息是可信的,JWT可以使用HMAC算法或者是RSA的公私秘钥对进行签名。

提炼主要信息:JWT是一种规范,用来描述一种一Json对象来传递信息的规范。
我们采用的JWT规范的一个实现的Jar包:jsonwebtoken
在jsonwebtoken提供的方法中,可以通过Claims来将自定义的信息写入负载(Payload),Claims实际上是一个Map,通过put(key, value)的形式就可以将数据以键值对的形式写入,比如:

Claims claims = Jwts.claims().setSubject(user.getName()); //Claims是一个Map,可以向其中写入自定义的key-value值 claims.put("userId", user.getUserid()); claims.put("Authorities", user.getAuthorities());

使用Claims将自定义的信息写入JWT负载

解析的时候,要获取负载(Payload)中的信息,仍然可以像使用Map一样,通过get(key)的形式将值取出,比如:

Claims claims = Jwts.parser() .setSigningKey(DatatypeConverter.parseBase64Binary(PRIVATE_KEY)) .parseClaimsJws(token).getBody(); int userId = (Integer) claims.get("userId");

使用Claims从负载中取出需要的数据

但是,我们将上文提到的AuthoritiesVo类的实例化对象写入到负载中,并置key为Authorities,并不能通过get("Authorities")直接强转为AuthoritiesVo对象:
下述代码会报错:

authorities = (AuthoritiesVo) claims.get("Authorities");

解析token串的函数方法
报错的主要异常信息如下:
java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to imp4sep.vo.AuthoritiesVo at imp4sep.utils.TicketUtils.parseToken(TicketUtils.java:118) at imp4sep.utils.TicketUtilsTest.testParseToken(TicketUtilsTest.java:89) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source)
我们首先判断,claims.get("Authorities")语句得到的内容是什么:
用输出:
{userid=10001, privil=[COM_ADMIN, LEADER, PM], privilValue=[2, 3, 4]}
鼠标悬浮(或者添加watch),可以看到,claims是一个LinkedHashMap: claims是一个LinkedHashMap

展开可以看到,get方法返回的也是一个LinkedHashMap:

get方法返回的也是一个LinkedHashMap

我们之所以不能将返回值强制类型转换为AuthoritiesVo问题就在此。
继续展看层层嵌套的HashMap,在table中找到我们需要的privil和privilValue
(因为HashMap采用的是散列算法,所以table中有些单元是空的):

展看LinkedHashMap查看其中的对象

既然反射并不能直接得到最终的类对象,现在我们知道了返回值类型为链表哈希映射表,就不直接强制类型,采用“手动”的方式解决:

LinkedHashMap hashmap = (LinkedHashMap) claims.get("Authorities"); ArrayList<String> privil = (ArrayList<String>) hashmap.get("privil"); ArrayList<Integer> privilValue = (ArrayList<Integer>) obj.get("privilValue"); authorities.setPrivil(privil); authorities.setPrivilValue(privilValue);

至此,问题解决。

上一篇下一篇

猜你喜欢

热点阅读