C++ token JWT HS256 生成token和验证token

使用到的类库

jsoncpp:用于json数据操作

boost:智能指针和正则表达,如果是C++11以上版本可以替换成C++11的智能指针和正则表达式

CryptoPP:大名鼎鼎的免费的C++加解密类库,Crypto++几乎涵盖了所有的加解密算法

以上类库是可以跨平台使用
代码

宏定义请参看负载均衡权重系数算法 C++中的se_std.h
头文件

#include <string>
#include  <strstream>
#include <boost/noncopyable.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/regex.hpp>
#include <json/json.h>
#include "se_std.h"
#include "se_conf.h"
#include <base64.h>
#include  <hmac.h>
#include  <sha.h>

using namespace std;

#ifndef H_3695350A_4107_49D7_BE41_60F19549BD53
#define H_3695350A_4107_49D7_BE41_60F19549BD53

/*
*    Author:
*    Version:1.0
*    Copyright:
*    Description:    生成token和验证token
*                        https://jwt.io/introduction/
*    Create:2018-02-01
*    LastChange:2018-02-01
*/
class se_token : public boost::noncopyable {
private:
    const int FLAG;
    CryptoPP::HMAC< CryptoPP::SHA256 > m_hs256;
    boost::scoped_ptr<Json::StreamWriter> m_jwrite;
    boost::scoped_ptr<Json::CharReader> m_jread;
    stringstream m_strbuf;
    string m_header, m_header_base64;
    Json::Value m_jheader;
    boost::regex m_reg_exp;
    char m_numbuf[SE_MAX_NUMSTR];
public:
    se_token()
        :FLAG(CryptoPP::HashVerificationFilter::PUT_RESULT | CryptoPP::HashVerificationFilter::HASH_AT_END) {
        this->init();
    };
    se_token(const CryptoPP::SecByteBlock &key)
        :m_hs256(key, key.size()),
        FLAG(CryptoPP::HashVerificationFilter::PUT_RESULT | CryptoPP::HashVerificationFilter::HASH_AT_END) {
        this->init();
    };
    virtual ~se_token() {};
private:
    /*初始化*/
    void init();
    /*清除字符缓冲区内容*/
    inline void clear(stringstream &strbuf) const {
        strbuf.str(""); strbuf.clear();
    }
private:
    void parse_token(const string &token, string &msg, string &b64payload, string &b64sig) const;
public:
    /*设置HMAC key*/
    inline void set_key(const CryptoPP::SecByteBlock &key) {
        this->m_hs256.SetKey(key, key.size());
    }
    /*设置HMAC key(key为base64编码)*/
    void set_key_base64(const string &b64key);
    /*生成key*/
    void generate_key(string &b64key) const;
    /*生成签名*/
    void signature(const string &plain, string &mac);
    /*生成签名(签名为base64编码)*/
    void signature_base64(const string &plain, string &b64);
    /*验证签名*/
    bool verify(const string &msg, const string &signature);
    /*验证签名(签名为base64编码)*/
    bool verify_base64(const string &msg, const string &b64);
    /*获取JWT Header信息*/
    inline const string &header() const {
        return this->m_header;
    }
    /*获取JWT Header信息*/
    inline const Json::Value &header_json() const {
        return this->m_jheader;
    }
    /*    根据json生成token
    *    注意,生成的token如要附件在url中必须采用url_encode后输出
    */
    void generate_token(const Json::Value &payload, string &token);
    /*    根据payload json格式字符串生成token
    *    注意,生成的token如要附件在url中必须采用url_encode后输出
    */
    void generate_token(const string &payload, string &token);
    /*解析token*/
    void parse_token(const string &token, Json::Value &payload);
    /*更新token的过期时间*/
    void update_expires(const se_conf_server &conf, string &token);
};

#endif /* H_3695350A_4107_49D7_BE41_60F19549BD53 */

源文件

#include "se_token.h"
#include <osrng.h>

#define  HS256KEY ("eoRK6DnPRTbQ72oEG+ANFg==")

void se_token::init() {
    Json::StreamWriterBuilder writer;
    writer["commentStyle"] = "None";
    writer["indentation"] = "";  // or whatever you like    
    writer["precision"] = 6;
    this->m_jwrite.reset(writer.newStreamWriter());

    Json::CharReaderBuilder rbuilder;
    this->m_jread.reset(rbuilder.newCharReader());

    this->m_header = "{\"typ\":\"JWT\",\"alg\":\"HS256\"}";
    CryptoPP::StringSource(this->m_header, true,
        new CryptoPP::Base64Encoder(
            new CryptoPP::StringSink(this->m_header_base64), false
        )
    );

    this->m_reg_exp = "(\\\"exp\\\")\\:(\\d+)";

    this->m_jheader["typ"] = "JWT";
    this->m_jheader["alg"] = "HS256";
    this->set_key_base64(HS256KEY);
}

void se_token::generate_key(string &b64key) const {
    CryptoPP::AutoSeededRandomPool prng;
    CryptoPP::SecByteBlock key(16);
    prng.GenerateBlock(key, key.size());

    CryptoPP::StringSource(key, key.size(), true,
        new CryptoPP::Base64Encoder(
            new CryptoPP::StringSink(b64key), false
        )
    );
}

void se_token::set_key_base64(const string &b64key) {
    CryptoPP::SecByteBlock key;
    string encoded;
    CryptoPP::StringSource(b64key, true,
        new CryptoPP::Base64Decoder(
            new CryptoPP::StringSink(encoded)
        )
    );
    key.Assign((const byte *)encoded.c_str(), encoded.size());
    this->set_key(key);
}

void se_token::signature(const string &plain, string &mac) {
    mac.clear();
    CryptoPP::StringSource ss1(plain, true,
        new CryptoPP::HashFilter(this->m_hs256,
            new CryptoPP::StringSink(mac)
        )
    );

}

void se_token::signature_base64(const string &plain, string &b64) {
    string mac;
    b64.clear();
    this->signature(plain, mac);
    CryptoPP::StringSource(mac, true,
        new CryptoPP::Base64Encoder(
            new CryptoPP::StringSink(b64), false
        )
    );
}

bool se_token::verify(const string &msg, const string &signature) {
    bool result = false;
    CryptoPP::StringSource ss(msg + signature, true,
        new CryptoPP::HashVerificationFilter(this->m_hs256,
            new CryptoPP::ArraySink((byte*)&result, sizeof(result)),
            FLAG
        )
    );
    return result;
}

bool se_token::verify_base64(const string &msg, const string &b64sig) {
    string signature;
    CryptoPP::StringSource(b64sig, true,
        new CryptoPP::Base64Decoder(
            new CryptoPP::StringSink(signature)
        )
    );
    return this->verify(msg, signature);
}

void se_token::generate_token(const Json::Value &payload, string &token) {
    this->clear(this->m_strbuf);
    ostream stream(this->m_strbuf.rdbuf());
    this->m_jwrite->write(payload, &stream);
    string str_payload = this->m_strbuf.str();
    this->clear(this->m_strbuf);

    string payload_base64, signature;
    CryptoPP::StringSource(str_payload, true,
        new CryptoPP::Base64Encoder(
            new CryptoPP::StringSink(payload_base64), false
        )
    );

    token.clear();
    token.append(this->m_header_base64).append(".").append(payload_base64);
    this->signature_base64(token, signature);
    token.append(".").append(signature);
}

void se_token::generate_token(const string &payload, string &token) {
    string payload_base64, signature;
    CryptoPP::StringSource(payload, true,
        new CryptoPP::Base64Encoder(
            new CryptoPP::StringSink(payload_base64), false
        )
    );

    token.clear();
    token.append(this->m_header_base64).append(".").append(payload_base64);
    this->signature_base64(token, signature);
    token.append(".").append(signature);
}

void se_token::parse_token(const string &token, string &msg, string &b64payload, string &b64sig) const {
    string::size_type pos1 = token.find(".");
    string::size_type pos2 = token.find(".", pos1 + 1);
    if (string::npos == pos1 || string::npos == pos2)
        SE_THROW("invalid token!");
    msg = token.substr(0, pos2);
    ++pos1;
    b64payload = token.substr(pos1, pos2 - pos1);
    ++pos2;
    b64sig = token.substr(pos2);
}

void se_token::parse_token(const string &token, Json::Value &payload) {
    string msg, b64payload, b64sig;
    this->parse_token(token, msg, b64payload, b64sig);
    if (!this->verify_base64(msg, b64sig))
        SE_THROW("lllegal token!");

    string plian, tmp;
    CryptoPP::StringSource(b64payload, true,
        new CryptoPP::Base64Decoder(
            new CryptoPP::StringSink(plian)
        )
    );

    const char *ptr_start = plian.c_str();
    const char *ptr_end = ptr_start + plian.size();
    bool parsingSuccessful = this->m_jread->parse(ptr_start, ptr_end, &payload, &tmp);
    if (!parsingSuccessful)
        SE_THROW(tmp);
}

void se_token::update_expires(const se_conf_server &conf, string &token) {
    string msg, b64payload, b64sig;
    this->parse_token(token, msg, b64payload, b64sig);
    if (!this->verify_base64(msg, b64sig))
        SE_THROW("invalid token!");

    string plian;
    CryptoPP::StringSource(b64payload, true,
        new CryptoPP::Base64Decoder(
            new CryptoPP::StringSink(plian)
        )
    );

    /*获取当前时间*/
    const time_t tt = time(NULL);
    time_t exp = tt + conf.expires();
#if defined(_MSC_VER)
    _snprintf_s(this->m_numbuf, SE_MAX_NUMSTR, "$1:%I64d", exp);
#else
    snprintf(this->m_numbuf, SE_MAX_NUMSTR, "$1:%lld", exp);
#endif
    //替换过期时间
    std::string result = boost::regex_replace(plian, this->m_reg_exp, this->m_numbuf);
    this->generate_token(result, token);
}

输出

调用generate_token方法后的输出结果(JWT)

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MTc0OTgwNzQsImlzcyI6ImttY2IuY29tIiwibmFtZSI6InVzZXJpZCIsInVzZXJpZCI6MX0=.u3lauh/Inw+fTq7kITL+5mwQQ65LP5t2g9CHXWQZkrk=

如果要直接在url中使用,还需要对上述字符串进行url编码,否则因url编码的原因,会导致验证失败.

/*经过url编码后的token,验证成功*/
http://localhost:9101/heightservice?m=chatoken&token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MTc0OTk5ODAsImlzcyI6ImttY2IuY29tIiwibmFtZSI6InVzZXJpZCIsInVzZXJpZCI6MX0%3D.EGK4P7wqMgm0HjwLuApkqVvyc5AZUJ6zvnQsBrqlhHM%3D

2018-11-30补充

根据JWT的标准,token Base64还需要用下面两个函数转换.转换后url就没问题了,同时可以在JWT网站上解析token

void SE_base64uri_encode(char *b64, int32_t len) {
    int i, t;
    for (i = t = 0; i < len; i++) {
        switch (b64[i]) {
        case '+':
            b64[t++] = '-';
            break;
        case '/':
            b64[t++] = '_';
            break;
        case '=':
            break;
        default:
            b64[t++] = b64[i];
        }
    }
    b64[t] = '\0';
}

bool SE_base64uri_decode(const char *src, int32_t len, char **base64) {
    int32_t i = 0, z = 0;
    char *bin_data = NULL;


    bin_data = malloc(len + 4);
    if (SE_PTR_ISNULL(bin_data))
        goto SE_ERROR_CLEAR;
    for (i = 0; i < len; i++) {
        switch (src[i]) {
        case '-':
            bin_data[i] = '+';
            break;
        case '_':
            bin_data[i] = '/';
            break;
        default:
            bin_data[i] = src[i];
        }
    }
    z = 4 - (i % 4);
    if (z < 4) {
        while (z--)
            bin_data[i++] = '=';
    }
    bin_data[i] = '\0';
    *base64 = bin_data;
    return true;
SE_ERROR_CLEAR:
    SE_free(bin_data);
    return false;
}

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JWT(JSON Web Token)是一种用于身份验证和授权的开放标准(RFC 7519)。它是一种轻量级的安全传输方式,用于在网络应用间传递声明信息。JWT由三部分组成:头部(Header)、载荷(Payload)和签名(Signature)。 头部包含了关于令牌的元数据和加密算法的信息,通常由两部分组成:令牌类型(即JWT)和所使用的签名算法(如HMAC SHA256或RSA)。 载荷是JWT的主要内容,包含了一些声明信息,如用户ID、角色、权限等。载荷可以自定义,但建议只包含一些非敏感的信息,因为JWT是可解码的。 签名是对头部和载荷进行加密生成的,用于验证JWT的真实性和完整性。签名需要使用头部中指定的算法和密钥进行生成,接收方可以通过验证签名来确保JWT没有被篡改。 生成JWT token的过程如下: 1. 创建一个包含所需声明信息的JSON对象。 2. 使用Base64编码头部和载荷,形成两个字符串。 3. 将两个字符串用点号连接起来,形成一个未签名的JWT。 4. 使用指定的算法和密钥对未签名的JWT进行签名,生成签名字符串。 5. 将签名字符串添加到未签名的JWT末尾,形成最终的JWT token验证JWT token的过程如下: 1. 将接收到的JWT token按点号分割为头部、载荷和签名三部分。 2. 使用相同的算法和密钥对头部和载荷进行签名,生成一个新的签名字符串。 3. 将新生成的签名字符串与接收到的签名进行比较,如果相同,则说明JWT token是有效的。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值