JWTトークンの生成と解析認証の検証[C ++]

トークンを実装する方法はたくさんあります。この記事では、Json Web Token(JWT)によって生成されたトークンを紹介します。JWTによって生成されたトークンの利点は何ですか?

  • セキュリティは比較的高く、キー暗号化に加えて、複数のアルゴリズムをサポートしています。
  • 運ばれる情報はカスタマイズされており、トークンの有効期限が切れているかどうかを確認するために行うことができます。
  • 検証情報はフロントエンドで保存でき、バックエンドはトークンを保存するためにメモリを消費する必要がありません。

 1.JWT構成

最初の部分はヘッダー、2番目の部分はペイロード、3番目の部分は署名と呼ばれます。インターネット上のヘッダーとペイロードの形式に関する情報は他にもたくさんありますが、その中には次のことを知っておく必要があります。

ヘッダー、ヘッダー情報には主に(パラメータータイプ-JWT 、署名アルゴリズム-HS256、HS512など)
poyloadが含まれ、負荷は基本的に保存する情報です(情報が公開されるため、機密データを追加しないでください)
署名、署名の役割は、データの悪意のある改ざん防ぐことです。

最終トークンxxxxxx.yyyyyyyy.zzzzzz

2.サーバー側での認証

(1)クライアントがサーバーに接続するときに運ばれる認証トークン。例:(以下のすべてのソースコードで使用する必要があるトークン情報は、次のトークンです)

eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJRTVVMVVhBQSIsInNjb3BlIjpbIlJPTEVfQ0xJRU5U
Il0sImlzcyI6Imh0dHA6Ly9za3lsaWdodC5jb20uaGsiLCJpZCI6ImZmNTdjOWUzLWI4YTItNGVkYi05
MDUzLTc1MzMxZTg4ZjQ4YSIsImV4cCI6MTU1MzA1MTI5NSwiaWF0IjoxNTUzMDQ3Njk1fQ.Em8HJJM2vCK2bqYD5qw-Czxz__hYuOw-DiDHYZPxmzH5clYIAFJ9WUgciihdbps8Fmm88gspYFoHqYRz8X5BfA

base64の後に頭を取得できます:eyJhbGciOiJIUzUxMiJ9

base64後のペイロード:

eyJzdWIiOiJRTVVMVVhBQSIsInNjb3BlIjpbIlJPTEVfQ0xJRU5UIl0sImlzcyI6Imh0dHA6Ly9za3lsaWdodC5jb20uaGsiLCJpZCI6ImZmNTdjOWUzLWI4YTItNGVkYi05MDUzLTc1MzMxZTg4ZjQ4YSIsImV4cCI6MTU1MzA1MTI5NSwiaWF0IjoxNTUzMDQ3Njk1fQ

そして署名:

Em8HJJM2vCK2bqYD5qw-Czxz__hYuOw-DiDHYZPxmzH5clYIAFJ9WUgciihdbps8Fmm88gspYFoHqYRz8X5BfA

認証はどのように渡されますか?言い換えれば、サーバーはクライアント認証が渡されたかどうかをどのように判断すると思いますか?

トークンを生成する場合:

jwtの3番目の部分はビザ情報であり、このビザ情報は3つの部分で構成されています。

  • ヘッダー(base64の後)
  • ペイロード(base64以降)
  • 秘密

この部分では、.接続によって形成された文字列を使用するためにbase64で暗号化されたヘッダーとbase64で暗号化されたペイロードが必要secret(密钥)です。次に、ヘッダー宣言された暗号化方式を使用し結合および暗号化、JWTの3番目の部分を構成します。

// javascriptは、例としてHMACSHA256アルゴリズムを採用します
var encodingString = base64UrlEncode(header)+ '。' + base64UrlEncode(payload);

varsignature = HMACSHA256(encodedString、 'secret');

(2)サーバー認証

ヘッダーをbase64デコードして、特定のアルゴリズムを取得できます。コードは次のとおりです。

Base64デコード機能:

/*
Base64解码
*/
static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";

static inline bool is_base64(unsigned char c) 
{
	return (isalnum(c) || (c == '+') || (c == '/'));
}
std::string Base64Decode(std::string const& encoded_string) 
{
	int in_len = encoded_string.size();
	int i = 0;
	int j = 0;
	int in_ = 0;
	unsigned char char_array_4[4], char_array_3[3];
	std::string ret;

	while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_]))
      {
		char_array_4[i++] = encoded_string[in_]; in_++;
		if (i == 4)
               {
			for (i = 0; i < 4; i++)
				char_array_4[i] = base64_chars.find(char_array_4[i]);

			char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
			char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
			char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

			for (i = 0; (i < 3); i++)
				ret += char_array_3[i];
			i = 0;
		}
	}

	if (i) 
        {
		for (j = i; j < 4; j++)
			char_array_4[j] = 0;

		for (j = 0; j < 4; j++)
			char_array_4[j] = base64_chars.find(char_array_4[j]);

		char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
		char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
		char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

		for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
	}

	return ret;
}

ヘッダー(eyJhbGciOiJIUzUxMiJ9)を解析すると、情報は次のようになり、使用されている暗号化アルゴリズムを知ることができます。

int main(int argc, char argv[])
{

	std::string headerstr = Base64Decode("eyJhbGciOiJIUzUxMiJ9");
	printf("header:%s\n", headerstr.c_str());

	/*
	header:{"alg":"HS512"}
	
	*/

	return 0;
}

この時点で、これを行います。

encode = Base64Encode(header)+ '。' + Base64Encode(payload)、これはトークンのxxxx.yyyyy部分であるため、サーバーでBase64操作を実行する必要はありません。

特定の署名プロセスでは、最初にBase64Encodeの実装プロセスを知る必要があり、次に以下を使用します。

std::string Base64Encode(unsigned char const* bytes_to_encode, unsigned int in_len) 
{
	std::string ret;
	int i = 0;
	int j = 0;
	unsigned char char_array_3[3];
	unsigned char char_array_4[4];

	while (in_len--)
      {
		char_array_3[i++] = *(bytes_to_encode++);
		if (i == 3) 
                {
			char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
			char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
			char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
			char_array_4[3] = char_array_3[2] & 0x3f;

			for (i = 0; (i < 4); i++)
				ret += base64_chars[char_array_4[i]];
			i = 0;
		}
	}

	if (i)
	{
		for (j = i; j < 3; j++)
			char_array_3[j] = '\0';

		char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
		char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
		char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
		char_array_4[3] = char_array_3[2] & 0x3f;

		for (j = 0; (j < i + 1); j++)
			ret += base64_chars[char_array_4[j]];

		while ((i++ < 3))
			ret += '=';

	}

	return ret;

}

次に、HMAC512暗号化を使用します。サーバーがキーを取得したと考えられます。認証検証プロセスは次のとおりです。

#include <string>
#include <stdint.h>
#include "openssl/hmac.h"
#include "openssl/evp.h"     //使用openssl
#include<algorithm>
#include<string>
#include<iostream>

int main(int argc, char argv[])
{

        //注意密钥
	const char * key = "ee394b990568d08a";

	const  char  str[] = "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJRTVVMVVhBQSIsInNjb3BlIjpbIlJPTEVfQ0xJRU5UIl0sImlzcyI6Imh0dHA6Ly9za3lsaWdodC5jb20uaGsiLCJpZCI6ImZmNTdjOWUzLWI4YTItNGVkYi05MDUzLTc1MzMxZTg4ZjQ4YSIsImV4cCI6MTU1MzA1MTI5NSwiaWF0IjoxNTUzMDQ3Njk1fQ";
        
	HMAC_CTX       mdctx;

	const EVP_MD * evpmd = NULL;

	evpmd = EVP_sha512();     //算法

	HMAC_CTX_init(&mdctx);

	HMAC_Init_ex(&mdctx, key, strlen(key), evpmd, NULL);

        unsigned char * outdate = (unsigned char*)malloc(EVP_MAX_MD_SIZE);

	unsigned int   size = 0;

	HMAC_Update(&mdctx, (const unsigned char *)str, strlen(str));

	HMAC_Final(&mdctx, outdate, &size);

	HMAC_CTX_cleanup(&mdctx);

	for (int i = 0; i < size; i++)
	{
	    printf("%x", (unsigned int)outdate[i]);
	}
	printf("\n");

        //注意:此时得到的outdate并非是token中的sign,还需要继续处理,如下:

	std::string encode = Base64Encode(outdate, size);   //使用Base64Encode

	printf("encode:%s\n", encode.c_str());

	std::string signature = encode;

        //思考:为什么接下来要对符号处理??这操作不能缺少!!
//
	replace(signature.begin(), signature.end(), '+', '-');
	replace(signature.begin(), signature.end(), '/', '_');

	std::string::iterator iter;
	for (iter = signature.begin(); iter != signature.end(); iter++)
	if (*iter == '=')
	{
		signature.erase(iter);
		iter--;
	}
///
	printf("signature:%s\n", signature.c_str());

	int lsuccess = 0;
	lsuccess = strncmp("Em8HJJM2vCK2bqYD5qw-Czxz__hYuOw-DiDHYZPxmzH5clYIAFJ9WUgciihdbps8Fmm88gspYFoHqYRz8X5BfA", signature.c_str(), signature.size());
       //字符串比较相等,鉴权也就通过了,签名OK

	printf("lsuccess:%d\n", lsuccess);

	return 0;
}

注:上記のBase64EncodeとBase64Decodeはすべて検証済みであり、このメソッドは通常どおり使用できます。自分で保持する必要がある場合、質問がある場合は、メッセージとガイドを残してください。ありがとうございます。[WeChat公式アカウントの追加へようこそ:yansuirengui]

おすすめ

転載: blog.csdn.net/Swallow_he/article/details/88684987