AK、SK实现(双方API交互:签名及验证)
参考:https://blog.csdn.net/yqwang75457/article/details/117815474
1、原理
AK/SK:
AK:Access Key Id,用于标示用户。
SK:Secret Access Key,是用户用于加密认证字符串和用来验证认证字符串的密钥,其中SK必须保密。
通过使用Access Key Id / Secret Access Key加密的方法来验证某个请求的发送者身份。
基本思路:
1.客户端需要在认证服务器中预先设置 access key(AK 或叫 app ID) 和 secure key(SK)。
2.在调用 API 时,客户端需要对参数和 access key 等信息结合 secure key 进行签名生成一个额外的 sign字符串。
3.服务器接收到用户的请求后,系统将使用AK对应的相同的SK和同样的认证机制生成认证字符串,并与用户请求中包含的认证字符串进行比对。如果认证字符串相同,系统认为用户拥有指定的操作权限,并执行相关操作;如果认证字符串不同,系统将忽略该操作并返回错误码。
2、实现
2.1 服务端
注解拦截器,拦截请求
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface IdentifySk {
String value() default "";
}
切面
@Component
@Aspect
public class IdentifyRequest {
@Pointcut("@annotation(com.chinatower.platform.client.aop.IdentifySk)")
public void pointCut() {
}
@Before(value = "pointCut()")
public void before(JoinPoint joinPoint) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
//获取请求头信息
String ts = request.getHeader("ts");
Long timeStamp = StrUtil.isNotBlank(ts) ? Long.valueOf(ts) : null;
String msgId = request.getHeader("msgId");
String appId = request.getHeader("appId");
String sign = request.getHeader("sign");
boolean res = SignUtil.checkHeaderInfo(timeStamp, msgId, appId, sign);
Validate.isTrue(res,"请求失败,请检查请求头");
}
}
生成sign加密工具类
@Slf4j
public class SHACoderUtil {
public static String sha256(String str){
String encodeStr = "";
try {
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
messageDigest.update(str.getBytes("UTF-8"));
encodeStr = byte2Hex(messageDigest.digest());
} catch (Exception e) {
log.error("sha256加码失败",e);
}
return encodeStr;
}
/**
* 将byte转为16进制
* @param bytes
* @return
*/
private static String byte2Hex(byte[] bytes){
StringBuffer stringBuffer = new StringBuffer();
String temp = null;
for (int i=0;i<bytes.length;i++){
temp = Integer.toHexString(bytes[i] & 0xFF);
if (temp.length()==1){
//1得到一位的进行补0操作
stringBuffer.append("0");
}
stringBuffer.append(temp);
}
return stringBuffer.toString();
}
}
校验工具类
public class SignUtil {
/**
* 按签名算法获取sign
*
* @param appId
* @param appSecret
* @param ts 时间戳
* @param msgId 请求唯一标识
* @return
*/
public static String getSign(String appId, String appSecret, String ts, String msgId) {
String str = IStringUtils.spliceStr("ts=", String.valueOf(ts), "&msgId=", msgId, "&appId=", appId, "&appSecret=", appSecret);
// 对待加密字符串进行加密,得到sign值
return SHACoderUtil.sha256(str);
}
/**
* 鉴别 sign ,被调用方
*/
public static boolean checkHeaderInfo(Long ts, String msgId, String appId, String sign) {
if (ts == null || StrUtil.isBlank(msgId) || StrUtil.isBlank(appId) || StrUtil.isBlank(sign)) {
return false;
}
//可以加一个利用redis防止重复请求
//超过20分钟,表示请求无效
if (System.currentTimeMillis() - ts > 20 * 60 * 1000) {
return false;
}
//根据参数加密,验证和传来的sign是否一致
String reSign = getSign(appId, CommonConstant.SK, String.valueOf(ts), msgId);
if (sign.equals(reSign)) {
return true;
}
return false;
}
}
使用注解
@IdentifySk()
@PostMapping("/testSk")
public void testSk() {
System.out.println("ceshi");
}
2.2 客户端
加密工具类,同上SHACoderUtil
生成请求头和sign工具类
public class SignUtil {
/**
* 构建请求头 调用方
*/
public static Map<String, String> requestHeader(String appId, String appSecret) {
String ts = String.valueOf(System.currentTimeMillis());
String msgId = UUID.randomUUID().toString();
Map<String, String> header = new HashMap<>(16);
// 进行接口调用时的时间戳,即当前时间戳(毫秒),服务端会校验时间戳,例如时间差超过20分钟则认为请求无效,防止重复请求的攻击
header.put("ts", ts);
//每个请求提供一个唯一的标识符,服务器能够防止请求被多次使用
header.put("msgId", msgId);
header.put("appId", appId);
String sign = getSign(appId, appSecret, ts, msgId);
header.put("sign", sign);
return header;
}
/**
* 按签名算法获取sign(客户端和服务器端算法一致,都需要用)
*
* @param appId
* @param appSecret
* @param ts 时间戳
* @param msgId 请求唯一标识
* @return
*/
public static String getSign(String appId, String appSecret, String ts, String msgId) {
String str = IStringUtils.spliceStr("ts=", ts, "&msgId=", msgId, "&appId=", appId, "&appSecret=", appSecret);
// 对待加密字符串进行加密,得到sign值
return SHACoderUtil.sha256(str);
}
}
远程请求http工具类
@Slf4j
public class HttpClientUtil {
private static final String ENCODING_TYPE = "UTF-8";
public static String doGet(String url, Map<String, String> param) {
log.info("doGet方法,url={}, param={}", url, param);
CloseableHttpClient httpclient = HttpClients.createDefault();
String resultString = "";
CloseableHttpResponse response = null;
try {
// 创建uri
URIBuilder builder = new URIBuilder(url);
if (param != null) {
for (String key : param.keySet()) {
builder.addParameter(key, param.get(key));
}
}
URI uri = builder.build();
HttpGet httpGet = new HttpGet(uri);
response = httpclient.execute(httpGet);
// 判断返回状态是否为200
if (response.getStatusLine().getStatusCode() == 200) {
resultString = EntityUtils.toString(response.getEntity(), ENCODING_TYPE);
}
log.info("doGet方法返回状态={}, 结果={}", response.getStatusLine().getStatusCode(), resultString);
} catch (Exception e) {
log.error("发送get请求失败,原因={}", e.getLocalizedMessage());
e.printStackTrace();
} finally {
try {
if (response != null) {
response.close();
}
httpclient.close();
} catch (IOException e) {
log.error("关闭流失败,原因={}", e.getMessage());
e.printStackTrace();
}
}
return resultString;
}
public static String doPostJson(String url, String json,Integer timeOut) {
log.info("Http 的请求地址是{}, 请求参数是 {}",url,json);
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
HttpPost httpPost = new HttpPost(url);
// 创建请求内容
StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);
httpPost.setEntity(entity);
//构建超时等配置信息
RequestConfig config = RequestConfig.custom().setConnectTimeout(timeOut) //连接超时时间
.setConnectionRequestTimeout(timeOut) //从连接池中取的连接的最长时间
.setSocketTimeout(timeOut) //数据传输的超时时间
.setStaleConnectionCheckEnabled(true) //提交请求前测试连接是否可用
.build();
httpPost.setConfig(config);
response = httpClient.execute(httpPost);
resultString = EntityUtils.toString(response.getEntity(), ENCODING_TYPE);
} catch (ConnectTimeoutException e){
// 链接拒绝 ConnectTimeoutException
log.error("发送post json 请求失败,原因={}", e);
resultString = httpTimeOutOrConnect("连接超时", CommonConstant.HTTP_OUT_TIME);
} catch (HttpHostConnectException e){
// 链接拒绝 ConnectTimeoutException
log.error("发送post json 请求失败,原因={}", e);
resultString = httpTimeOutOrConnect("连接拒绝", CommonConstant.HTTP_OUT_TIME);
} catch (SocketTimeoutException e){
// 链接超时
log.error("发送post json 请求失败,原因={}", e);
resultString = httpTimeOutOrConnect("请求超时",CommonConstant.HTTP_OUT_TIME);
} catch (Exception e) {
// 其他异常
log.error("发送post json 请求失败,原因={}", e);
resultString = httpTimeOutOrConnect("请求失败",CommonConstant.HTTP_OUT_TIME_ERROR);
} finally {
try {
if (response != null) {
response.close();
}
} catch (IOException e) {
log.error("关闭流失败, 原因={}", e.getMessage());
e.printStackTrace();
}
}
log.info("Http 请求结果是{}",resultString);
return resultString;
}
/**
* 带请求头的的post请求
* @param url
* @param headMap
* @param json
* @param timeOut
* @return
*/
public static String doPostJson(String url, Map<String, String> headMap,String json,Integer timeOut) {
log.info("Http 的请求地址是{},请求头:{}, 请求参数是 {}",url, JSON.toJSONString(headMap),json);
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
HttpPost httpPost = new HttpPost(url);
//请求头
if (headMap != null && !headMap.isEmpty()){
for (String key:headMap.keySet()){
httpPost.addHeader(key,headMap.get(key));
}
}
// 创建请求内容
if (StrUtil.isNotBlank(json)){
StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);
httpPost.setEntity(entity);
}
//构建超时等配置信息
RequestConfig config = RequestConfig.custom().setConnectTimeout(timeOut) //连接超时时间
.setConnectionRequestTimeout(timeOut) //从连接池中取的连接的最长时间
.setSocketTimeout(timeOut) //数据传输的超时时间
.setStaleConnectionCheckEnabled(true) //提交请求前测试连接是否可用
.build();
httpPost.setConfig(config);
response = httpClient.execute(httpPost);
resultString = EntityUtils.toString(response.getEntity(), ENCODING_TYPE);
} catch (ConnectTimeoutException e){
// 链接拒绝 ConnectTimeoutException
log.error("发送post json 请求失败,原因={}", e);
resultString = httpTimeOutOrConnect("连接超时", CommonConstant.HTTP_OUT_TIME);
} catch (HttpHostConnectException e){
// 链接拒绝 ConnectTimeoutException
log.error("发送post json 请求失败,原因={}", e);
resultString = httpTimeOutOrConnect("连接拒绝",CommonConstant.HTTP_OUT_TIME);
} catch (SocketTimeoutException e){
// 链接超时
log.error("发送post json 请求失败,原因={}", e);
resultString = httpTimeOutOrConnect("请求超时",CommonConstant.HTTP_OUT_TIME);
} catch (Exception e) {
// 其他异常
log.error("发送post json 请求失败,原因={}", e);
resultString = httpTimeOutOrConnect("请求失败",CommonConstant.HTTP_OUT_TIME_ERROR);
} finally {
try {
if (response != null) {
response.close();
}
} catch (IOException e) {
log.error("关闭流失败, 原因={}", e.getMessage());
e.printStackTrace();
}
}
log.info("Http 请求结果是{}",resultString);
return resultString;
}
/**
*
* @return
*/
private static String httpTimeOutOrConnect(String returnMsg, String returnCode){
JSONObject jsonObject = new JSONObject();
jsonObject.put("returnMsg", returnMsg);
jsonObject.put("returnCode", returnCode);
jsonObject.put("errorMsg", returnMsg);
jsonObject.put("errorCode", returnCode);
return jsonObject.toJSONString();
}
}
测试请求服务端:
main(){
Map<String, String> headMap = SignUtil.requestHeader(commonProperties.getAk(), commonProperties.getSk());
HttpClientUtil.doPostJson(url, headMap, "ceshi", 60000);
}
AK、SK实现(双方API交互:签名及验证)的更多相关文章
- PHP开发API接口签名及验证
<?php // 设置一个密钥(secret),只有发送方,和接收方知道 /*----发送方和接收方- start ----*/ $secret = "28c8edde3d61a041 ...
- 使用CEPH RGW admin ops API 进行用户user AK/SK管理的秘诀
需求: 云平台面板上需要支持为不同的用户创建不同的RGW 的AK/SK用户秘钥,以完成对象存储的用户隔离,并可以管理bucket和查看bucket容量信息. 分析:查阅CEPH官网文档 S3 API ...
- 034.认证方式 | 基本认证 、Token认证、 AK/SK认证
认证方式 关于认证: https://www.cnblogs.com/badboyh2o/p/11068779.html https://www.cnblogs.com/badboyh2o/p/110 ...
- 前后端分离后API交互如何保证数据安全性
前后端分离后API交互如何保证数据安全性? 一.前言 前后端分离的开发方式,我们以接口为标准来进行推动,定义好接口,各自开发自己的功能,最后进行联调整合.无论是开发原生的APP还是webapp还是PC ...
- AK/SK加密认证
AK/SK认证的实现 AK/SK概述 1.什么是AKSK ak/sk是一种身份认证方式,常用于系统间接口调用时的身份验证,其中ak为Access Key ID,sk为Secret Access Key ...
- 前后端API交互数据加密——AES与RSA混合加密完整实例
前言 前段时间看到一篇文章讲如何保证API调用时数据的安全性(传送门:https://blog.csdn.net/ityouknow/article/details/80603617),文中讲到利用R ...
- 前后端API交互数据加密——AES与RSA混合加密完整实例(转载)
前言 前段时间看到一篇文章讲如何保证API调用时数据的安全性(传送门:https://blog.csdn.net/ityouknow/article/details/80603617),文中讲到利用R ...
- 【WEB API项目实战干货系列】- API登录与身份验证(三)
上一篇: [WEB API项目实战干货系列]- 接口文档与在线测试(二) 这篇我们主要来介绍我们如何在API项目中完成API的登录及身份认证. 所以这篇会分为两部分, 登录API, API身份验证. ...
- WebApi基于Token和签名的验证
最近一段时间在学习WebApi,涉及到验证部分的一些知识觉得自己并不是太懂,所以来博客园看了几篇博文,发现一篇讲的特别好的,读了几遍茅塞顿开(都闪开,我要装逼了),刚开始读有些地方不理解,所以想了很久 ...
- iOS使用Security.framework进行RSA 加密解密签名和验证签名
iOS 上 Security.framework为我们提供了安全方面相关的api: Security框架提供的RSA在iOS上使用的一些小结 支持的RSA keySize 大小有:512,768,10 ...
随机推荐
- 【重学C++】05 | 说透右值引用、移动语义、完美转发(下)
文章首发 [重学C++]05 | 说透右值引用.移动语义.完美转发(下) 引言 大家好,我是只讲技术干货的会玩code,今天是[重学C++]的第五讲,在第四讲<[重学C++]04 | 说透右值引 ...
- VSCode 中利用 Remote SSH 连接远程服务器
北京时间 2019 年 5 月 3 日,在 PyCon 2019 大会上,微软发布了 VS Code Remote.这是一个用来实现远程开发的功能插件,对于许多使用 Windows 进行开发,但是需要 ...
- flutter系列之:做一个会飞的菜单
目录 简介 定义一个菜单项目 让menu动起来 添加菜单内部的动画 总结 简介 flutter中自带了drawer组件,可以实现通用的菜单功能,那么有没有一种可能,我们可以通过自定义动画来实现一个别样 ...
- 聊聊MAUI、WinUI3和WPF的优势及劣势
今天在群里聊到WinUI3的学习及发展,还有他那堪比玩具的使用体验,正好梳理一篇关于WinUI3.MAUI和WPF优劣势,我整理的不是很好,所以又让ChatGPT在生成了一遍,感觉整体还可以.看完可以 ...
- [Java SE] 彻底搞懂Java程序的三大参数配置途径:系统变量与JVM参数(VM Option)/环境变量/启动程序参数args
0 序言 一次没搞懂,处处受影响.这个问题属于基础问题,但又经常踩坑,不得不重视一下了. 1 Java程序动态参数的配置途径:系统变量与JVM参数(VM Option) vs 环境变量 vs 启动程序 ...
- 常用的Java Enum JdbcType
常用的Java Enum JdbcType ARRAY BIGINT BINARY BIT BLOB BOOLEAN CHAR CLOB CURSOR DATE DECIMAL DOUBLE FLOA ...
- 全同态(Fully Homomorphic Encryption, FHE)和半同态(Partially Homomorphic Encryption, PHE)介绍
全同态(Fully Homomorphic Encryption, FHE)和半同态(Partially Homomorphic Encryption, PHE) 全同态加密(FHE)是指一种加密方案 ...
- Gitlab版本升级
Gitlab docker部署命令 docker run -d -p 8443:443 -p 30080:80 -p 9444:22 --name gitlab --restart always \ ...
- 衔尾法解决当无法使用空闲中断以及DMA中断时配置DMA接收串口不定长数据
[Ooonly新人贴]记录工作中遇到的问题,话不多说先上干货 问题:类似K线与蓝牙接收部门模块,要求由原来的接收串口中断改为DMA接收.据说要用到空闲中断与DMA中断,但是经仿真发现DMA每完成传输一 ...
- PostgreSQL 12 文档: 部分 I. 教程
部分 I. 教程 欢迎来到PostgreSQL教程.下面的几章将为那些新接触PostgreSQL.关系数据库概念和 SQL 语言的读者给出一个简单介绍.我们只假定读者拥有关于如何使用计算机的一般知识. ...