博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JWT之token生成和解析鉴权验证[C++]
阅读量:4165 次
发布时间:2019-05-26

本文共 6397 字,大约阅读时间需要 21 分钟。

实现Token的方式有很多,本篇介绍的是利用Json Web Token(JWT)生成的Token.JWT生成的Token有什么好处呢?

  • 安全性比较高,加上密匙加密而且支持多种算法。
  • 携带的信息是自定义的,而且可以做到验证token是否过期。
  • 验证信息可以由前端保存,后端不需要为保存token消耗内存。

 1.JWT构成

第一部分我们称它为头部(header),第二部分我们称其为载荷(payload),第三部分是签证(signature).网上也有很多其他资料关于header 、payload的格式,其中要知道:

header,头部信息主要包括(参数的类型--JWT,签名的算法--HS256、HS512等等)

poyload,负荷基本就是自己想要存放的信息(因为信息会暴露,不应该在载荷里面加入任何敏感的数据)
sign,签名的作用就是为了防止恶意篡改数据.

最终的token : xxxxxx.yyyyyyyy.zzzzzz

2.服务器端进行鉴权

(1)客户端连接服务端时,携带的鉴权token 比如:(以下的所有源码中需要用到的token信息都是如下token)

eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJRTVVMVVhBQSIsInNjb3BlIjpbIlJPTEVfQ0xJRU5UIl0sImlzcyI6Imh0dHA6Ly9za3lsaWdodC5jb20uaGsiLCJpZCI6ImZmNTdjOWUzLWI4YTItNGVkYi05MDUzLTc1MzMxZTg4ZjQ4YSIsImV4cCI6MTU1MzA1MTI5NSwiaWF0IjoxNTUzMDQ3Njk1fQ.Em8HJJM2vCK2bqYD5qw-Czxz__hYuOw-DiDHYZPxmzH5clYIAFJ9WUgciihdbps8Fmm88gspYFoHqYRz8X5BfA

我们可以拿到base64后的head:eyJhbGciOiJIUzUxMiJ9

base64后的payload:

eyJzdWIiOiJRTVVMVVhBQSIsInNjb3BlIjpbIlJPTEVfQ0xJRU5UIl0sImlzcyI6Imh0dHA6Ly9za3lsaWdodC5jb20uaGsiLCJpZCI6ImZmNTdjOWUzLWI4YTItNGVkYi05MDUzLTc1MzMxZTg4ZjQ4YSIsImV4cCI6MTU1MzA1MTI5NSwiaWF0IjoxNTUzMDQ3Njk1fQ

以及sign:

Em8HJJM2vCK2bqYD5qw-Czxz__hYuOw-DiDHYZPxmzH5clYIAFJ9WUgciihdbps8Fmm88gspYFoHqYRz8X5BfA

鉴权是如何通过的呢?或者说,我们认为服务端如何判断客户端鉴权通过与否呢?

在生成token时:

jwt的第三部分是一个签证信息,这个签证信息由三部分组成:

  • header (base64后的)
  • payload (base64后的)
  • secret

这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行secret(密钥)组合加密,然后就构成了JWT的第三部分。

// javascript  以HMACSHA256算法为例

var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);

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

(2)服务端鉴权

可以对header进行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),即是token中的xxxx.yyyyy部分,因此对于服务端无需再做Base64操作。

在进行具体的sign过程,先要知道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 
#include
#include "openssl/hmac.h"#include "openssl/evp.h" //使用openssl#include
#include
#include
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,都是经过验证,方法可以正常使用,如有需要自己保留,如有问题欢迎留言指导,谢谢![欢迎添加微信公总号:yansuirengui ]

转载地址:http://jjqxi.baihongyu.com/

你可能感兴趣的文章
mysqldump
查看>>
字符串 与 java.sql.Timestamp转换博客分类: javaJavaSQL
查看>>
奇怪的ubuntu不能解析域名的问题
查看>>
cat | wc -l 少一行的问题
查看>>
socket 科普文章
查看>>
Mutex, semaphore, spinlock的深度解析
查看>>
pthread线程使用小结
查看>>
线程池 范例
查看>>
utf8转gbk,去掉繁体字符
查看>>
UTF-8 GBK UTF8 GB2312 之间的区别和关系
查看>>
Linux内存管理之一 分段与分页
查看>>
Linux内存管理之二 内存节点和内存分区
查看>>
Linux内存管理之三 页的分配和释放
查看>>
Linux下基于C/C++的Socket编程实例
查看>>
linux-删除当前目录下,文件大小小于2k的文件
查看>>
Linux-压缩文件夹,并排除某个文件夹
查看>>
Linux-查看文件每行有多少列
查看>>
RobotFramework-接口测试-SSL双向验证证书配置
查看>>
RobotFramework-关键字-Wait Until Keyword Succeeds
查看>>
Ubuntu系统上轻松截图的几种方法介绍
查看>>