API接口签名方式

背景

为了确保接口的安全性和数据的完整性,以及验证请求的来源和身份。以下是签名在API接口中的一些关键作用:

  1. 身份验证:签名可以帮助API服务验证请求的来源是否可信。当客户端发送请求时,它会在请求中包含一个签名,这个签名通常基于请求的参数、时间戳和一个只有客户端和服务端知道的密钥。服务端可以验证这个签名是否有效,从而确认请求是否来自一个已授权的客户端。
  2. 防止重放攻击:在签名中包含一个时间戳或nonce(一次性随机数)可以防止攻击者重放(即重复使用)旧的请求。服务端可以检查时间戳或nonce是否已被使用过,从而防止重放攻击。
  3. 防止篡改:签名可以确保请求在传输过程中没有被篡改。如果请求的参数被恶意修改,那么签名也会相应地改变,服务端在验证签名时就能发现这一点。
  4. 提高安全性:使用密钥进行签名可以确保只有知道密钥的客户端才能发送有效的请求。这增加了API的安全性,因为即使攻击者截获了请求,他们也无法伪造有效的签名(除非他们也知道密钥)。
  5. 支持权限控制:通过不同的签名密钥或签名方法,服务端可以实现更细粒度的权限控制。例如,不同的客户端或用户可能使用不同的密钥进行签名,服务端可以根据签名所使用的密钥来判断请求者的权限。
  6. 简化日志和审计:由于签名包含了请求的来源和参数等信息,因此它可以作为日志和审计的重要依据。当出现问题时,服务端可以通过分析签名来快速定位问题的来源和原因。
    在实际应用中,常见的签名方法包括HMAC(基于密钥的哈希消息认证码)、OAuth等。这些方法都使用了一个密钥来生成签名,并且都有相应的验证算法来确保签名的有效性和安全性
    其实有很多种签名或加密方式对API接口进行安全认证,只需要使用双方制定一套规则。
    在这介绍2种简单的方式:

数字签名

规则: 对字段进行字典排序,按规则拼接(规则自定义 常见的有key=value&key2=value2&…&)

示例代码:

/**
     * 参数签名
     * 有值参数字典排序
     * key1=value1&key2=value2&...&key=randomStr
     * @param object     参数
     * @param randomStr 随机字符,可自定义规则
     * @return
     */
    public String getSign(Object object, String randomStr) {
        if (object == null) {
            return "";
        }

        Map map = JSONObject.parseObject(JSON.toJSONString(object), Map.class);
        StringBuilder stringBuffer = new StringBuilder();
        SortedMap<String, Object> sortedMap = new TreeMap<>(map);
        Set<Map.Entry<String, Object>> set = sortedMap.entrySet();
        set.forEach(entry -> {
            stringBuffer.append(entry.getKey() + "=" + entry.getValue() + "&");
        });

        stringBuffer.append("key=").append(randomStr);
        String str = stringBuffer.toString();
        // 这个结果得到的是个拼接后的字符串明文,可以对该字符串进行加密传输,加密方式可自行选择
        // 可以用MD5, 对称加密等
        return str;
    }

利用JWT

JWT简介

JWT(JSON Web Token)是一种开放标准(RFC 7519)定义的方式,用于在网络通信中安全地表示声明(claims)。JWT通常用于在客户端和服务端之间安全地传输信息,特别是在无状态的RESTful API中。JWT的结构包括三个部分:头部(Header)、载荷(Payload)和签名(Signature)。

头部(Header):描述了JWT的元数据,包括使用的签名算法(如HMAC SHA256或RSA)和JWT的类型(通常是JWT)。这些元数据被Base64Url编码后形成JWT的第一部分。
载荷(Payload):包含了需要传输的用户信息,例如用户名、用户ID、角色等。这些信息也是经过Base64Url编码的。另外,JWT还定义了一些标准字段,如exp(过期时间戳)、iss(发行者)等,也可以根据需要添加到载荷中。
签名(Signature):签名部分是对头部和载荷的签名,用于验证JWT的完整性和发送者的身份。签名是使用服务器私钥和指定的签名算法(如HMAC SHA256或RSA)进行的。签名完成后,也会进行Base64Url编码,形成JWT的第三部分。

示例代码:

public class JwtTokenUtils {

    private static final String TOKEN_TYPE = "TokenType";

    private static final String TOKEN = "Token";

    private static final String REFRESH_TOKEN = "RefreshToken";

    private static final String USER_NAME = "UserName";

    private static final String DEFAULT = "default";

    /**
     * 密钥
     */
    private static final String SECRET = "SHIRO+JWT+XXX";

    private static final Algorithm HMAC_256 = Algorithm.HMAC256(SECRET);

    /**
     * 创建token
     * @param userName
     * @param timeoutSec
     * @return
     */
    public static String createToken(String userName, long timeoutSec, String jti) {
        return createToken(userName, TOKEN, timeoutSec, jti);
    }

    /**
     * 创建refreshToken
     * @param userName
     * @param timeoutSec
     * @return
     */
    public static String createRefreshToken(String userName, long timeoutSec, String jti) {
        return createToken(userName, REFRESH_TOKEN, timeoutSec, jti);
    }

    /**
     * 创建refreshToken
     * @param userName
     * @param timeoutSec
     * @return
     */
    public static String createToken(String userName, String tokenTypeValue, long timeoutSec, String jti) {
        Date expires = new Date(System.currentTimeMillis() + timeoutSec * 1000);
        return JWT.create()
                .withClaim(USER_NAME, userName)
                .withClaim(TOKEN_TYPE, tokenTypeValue)
                .withClaim("jti", jti)
                .withExpiresAt(expires)
                .sign(HMAC_256);

    }

    /**
     * 校验 token 是否正确
     *
     * @param token 密钥
     * @return 是否正确
     */
    public static boolean verify(String token) {
        if(token == null || token.trim().isEmpty()){
            return false;
        }
        try {
            //在token中附带了username信息
            JWTVerifier verifier = JWT.require(HMAC_256)
                    .build();
            //验证 token
            verifier.verify(token);
            return true;
        } catch (Exception exception) {
            return false;
        }
    }

    /**
     * token是否过期
     * @param token
     * @return
     */
    public static boolean isTokenExpired(String token) {
        Optional<Date> optional = getExpiresAt(token);
        if (optional.isPresent()) {
            return optional.get().before(new Date());
        }

        return true;
    }

    /**
     * 获取参数token所载的过期时间
     *
     * @param token
     * @return
     */
    public static Optional<Date> getExpiresAt(String token) {
        if (token == null || token.trim().isEmpty()) {
            return Optional.empty();
        }

        try {
            DecodedJWT jwt = JWT.decode(token);
            return Optional.of(jwt.getExpiresAt());
        } catch (JWTDecodeException e) {
            return Optional.empty();
        }
    }

    public static String getUserName(String token) {
        if (StringUtils.isBlank(token)) {
            return "";
        }

        try {
            DecodedJWT jwt = JWT.decode(token);
            return jwt.getClaim(USER_NAME).asString();
        } catch (JWTDecodeException e) {
            return "";
        }
    }

    /**
     * 刷新token。 如果提供的token合法,则返回一个新的token,重置过期时间
     *
     * @param token
     * @param timeout
     * @return
     */
    public static String refreshToken(String token, long timeout, String jti) {
        if(token == null || token.trim().isEmpty()){
            return "";
        }

        DecodedJWT jwt = JWT.decode(token);
        String userName = jwt.getClaim(USER_NAME).asString();
        return createToken(userName, timeout, jti);
    }

}

以上仅为日常记录,欢迎路过的大佬,大神来指导。

  • 12
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值