Ghost可以作为一个CMS系统,其提供了Admin API供调用,以接口方式实现对于系统的管理。
其Admin API接口使用两种认证方式:
token方式 认证适合服务端访问Ghost系统;
用户名/密码适合客户端访问。
由于自己是要做一个自定义的客户端工具,来由格式化的数据自动生成博客内容,因此使用用户名/密码方式和token的方式都可以。
Ghost系统,使用了标准的jwt标准的token,鉴于此尝试使用token来做认证。先先后后了解和学习了许多的内容,主要是在spring security体系下面jwt的应用。包括spring security提下下面基于oauth2协议的认证流程。
spring security下面基于OAuth2协议的认证方式目前还记得的大概是这么个流程:
- 用户访问资源中心,
- 资源中心检测到用户未携带有效的认证信息,将用户重定向到认证中心(同时将目标资源链接作为参数一同携带过去),
- 认证中心要求用户输入用户名/密码,验证有效后为目标资源链接生成一个一次性的code返回给用户,
- 用户使用这个code再请求认证中心获取到一个token(可以多次使用,但是有有效期),这个token中包含了用户必要信息包括用户名,授权资源信息等,由RSA私钥加密
- 用户携带token去访问资源中心,资源中心使用RSA公钥对token解密认证,获取其中携带的信息,如果符合相应资源的权限,则返回资源。同时如果检测到用户携带的token即将过期时,进行续期操作。
这个应该就是jwt的认证方式,其中主要有用户、资源中心、认证中心三个角色。
言归正传,说回Ghost的jwt token来。Ghost提供了一个两段由冒号分割的Admin Key,前一段是id,后一段是16进制编码的secret。使用这两个信息对需要携带的token信息进行加密,Ghost即可认证,并解密出token中包含的信息。
按照Ghost官方的说法,需要三个步骤:
- 将admin key拆分成id和secret字符串
- 将secret进行16进制到byte[]的转换
- 使用选择的jwt工具库,利用id和转换过的secret,包含进要求的payload和header信息生成一个token即可。
但是自己研究的时候其实并不顺利,主要是auth0原生的jwt库的工具中没有HS256的加密算法,也不支持接收byte[]类型作为加密串。使用spring security的JwtHelper也有类似的问题,没有HS256的算法。
最后去网上找找,发现好几个帖子都在用一个jwt的工具库叫做jjwt,完整的依赖如下:
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
有了利器之后,一切都变得顺利起来,完整的生成token的代码如下:
public static void connectAsAdmin(String adminApiKey) throws Exception {
//Split the API key by the : into an id and a secret
String[] arr = adminApiKey.split(":");
String id = arr[0];
String secret = arr[1];
//Decode the hexadecimal secret into the original binary byte array
byte[] secretArr=hexToByte(secret);
//Pass these values to your JWT library of choice, ensuring that the header and payload are correct.
Map<String,Object> header=new HashMap<>();
header.put("alg","HS256");
header.put("kid",id);
header.put("typ","JWT");
Map<String,Object> payload=new HashMap<>();
Long now= new Date().getTime()/1000;
payload.put("iat",now);
payload.put("exp",now+5*60);
payload.put("aud","/v2/admin/");
String token = Jwts.builder()
.addClaims(payload)
.setHeader(header)
.signWith(SignatureAlgorithm.HS256,secretArr).compact();
System.out.println(token);
}
其中有个16进制转换byte[]的方法hexToByte
public static byte[] hexToByte(String hex){
int m = 0, n = 0;
int byteLen = hex.length() / 2; // 每两个字符描述一个字节
byte[] ret = new byte[byteLen];
for (int i = 0; i < byteLen; i++) {
m = i * 2 + 1;
n = m + 1;
int intVal = Integer.decode("0x" + hex.substring(i * 2, m) + hex.substring(m, n));
ret[i] = Byte.valueOf((byte)intVal);
}
return ret;
}
这段代码,着实花了不少时间,两周?甚至一个月,也可能最近工作比较忙,也可能回家要忙着抱孩子,借口着实是不少。卡在这里这么久了,也是比较急,但是实际上只要用心去分析,用心去网上找,保持自己在一个较好的状态,实际上问题的解决都是个把小时的事情,关键是解决了问题,以及学习到了一些内容。
除了以上的接口,最近还读了几本书,包括一本《海明威传》《江*民传》,眼下还有一个《乌合之众》在看。同时还看了《烈火英雄》《哪吒之魔童归来》等等电影。
下面还有token的认证等,但是我的项目中token认证是由Ghost系统完成的。