需求分析
在互联中,我们的服务是不对外开放的,但是有先场景下我们可以对外开放,但是必须是系统所允许的用户才可以,这样做一方面保证安全,另一方面可以提升平台的能力,比如调用微信的接口必须要进行微信开发者认证,比如阿里云等等,那么我们如何实现一个自己的开放平台呢?
实现思路
平台接入流程图如下:
提供一个申请接入的入口,用户通过注册审核通过后获取appid和appserect得到访问应用服务的钥匙,然后通过钥匙获取访问的token.
服务端验证流程图如下
服务端要验证访问者是不是真实的用户,有时候还可以增加ip白名单进行拦截,具体要根据业务进行扩展,总体思路如上,返回token后,用户就可以拿着token调用服务的API,服务端可以通过aop进行接口统计等等。
服务端验证代码示例
/**
* 获取登录token
*
* @param body
* @param request
* @return
*/
public String getAccessToken(String body, HttpServletRequest request) {
log.info("body是:{}", body);
String appid = request.getHeader("appid");
// String secret1 = request.getHeader("secret");
try {
if (!StringUtils.hasText(body)) {
throw new ServiceException("必传参数:body不能为空!");
}
UserApplicationEntity validAppidAndSecret = this.isValidAppidAndSecret(appid);
if (validAppidAndSecret==null) {
throw new ServiceException("appid/secret:验证失败");
}
String secret1 = validAppidAndSecret.getSecret();
// String decode = URLDecoder.decode(body, StandardCharsets.UTF_8);
log.info("解密后的值是:{}", body);
String decryptBodyStr = RsaUtil.decryptStr(body, secret1);
log.info("P{}",decryptBodyStr);
JSONObject decryptBody = JSONObject.parseObject(decryptBodyStr);
String secret = decryptBody.getString("secret");
//验证appid和secret合法性
if(!secret1.equals(secret)){
throw new ServiceException("secret:验证失败,请联系管理员");
}
Date expiryDate = validAppidAndSecret.getExpiryDate();
int compare = DateUtil.compare(expiryDate, new Date());
if(compare<0){
throw new ServiceException("appid/secret:已过期,请联系管理员");
}
//根据当前时间戳和请求时间戳验证是否盗链
Long timestamp = decryptBody.getLong("timestamp");
long requestInterval = System.currentTimeMillis() - timestamp;
if (requestInterval > REQUEST_EXPIRES_TIME) {
throw new ServiceException("时间已经超时");
}
//验证调用方身份合法性(验证签名)
Optional<String> signOpt = Optional.ofNullable(request.getHeader("sign"));
if (signOpt.isEmpty()) {
throw new ServiceException("身份不合法!");
}
String grantType = decryptBody.getString("grantType");
String salt = decryptBody.getString("salt");
String generatedSign = SecureUtil.md5(appid + secret + grantType + timestamp+salt);
String sign = signOpt.get();
if (!sign.equals(generatedSign)) {
throw new ServiceException("身份不合法!");
}
//通过jwt生成access token
Map<String, Object> payload = new HashMap<>();
payload.put("appid", appid);
payload.put("secret", secret);
payload.put("grantType", grantType);
payload.put("timestamp", timestamp);
payload.put("salt", salt);
payload.put("sign", sign);
return jwtService.generateAccessToken(appid,payload);
} catch (Exception e) {
log.error("验证失败,请联系管理员:{}" , e);
throw new ServiceException("验证失败,请联系管理员:" + e);
}
}