JWT token generation and parsing authentication verification [C++]

There are many ways to implement Token. This article introduces Token generated by Json Web Token (JWT). What are the benefits of Token generated by JWT?

  • The security is relatively high, plus key encryption and supports multiple algorithms.
  • The information carried is customized, and it can be done to verify whether the token has expired.
  • The verification information can be saved by the front end, and the back end does not need to consume memory for saving tokens.

 1. JWT composition

The first part is called the header, the second part is called the payload, and the third part is the signature. There are many other information about the format of the header and payload on the Internet. Among them, you should know:

header, header information mainly includes (parameter type-JWT , signature algorithm- HS256, HS512, etc.)
poyload, the load is basically the information you want to store (because the information will be exposed, you should not add any Sensitive data)
sign, the role of signature is to prevent malicious tampering with data.

The final token : xxxxxx.yyyyyyyy.zzzzzz

2. Authentication on the server side

(1) The authentication token carried when the client connects to the server, for example: (the token information that needs to be used in all the following source codes is the following token)

eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJRTVVMVVhBQSIsInNjb3BlIjpbIlJPTEVfQ0xJRU5U
Il0sImlzcyI6Imh0dHA6Ly9za3lsaWdodC5jb20uaGsiLCJpZCI6ImZmNTdjOWUzLWI4YTItNGVkYi05
MDUzLTc1MzMxZTg4ZjQ4YSIsImV4cCI6MTU1MzA1MTI5NSwiaWF0IjoxNTUzMDQ3Njk1fQ.Em8HJJM2vCK2bqYD5qw-Czxz__hYuOw-DiDHYZPxmzH5clYIAFJ9WUgciihdbps8Fmm88gspYFoHqYRz8X5BfA

We can get the head after base64: eyJhbGciOiJIUzUxMiJ9

Payload after base64:

eyJzdWIiOiJRTVVMVVhBQSIsInNjb3BlIjpbIlJPTEVfQ0xJRU5UIl0sImlzcyI6Imh0dHA6Ly9za3lsaWdodC5jb20uaGsiLCJpZCI6ImZmNTdjOWUzLWI4YTItNGVkYi05MDUzLTc1MzMxZTg4ZjQ4YSIsImV4cCI6MTU1MzA1MTI5NSwiaWF0IjoxNTUzMDQ3Njk1fQ

And sign:

Em8HJJM2vCK2bqYD5qw-Czxz__hYuOw-DiDHYZPxmzH5clYIAFJ9WUgciihdbps8Fmm88gspYFoHqYRz8X5BfA

How is authentication passed? In other words, how do we think the server judges whether the client authentication is passed or not?

When generating token:

The third part of jwt is a visa information, this visa information consists of three parts:

  • header (after base64)
  • payload (after base64)
  • secret

This part requires the base64-encrypted header and base64-encrypted payload to use .the string formed by the connection, and then secret(密钥)combine and encrypt it through the encryption method declared in the header , and then constitute the third part of the JWT.

// javascript takes HMACSHA256 algorithm as an example
var encodedString = base64UrlEncode(header) +'.' + base64UrlEncode(payload);

var signature = HMACSHA256(encodedString, 'secret');

(2) Server authentication

You can base64 decode the header to get the specific algorithm, the code is as follows:

Base64 decoding function:

/*
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;
}

When we parse the header (eyJhbGciOiJIUzUxMiJ9), the information is as follows, so we can know what encryption algorithm is used;

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

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

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

	return 0;
}

At this point, we do this:

encode = Base64Encode(header) +'.' +Base64Encode(payload), which is the xxxx.yyyyy part of the token , so there is no need to perform Base64 operations on the server.

In the specific sign process, we must first know the Base64Encode implementation process, and then we will use:

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;

}

Next, use HMAC512 encryption. We believe that the server has obtained the key. The authorization verification process is as follows :

#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;
}

Note: The above Base64Encode and Base64Decode are all verified and the method can be used normally. If necessary, keep it by yourself. If you have any questions, please leave a message for guidance, thank you! [Welcome to add WeChat official account: yansuirengui]

Guess you like

Origin blog.csdn.net/Swallow_he/article/details/88684987