无法比较的两个概念:JWT和Session

这两个概念单独解释其实很简单,但是混在一起可能会说不清。正好看到网上有人聊起了这个,我就做个整理,也当知识回顾。

问题:为什么要用 jwt token 来做身份认证,而不是使用session+cookie方式?

那么我们先看概念本身的解释:

JWT ——JSON Web Token

JWE——JSON Web Encryption

JWS——JSON Web Signature

Session,此处可以翻译为会话,指客户端和服务端维持有状态的一种通信机制。根据Session Data的存储位置,可以分为Server Side Session和 Client Side Session。一般说的session & cookie机制是指使用的Server Side Session。

所以JWT和Session不是同一个概念的东西,没法拿来比较。回头再看这个问题就发现有点意思了。我们使用jwt token来做身份认证,我们可以把jwt token(会含有一些用户信息)通过cookie、HTTP头,url参数等多种方式下发给客户端(Client Side Session)从而实现session机制;而传统的服务端session-data + session-id(by cookie)方式,session-id只是一串随机字符串,不用解析,在和服务端交互时直接原样传输就行了。所以原作者问题可能是Server Side Session和Client Side Session的区别!

References:

https://en.wikipedia.org/wiki/JSON_Web_Token

https://blog.by24.cn/archives/about-session.html

https://www.v2ex.com/t/774127

1MB=1024KB? 1000KB?

如题,这个问题第一感觉是1MB肯定等于1024KB,只有在硬盘生产厂商那里才会使用1000进制。事实是如此吗?最近被人补了一课,了解单位换算中的两种标准。

按照国际单位制的标准: 1MB=1000KB (1Megabyte = 1000Kilobyte)
按照IEC 60027-2标准: 1MiB=1024KiB (1Mebibyte = 1024 Kibibyte)

字节的次方单位
十进制前缀
(SI)
名字 缩写 次方
Kilobyte KB 103
Megabyte MB 106
Gigabyte GB 109
Terabyte TB 1012
Petabyte PB 1015
Exabyte EB 1018
Zettabyte ZB 1021
Yottabyte YB 1024
二进制前缀
(IEC 60027-2)
名字 缩写 次方
Kibibyte KiB 210
Mebibyte MiB 220
Gibibyte GiB 230
Tebibyte TiB 240
Pebibyte PiB 250
Exbibyte EiB 260
Zebibyte ZiB 270
Yobibyte YiB 280

所以严格来说如果显示为1MB,则说明使用的是国际单位制标准,等于1000KB。1MiB说明是使用的IEC 60027-2标准,等于1024KiB。

  • 但是注意,Microsoft Windows系统中仍在大量使用公制前缀的二进制写法(比如显示为1MB的文件,按理说应该是使用的SI标准,等于1000KB,但是实际windows却按IEC 60027-2标准来处理的,实际文件大小是1024KB)。
  • Mac OS X自Snow Leopard(v10.6)发行以来,文件大小均是以十进制前缀记录的(比如显示为14KB,87.1MB)。(但是在shell环境里,部分软件使用的M,K等标识符表示1024进制,比如ls;但是df命令使用的是Gi,Mi等SI单位制的符号)

 

References:

https://zh.wikipedia.org/wiki/国际单位制

https://zh.wikipedia.org/wiki/千字节

Go to WebAssembly

什么是WebAssembly?

WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable target for compilation of high-level languages like C/C++/Rust, enabling deployment on the web for client and server applications.

让我们用tinygo来写一个WebAssembly的模块,然后用js调用吧。首先下载TINYGO:(https://tinygo.org/,请下载对应版本的二进制文件)。然后将bin/tinygo(or tinygo.exe)加入到PATH路径。

然后先创建一个main.go文件(文件名必须是main.go,因为js里有用到文件名),文件内容如下:

package main

// This calls a JS function from Go.
func main() {
    println("Go: Hello World") // expecting 5
    println("Go: add(2+3):", add(2, 3)) // expecting 5
    println("Go: multiply(2*3):", multiply(2, 3)) // expecting 6
}

// This function is imported from JavaScript, as it doesn't define a body.
// You should define a function named 'main.add' in the WebAssembly 'env'
// module from JavaScript.
func add(x, y int) int

// This function is exported to JavaScript, so can be called using
// exports.multiply() in JavaScript.
//go:export multiply
func multiply(x, y int) int {
    return x * y;
}

然后用tinygo编译这个go文件,生成wasm文件。

目前最新版的tinygo0.12.0,最高只支持golang 1.13(目前最新版本的golang1.14),编译的命令中文件名必须是main.go,不能写”./main.go”

tinygo build -o wasm.wasm -target wasm main.go

Error:  tinygo build -o wasm.wasm -target wasm ./main.go

 

然后用go写一个简单的文件服务器(用于启动http服务器,因为wasm加载必须在http/https的环境下),注意对于wasm文件,要设置Content-Type为application/wasm

package main

import (
    "log"
    "net/http"
    "strings"
)

const dir = "./html"

func main() {
    fs := http.FileServer(http.Dir(dir))
    log.Print("Serving " + dir + " on http://localhost:8080")
    http.ListenAndServe(":8080", http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
        resp.Header().Add("Cache-Control", "no-cache")
        if strings.HasSuffix(req.URL.Path, ".wasm") {
            resp.Header().Set("Content-Type", "application/wasm")
        }
        fs.ServeHTTP(resp, req)
    }))}

然后在服务器目录下创建html目录,目录里新建index.html文件(注意js代码中设置env的代码):

<html>
<head>
    <title>WebAssembly</title>
</head>
<body>
<script src="wasm_exec.js"></script>
<script type="text/javascript">
    const go = new Go(); // Defined in wasm_exec.js
    const WASM_URL = 'wasm.wasm';
    go.importObject.env['main.go.add'] = function (x, y) {
        return x + y
    };
    var wasm;

    console.log("'instantiateStreaming' in WebAssembly => " + ('instantiateStreaming' in WebAssembly ? "Y" : "N"));
    if ('instantiateStreaming' in WebAssembly) {
        WebAssembly.instantiateStreaming(fetch(WASM_URL), go.importObject).then(function (obj) {
            wasm = obj.instance;
            go.run(wasm);
            console.log('JS: multiplied two numbers:', wasm.exports.multiply(5, 3));
        }).catch((err) => {
            console.error(err);
        });
    } else {
        fetch(WASM_URL).then(resp =>
            resp.arrayBuffer()
        ).then(bytes =>
            WebAssembly.instantiate(bytes, go.importObject).then(function (obj) {
                wasm = obj.instance;
                go.run(wasm);
            })
        )
    }

</script>
</body>
</html>

index.html里面引用的wasm_exec.js文件就在tinygo下载包里,wasm.wasm就是我们刚刚编译生成的文件。

浏览器里打开这个网页 http://localhost:8080/index.html,发现控制台就能输出:

23:25:58.955 ‘instantiateStreaming’ in WebAssembly => Y

23:25:58.964 Go: Hello World

23:25:58.964 Go: add(2+3): 5

23:25:58.964 Go: multiply(2*3): 6

23:25:58.964 JS: multiplied two numbers: 15

23:25:58.966 Fetch finished loading: GET “http://localhost:8080/wasm.wasm”.

References:

https://webassembly.org/

https://tinygo.org/

 

对opencv的例子改进能识别出同一张图里面所有匹配的图像

/*
* A Demo to OpenCV Implementation of SURF
* Further Information Refer to “SURF: Speed-Up Robust Feature”
* Author: Liu Liu
* liuliu.1987+opencv@gmail.com
*/
#include “opencv2/objdetect/objdetect.hpp”
#include “opencv2/features2d/features2d.hpp”
#include “opencv2/highgui/highgui.hpp”
#include “opencv2/calib3d/calib3d.hpp”
#include “opencv2/nonfree/nonfree.hpp”
#include “opencv2/imgproc/imgproc_c.h”
#include “opencv2/legacy/legacy.hpp”
#include “opencv2/legacy/compat.hpp”

#include <iostream>
#include <vector>
#include <stdio.h>

using namespace cv;
using namespace std;

static void
flannFindPairs( const CvSeq*, const CvSeq* objectDescriptors,
const CvSeq*, const CvSeq* imageDescriptors, vector<int>& ptpairs )
{
int length = (int)(objectDescriptors->elem_size/sizeof(float));

cv::Mat m_object(objectDescriptors->total, length, CV_32F);
cv::Mat m_image(imageDescriptors->total, length, CV_32F);
// copy descriptors
CvSeqReader obj_reader;
float* obj_ptr = m_object.ptr<float>(0);
cvStartReadSeq( objectDescriptors, &obj_reader );
for(int i = 0; i < objectDescriptors->total; i++ )
{
const float* descriptor = (const float*)obj_reader.ptr;
CV_NEXT_SEQ_ELEM( obj_reader.seq->elem_size, obj_reader );
memcpy(obj_ptr, descriptor, length*sizeof(float));
obj_ptr += length;
}
CvSeqReader img_reader;
float* img_ptr = m_image.ptr<float>(0);
cvStartReadSeq( imageDescriptors, &img_reader );
for(int i = 0; i < imageDescriptors->total; i++ )
{
const float* descriptor = (const float*)img_reader.ptr;
CV_NEXT_SEQ_ELEM( img_reader.seq->elem_size, img_reader );
memcpy(img_ptr, descriptor, length*sizeof(float));
img_ptr += length;
}

// find nearest neighbors using FLANN
cv::Mat m_indices(objectDescriptors->total, 2, CV_32S);
cv::Mat m_dists(objectDescriptors->total, 2, CV_32F);
cv::flann::Index flann_index(m_image, cv::flann::KDTreeIndexParams(4)); // using 4 randomized kdtrees
flann_index.knnSearch(m_object, m_indices, m_dists, 2, cv::flann::SearchParams(64) ); // maximum number of leafs checked

int* indices_ptr = m_indices.ptr<int>(0);
float* dists_ptr = m_dists.ptr<float>(0);
for (int i=0;i<m_indices.rows;++i) {
if (dists_ptr[2*i]<0.6*dists_ptr[2*i+1]) {
ptpairs.push_back(i);
ptpairs.push_back(indices_ptr[2*i]);
}
}
}

/* a rough implementation for object location */
static int
locatePlanarObject( const CvSeq* objectKeypoints, const CvSeq* objectDescriptors,
const CvSeq* imageKeypoints, const CvSeq* imageDescriptors,
const CvPoint src_corners[4], CvPoint dst_corners[4] )
{
double h[9];
CvMat _h = cvMat(3, 3, CV_64F, h);
vector<int> ptpairs;
vector<CvPoint2D32f> pt1, pt2;
CvMat _pt1, _pt2;
int i, n;

flannFindPairs( objectKeypoints, objectDescriptors, imageKeypoints, imageDescriptors, ptpairs );

n = (int)(ptpairs.size()/2);
if( n < 4 )
return 0;

pt1.resize(n);
pt2.resize(n);
for( i = 0; i < n; i++ )
{
pt1[i] = ((CvSURFPoint*)cvGetSeqElem(objectKeypoints,ptpairs[i*2]))->pt;
pt2[i] = ((CvSURFPoint*)cvGetSeqElem(imageKeypoints,ptpairs[i*2+1]))->pt;
}

_pt1 = cvMat(1, n, CV_32FC2, &pt1[0] );
_pt2 = cvMat(1, n, CV_32FC2, &pt2[0] );
if( !cvFindHomography( &_pt1, &_pt2, &_h, CV_RANSAC, 5 ))
return 0;

for( i = 0; i < 4; i++ )
{
double x = src_corners[i].x, y = src_corners[i].y;
double Z = 1./(h[6]*x + h[7]*y + h[8]);
double X = (h[0]*x + h[1]*y + h[2])*Z;
double Y = (h[3]*x + h[4]*y + h[5])*Z;
dst_corners[i] = cvPoint(cvRound(X), cvRound(Y));
}

return 1;
}

int main(int argc, char** argv)
{
const char* object_filename = argc == 3 ? argv[1] : “box.jpg”;
const char* scene_filename = argc == 3 ? argv[2] : “box_in_scene.jpg”;

cv::initModule_nonfree();

IplImage* object = cvLoadImage( object_filename, CV_LOAD_IMAGE_GRAYSCALE );
IplImage* image = cvLoadImage( scene_filename, CV_LOAD_IMAGE_GRAYSCALE );
if( !object || !image )
{
fprintf( stderr, “Can not load %s and/or %s\n”,
object_filename, scene_filename );
exit(-1);
}

CvMemStorage* storage = cvCreateMemStorage(0);

cvNamedWindow(“Object Correspond”, 1);

static CvScalar colors[] =
{
{{0,0,255}},
{{0,128,255}},
{{0,255,255}},
{{0,255,0}},
{{255,128,0}},
{{255,255,0}},
{{255,0,0}},
{{255,0,255}},
{{255,255,255}}
};

IplImage* object_color = cvCreateImage(cvGetSize(object), 8, 3);
cvCvtColor( object, object_color, CV_GRAY2BGR );

CvSeq* objectKeypoints = 0, *objectDescriptors = 0;
CvSeq* imageKeypoints = 0, *imageDescriptors = 0;
int i;
CvSURFParams params = cvSURFParams(500, 1);

double tt = (double)cvGetTickCount();
cvExtractSURF( object, 0, &objectKeypoints, &objectDescriptors, storage, params );
printf(“Object Descriptors: %d\n”, objectDescriptors->total);

bool computer = true;

do
{
cvExtractSURF( image, 0, &imageKeypoints, &imageDescriptors, storage, params );
printf(“Image Descriptors: %d\n”, imageDescriptors->total);
tt = (double)cvGetTickCount() – tt;

printf( “Extraction time = %gms\n”, tt/(cvGetTickFrequency()*1000.));

CvPoint src_corners[4] = {{0,0}, {object->width,0}, {object->width, object->height}, {0, object->height}};
CvPoint dst_corners[4];

if( locatePlanarObject( objectKeypoints, objectDescriptors, imageKeypoints,
imageDescriptors, src_corners, dst_corners ))
{
for( i = 0; i < 4; i++ )
{
CvPoint r1 = dst_corners[i%4];
CvPoint r2 = dst_corners[(i+1)%4];
cvLine( image, cvPoint(r1.x, r1.y),
cvPoint(r2.x, r2.y ), colors[8] );
}

int width = dst_corners[1].x – dst_corners[0].x;
int height = dst_corners[2].y – dst_corners[0].y;

Rect selection(dst_corners[0], Size(width, height));
Mat roi(image, selection);
bitwise_not(roi, roi);
}
else
{
computer = false;
}

}while(computer);

cvShowImage( “Object Correspond”, image );

cvWaitKey(0);

cvDestroyWindow(“Object”);
cvDestroyWindow(“Object Correspond”);

return 0;
}

windows7恢复被误删的便笺

话说电脑这个东西借给普通用户用是往往会带来很大的麻烦,比如这个系统便笺[biàn jiān],被别人删了几次了。每次都很郁闷,但是因为种种原因也没有想着去恢复。这一次,是因为之前花了很大心血,做了不少的小摘抄,还没来得及整理成文档,结果被人一次性的给删除了。所以google一下看有什么恢复的方法。

其实方法很简单,因为windows 7系统自带有历史版本记录的功能。找到

C:\Users\taobig\AppData\Roaming\Microsoft\Sticky Notes 文件夹

(注:taobig为当前登录用户的用户名)

在文件夹中空白处(或者对这个文件夹)鼠标右键——“属性”——“以前的版本”

稍等片刻,现在系统正在帮你检索以前的版本,检索完后就会给你列出来。如下图

StickyNotes

参考来源:http://piscesfly.itpub.net/post/2874/498375

但是我发现我本机(X64 Windows 7)的Sticky Notes记录的目录与作者说的不一样)

 

附带:

微软给的教材,使用注册表来恢复误删的 Windows 7 便签提示

找到以下注册表子项:

HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Applets\StickyNotes

双击 PROMPT_ON_DELETE 注册表项。 数据框中,键入 00000001,然后单击确定

http://support.microsoft.com/kb/981026/zh-cn

 

 

SkyDrive的bug?!

昨天用SkyDrive传了两本txt的电子书,客户端一直显示在同步,但是一直没有显示成功。软件一直处于下图所示的同步状态,而且也不现实具体同步的进度。

我当时也没在意,以为是网络不稳定(你懂的!)。

但是今天仍然不能同步,于是我登陆网络版,试图利用浏览器在线上传文件。出现了如下的弹窗

这个弹窗终于让我知道是一位文件名中包含了“:”这个特殊字符。但是奇怪的是,这是一个中文的冒号。总所周知,在windows系统中文系统的Explorer中,文件名不能包含的是英文冒号“:”,但是用户是能建议包含中文冒号的文件名的,不知道为什么到了微软禁止上SkyDriver上传包含中文冒号的文件名的文件。最为过份的是客户端没有一点提示。

千呼万唤始出来的IE10 for windows 7

早就有报道微软放出了IE for windows 7的下载页面但是没有下载链接。前天晚上就传微软北京这边有负责人说将在13日放出版本。本以为是说的北京时间,昨天一直留意着,都没有看到发布。今天早上一看,倒是发布了。

http://ie.microsoft.com/testdrive/Info/Downloads/Default.html

这个地址,昨天白天查看时,只有两个链接,晚上时出现了这个链接

IE 10 on Windows 7 Preview
Released November 13th 2012

但是进去后,微软叫我们狠刷F5,泪奔啊。。。

今天进入倒是终于能下载了。支持多国语言,以及以下三个windows版本:

Windows 7 SP1 32-bit
Windows 7 SP1 64-bit
Windows Server 2008 R2 SP1 64-bit

 

安装过程很简单,就是注意一定要是打了SP1的系统,不然会提示“操作系统不受支持”。

 

IE 10外观上和IE9差不多,HTML5的跑分

注:虽然跑分不是唯一评判标准(比如:打死微软它也不会支持webM和ogg),但是通过这个测试还是能够看到清晰的看出浏览器对HMTL5的支持程度(比如:IE9就不支持File API、WebSocket,但是IE 10就支持了)。

权限问题导致windows 7 下CamStudio-Recorder不能保存录像

今天在使用CamStudio-Recorder时,发现一个非常恼火的问题,就是用CamStudio-Recorder录的屏幕视频不能保存,太奇怪了。因为在菜单选项中就没有保存这个选项,什么原因了?

因为我用的是windows 7 的系统,而且UAC是开启状态。所以怀疑是权限问题。以管理员权限运行软件,录像结束时,软件会弹出一个保存对话框,问题解决了。

权限不够,这是一个在windows 7 下很常见的问题。像7—zip不能保存设置,很多软件安装包双击安装时提示无法安装(一般都是没有权限创建临时文件夹,比如CamStudio-Recorder的安装文件就是这种情况,只是今天它的情况太特殊了——设置可以正常保存,但是无法创建视频文件)都是权限不够的问题。

关注 客户端软件更新

先看看我从三个地方截来的图片。本来是看见360软件管家有更新提示后,就跑到qq网站去瞧的,没想到qq的软件列表页面的更新提示没有更改,但是下载页面却悄然变化了。

这也是我一直有考虑的问题:现今的软件更新频率越来越快了,大的软件有稳定的服务器和技术支持,可以讲软件更新集成到软件本身中,但是对于小软件的更新却一直是个大问题。各种客户端的软件虽然是为了解决这个问题而诞生,但是这些软件本身肩负了太大的“使命”,很不纯粹了。所以一种新的方式要出现,像rss类似吧(我的想法),让软件更新不受制与某几家公司……

希望这一天早点到来。

[转载]Windows 8首个官方版本曝光

来源: http://www.sina.com.cn  2011年01月06日 10:37  驱动之家

在微软首席执行官史蒂夫·鲍尔默发表主题演讲之前,微软Windows部门主管Steven Sinofsky先为大家奉上了大餐前的开胃菜,演示了下一代Windows系统在ARM架构上的运行,并且宣布了微软新一代桌面电脑Surface 2.0。

尽管这是Windows 8首次官方露面,不过依然是犹抱琵琶半遮面。由于Windows 8仍处在早期开发阶段,所以Sinofsky并没有就其新功能做更多的阐述,亦或是要等到鲍尔默在晚些时候为大家揭晓。总之,从简单的演示中,我们可以看到,Windows 8的用户界面与Windows 7并没有明显变化

此次演示的Windows 8版本号为Build 6.2.7867,这也证实,Windows 8的内核架构为NT 6.2。相比内核为NT6.1的Windows 7,Windows 8并不会采用全新的架构,至少从内核版本中我们可以推测出这一点。也许Windows 8真的会在用户界面上大做文章?

之前有消息称,Windows 8将为32位和64位版本提供两个独立界面,其主要用户界面代号为“风”(Wind),支持拥有独立显卡的高端笔记本和台式机。这一切也许要等鲍尔默来揭晓了,我们会及时提供最新报道。

当前已知的最新Windows 8为Build 6.2.7904.0.101216-1845, 从其版本号可以看出,它编译于2010年12月16日。据悉,78XX是Windows 8的Milestone 1,79XX版本系列则是Windows 8的Milestone 2。如此说来,微软在CES上展示的是Windows 8 Milestone 1,而当前的开发已进入Milestone 2阶段。