ATHER Shu(A.S)的Blog http://www.asarea.cn ATHER Shu的blog,新的开始! zh-CN Copyright 2013-2014, ASAREA.CN shushenghong@gmail.com 2026年5月05日 17:38:10 +0800 2026年5月06日 04:57:09 +0800 <![cdata[claude code / codex的一些配置]]>
全局的mcp配置:
~/.claude.json
项目级别的mcp配置:
.mcp.json
可参考claudecode mcp

全局的skills配置:
~/.claude/skills
项目级别的skills配置:
project/.claude/skills
可参考探索 .claude 目录

codex:

全局的mcp配置:
~/.codex/config.toml
项目级别的mcp配置:
project/.codex/config.toml
可参考mcp

全局的skills配置:
~/.agents/skills
项目级别的skills配置:
project/.agents/skills
可参考where-to-save-skills]]>
http://www.asarea.cn/diary/321http://www.asarea.cn/diary/321http://www.asarea.cn/diary/321#commentsshushenghong@gmail.com(A.S)2026年5月05日 17:38:10 +0800
<![cdata[2026.5.5]]>下午打开博客,发现登录不了,引用的jquery cdn失效了,更换后终于能访问。
发现还是http,想用acme换成https,本想纯自动ai处理,忍住了用手动敲命令,结果发现还是要问ai才成功,这么看古法技艺确有失传风险:)
后面多记录一些AI时代的感悟吧]]>
http://www.asarea.cn/diary/320http://www.asarea.cn/diary/320http://www.asarea.cn/diary/320#commentsshushenghong@gmail.com(A.S)2026年5月05日 17:27:57 +0800
<![cdata[js的锁以及异步调用相关]]>思考下面的两个场景
注:以下示例代码均用typescript实现

let i = 0;
async function handler() {
i++;
console.log(new Date().getTime()/1000, '处理程序开始', i);
await new Promise(resolve => setTimeout(resolve, 2000)); // 模拟异步操作
console.log(new Date().getTime()/1000, '处理程序完成', i);
};

场景一:直接调用handler两次
function call1() {
handler();
handler();
}
call1();

打印如下:
1732934781.962 处理程序开始 1
1732934781.966 处理程序开始 2
1732934783.969 处理程序完成 2
1732934783.97 处理程序完成 2
可以看出,两次handler都同时先后进入(由于js单线程,i会变化),并同时卡在await等待2s。

场景二:await调用handler两次
async function call2() {
await handler();
await handler();
}
call2();

打印如下:
1732934929.563 处理程序开始 1
1732934931.569 处理程序完成 1
1732934931.57 处理程序开始 2
1732934933.571 处理程序完成 2

可以看出,下一次的handler会等上一次执行完毕再进行。

由此可以看出,js如果直接调用函数2次,函数中开头非异步的部分会按调用顺序执行,并同时卡在函数中异步的部分,异步结束后再执行剩余部分。

这很可能造成数据不同步等各种问题。对于要求不管是直接调用还是await调用多次,都要完全保证顺序的地方,需要引入锁机制。

核心就是,函数在进入时立即锁住,即便是直接调用多次,也会确定等待锁释放。
而根据前面的试验结论,可以用一个简单的异步promise来实现这个锁。
let lastlock = Promise.resolve();
async function getlock() {
let unlock = () => {};
let lock = lastlock;
let nlock = new Promise<void>((resolve) => {unlock = resolve;});
lastlock = nlock;
await lock;
return unlock;
}

上面的getlock函数,核心要点
1. lastlock一开始是一个已经resolve的promise,所以await它不会阻塞
2. 每次获取锁,js单线程执行会更改lastlock为一个新的promise,而这个promise的resolve函数会被传出来,也就成了一个unlock函数,这个unlock执行会resolve promise,完成异步。

改造handler函数,用锁
async function handler() {
const unlock = await getlock();
try {
i++;
console.log(new Date().getTime()/1000, '处理程序开始', i);
await new Promise(resolve => setTimeout(resolve, 2000)); // 模拟异步操作
console.log(new Date().getTime()/1000, '处理程序完成', i);
} finally {
unlock();
}
};

连续直接调用handler两次(即call1())
1732935948.492 处理程序开始 1
1732935950.497 处理程序完成 1
1732935950.497 处理程序开始 2
1732935952.499 处理程序完成 2

连续await调用handler两次(即call2()),打印如下
1732936175.165 处理程序开始 1
1732936177.171 处理程序完成 1
1732936177.171 处理程序开始 2
1732936179.173 处理程序完成 2
]]>
http://www.asarea.cn/diary/319http://www.asarea.cn/diary/319http://www.asarea.cn/diary/319#commentsshushenghong@gmail.com(A.S)2024年11月30日 11:18:18 +0800
<![cdata[golang学习之函数/方法/接口]]>
一个interface(接口)类型,由Type和Value组成,当Type和Value都是nil时,接口值才为空
1.如果形参是接口类型,传递任意具体类型变量,会将type设置为变量的具体类型,value设置为变量具体的值(变量需实现了此接口)
2.接口调用方法时,会动态解出Value值进行对应的方法调用
3.指向接口类型的指针没有任何意义
Type 类型
Value 值
]]>
http://www.asarea.cn/diary/318http://www.asarea.cn/diary/318http://www.asarea.cn/diary/318#commentsshushenghong@gmail.com(A.S)2022年1月06日 17:52:24 +0800
<![cdata[golang学习之零值]]>
类型 零值
string ""
int 0
float 0
bool false
数组(固定长度) 所有成员的零值
struct 所有成员的零值
slice nil
map nil
指针 nil
函数 nil
通道 nil
接口interface Type Value均为nil

package main

import "fmt"

func main() {
type Man struct {
name int
}
type Human interface {
GetName() string
}
var (
a string
b int
c float32
d bool
e [3]int
f Man
g []int
h map[string]string
i *int
j func() int
k chan int
l Human
)
fmt.Printf("a=%#v b=%#v c=%#v d=%#v e=%#v f=%#v g=%#v h=%#v i=%#v j=%#v k=%#v l=%#v\n", a, b, c, d, e, f, g, h, i, j, k, l)

}

// a="" b=0 c=0 d=false e=[3]int{0, 0, 0} f=main.Man{name:0} g=[]int(nil) h=map[string]string(nil) i=(*int)(nil) j=(func() int)(nil) k=(chan int)(nil) l=<nil>

]]>
http://www.asarea.cn/diary/317http://www.asarea.cn/diary/317http://www.asarea.cn/diary/317#commentsshushenghong@gmail.com(A.S)2022年1月06日 16:40:10 +0800
<![cdata[hello, 2018]]>http://www.asarea.cn/diary/316http://www.asarea.cn/diary/316http://www.asarea.cn/diary/316#commentsshushenghong@gmail.com(A.S)2022年1月06日 16:39:02 +0800<![cdata[字体类型名词解释]]>sans-serif: 无衬线字体,如黑体,雅黑
monospace:等宽字体




参考:
无衬线字体
等宽字体]]>
http://www.asarea.cn/diary/315http://www.asarea.cn/diary/315http://www.asarea.cn/diary/315#commentsshushenghong@gmail.com(A.S)2015年1月18日 11:30:22 +0800
<![cdata[获取mysql表注释以及列注释]]>
use xx_database;
show table status;
最后一列就是
+-----------------+--------+---------+------------+------+----------------+-------------+-----------------+--------------+-----------+----------------+---------------------+-------------+------------+-----------------+----------+----------------+---------+
| Name | Engine | Version | Row_format | Rows | Avg_row_length | Data_length | Max_data_length | Index_length | Data_free | Auto_increment | Create_time | Update_time | Check_time | Collation | Checksum | Create_options | Comment |
+-----------------+--------+---------+------------+------+----------------+-------------+-----------------+--------------+-----------+----------------+---------------------+-------------+------------+-----------------+----------+----------------+---------+
| game | InnoDB | 10 | Compact | 0 | 0 | 16384 | 0 | 0 | 7340032 | 1 | 2014年11月13日 15:18:13 | NULL | NULL | utf8_general_ci | NULL | | |
| goods | InnoDB | 10 | Compact | 0 | 0 | 16384 | 0 | 0 | 7340032 | 1 | 2014年11月13日 15:20:18 | NULL | NULL | utf8_general_ci | NULL | | |

获取列注释:
show full fields from xxx_table;
最后一列就是
+------------+------------------+-----------------+------+-----+---------+----------------+---------------------------------+---------+
| Field | Type | Collation | Null | Key | Default | Extra | Privileges | Comment |
+------------+------------------+-----------------+------+-----+---------+----------------+---------------------------------+---------+
| id | int(10) unsigned | NULL | NO | PRI | NULL | auto_increment | select,insert,update,references | |
| name | varchar(200) | utf8_general_ci | NO | | NULL | | select,insert,update,references | |
| url | varchar(1000) | utf8_general_ci | NO | | NULL | | select,insert,update,references | |
| created_at | int(10) unsigned | NULL | NO | | NULL | | select,insert,update,references | |
| updated_at | int(10) unsigned | NULL | NO | | NULL | | select,insert,update,references | |
+------------+------------------+-----------------+------+-----+---------+----------------+---------------------------------+---------+
]]>
http://www.asarea.cn/diary/314http://www.asarea.cn/diary/314http://www.asarea.cn/diary/314#commentsshushenghong@gmail.com(A.S)2014年11月13日 15:56:32 +0800
<![cdata[php连接ms sql数据库的一些问题]]>
1、安装,需要安装freetds
对于Debian(ubuntu这种)
apt-get install php5-sybase

2、字符集问题
mssql query如果返回
"Unicode data in a Unicode-only collation or ntext data cannot be sent to clients using DB-Library (such as ISQL) or ODBC version 3.7 or earlier."

编辑/etc/freetds/freetds.conf,添加如下两行:
[global]
;tds version = 4.2
tds version = 8.0
client charset = UTF-8
也可以在php.ini或者mssql独立module的ini(譬如ubuntu上php各个子module都有自己的独立配置文件)中指定charset
; Specify client character set..
; If empty or not set the client charset from freetds.comf is used
; This is only used when compiled with FreeTDS
mssql.charset = "UTF-8"


3、date format问题
默认mssql返回的时间类型是:mon dd yyyy hh:miAM (or PM)
这种格式插入不到mysql中(Incorrect date value),在php中也不支持,如new DateTime()。

解决方法一种是用mssql的convert语法,
另一种是设置php.ini或者mssql module的独立ini,加入
mssql.datetimeconvert = Off

参考:
mssql-query
mssql cast&convert]]>
http://www.asarea.cn/diary/313http://www.asarea.cn/diary/313http://www.asarea.cn/diary/313#commentsshushenghong@gmail.com(A.S)2014年9月15日 20:32:14 +0800
<![cdata[virtualbox虚拟网络:nat&bridge桥接网络]]>1、NAT
虚拟机virualmachine(下简称vm)对外界不可见,vm通过host主机访问外网。可以设置端口映射,将host主机的某些端口映射到VM的端口,这样外界访问host主机的端口会自动转发到VM的对应端口。


这也是最方便简洁的方式。

2、bridge桥接
(这里演示下host是win7,vm是ubuntu的情况。)
vm与host平起平坐,外界看起来就像是两台机器,vm有自己独立的ip。
vm通过主机的某网卡虚拟出一块网卡桥接到internet。
2.1 选择主机网卡,设置bridge

2.2 启动后会发现多了一块网卡

2.2 修改interface文件(这个文件相当于windows里的设置网络属性如ip、网关、子网掩码等,dhcp就是自动获得ip)
vim /etc/network/interfaces

2.3 启动网卡

2.4 查看ip


这种方式能模拟出一台真是机器来。

PS:NAT和bridge模式可以并存。]]>
http://www.asarea.cn/diary/312http://www.asarea.cn/diary/312http://www.asarea.cn/diary/312#commentsshushenghong@gmail.com(A.S)2014年8月25日 22:58:09 +0800
<![cdata[php图片加水印]]>其实很简单,用的无非是imagecopymerge或者imagecopy方法。
imagecopymerge可以设置水印透明度(可以总体设置水印图片透明度,但是水印图片自身不能带有alpha)。
imagecopy直接把水印覆盖到原图上(水印图片支持自身带有alpha,但不能总体设置水印图片透明度)。

如果想要水印图片带有alpha如png,同时又要支持设置整体水印图片透明度,则需要hack。
/**
* PNG ALPHA CHANNEL SUPPORT for imagecopymerge();
* This is a function like imagecopymerge but it handle alpha channel well!!!
**/
function imagecopymerge_alpha($dst_im, $src_im, $dst_x, $dst_y, $src_x, $src_y, $src_w, $src_h, $pct){
$opacity=$pct;
// getting the watermark width
$w = imagesx($src_im);
// getting the watermark height
$h = imagesy($src_im);

// creating a cut resource
$cut = imagecreatetruecolor($src_w, $src_h);
// copying that section of the background to the cut
imagecopy($cut, $dst_im, 0, 0, $dst_x, $dst_y, $src_w, $src_h);
// inverting the opacity
$opacity = $opacity;

// placing the watermark now
imagecopy($cut, $src_im, 0, 0, $src_x, $src_y, $src_w, $src_h);
imagecopymerge($dst_im, $cut, $dst_x, $dst_y, $src_x, $src_y, $src_w, $src_h, $opacity);

参考资料:Alpha support for PHP's imagecopymerge function

演示:
]]>
http://www.asarea.cn/diary/311http://www.asarea.cn/diary/311http://www.asarea.cn/diary/311#commentsshushenghong@gmail.com(A.S)2014年8月15日 22:58:50 +0800
<![cdata[windows查看端口占用情况]]>
netstat -ano | findstr "8083"
TCP 0.0.0.0:8083 0.0.0.0:0 LISTENING 5288

tasklist | findstr "5288"
VBoxHeadless.exe 5288 Console 1 20,432 K
]]>
http://www.asarea.cn/diary/310http://www.asarea.cn/diary/310http://www.asarea.cn/diary/310#commentsshushenghong@gmail.com(A.S)2014年7月31日 21:19:30 +0800
<![cdata[android安卓activity生命周期]]>

代码如下:
package cn.asarea.app.testandroid;

import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;

public class MainActivity extends Activity {
private static final String TAG = "test andriod";

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

Log.e(TAG, Thread.currentThread().getStackTrace()[2].getMethodName());
}

@Override
protected void onStart() {
// TODO Auto-generated method stub
super.onStart();

// StackTraceElement stackTraceElements[] = Thread.currentThread().getStackTrace();
// for (StackTraceElement stackTraceElement : stackTraceElements) {
// Log.e(TAG, stackTraceElement.getClassName() + ":" + stackTraceElement.getMethodName());
// }

Log.e(TAG, Thread.currentThread().getStackTrace()[2].getMethodName());
}

@Override
protected void onRestart() {
// TODO Auto-generated method stub
super.onRestart();

Log.e(TAG, Thread.currentThread().getStackTrace()[2].getMethodName());
}

@Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();

Log.e(TAG, Thread.currentThread().getStackTrace()[2].getMethodName());
}

@Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();

Log.e(TAG, Thread.currentThread().getStackTrace()[2].getMethodName());
}

@Override
protected void onStop() {
// TODO Auto-generated method stub
super.onStop();

Log.e(TAG, Thread.currentThread().getStackTrace()[2].getMethodName());
}

@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();

Log.e(TAG, Thread.currentThread().getStackTrace()[2].getMethodName());
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}

}
]]>
http://www.asarea.cn/diary/309http://www.asarea.cn/diary/309http://www.asarea.cn/diary/309#commentsshushenghong@gmail.com(A.S)2014年7月12日 10:31:47 +0800
<![cdata[/etc/shadow中特殊密码含义]]>!! means user account has not been initialed or has not been locked.
! means group password is not available.
* means login disabled.

参考:
Understanding /etc/shadow file
Shadow - 密码文件]]>
http://www.asarea.cn/diary/308http://www.asarea.cn/diary/308http://www.asarea.cn/diary/308#commentsshushenghong@gmail.com(A.S)2014年7月04日 13:43:57 +0800
<![cdata[git初探]]>
以下是自己根据查阅到的资料整理记录,
1、git是分布式的,但是并不是说没有中心服务器的概念,只不过任何一台clone了代码的电脑都可以成为中心服务器。譬如A、B都从C服务器clone了一份git代码,C坏了,A、B都可以恢复成为中心服务器。

2、git的分层
用惯了svn的人脑海里只有本地workspace和远程库两层概念,本地workspace开发完了就往远程库commit。简单粗暴。
对于git,则要加入一层,本地库。本地workspace往本地库commit,本地库再push到远程库。(你可以这样想,本地库是远程库的完整拷贝,所以才可以分布式,才可以离线提交)
我画的一张草图^_^(去年第一次用git时整的)

中心库目录

本地库是中心库的完整拷贝(所谓clone,clone的就是.git目录)



3、安装git,
对于ubuntu,
apt-get install git
mac,mac os预安装了git
windows,安装msysgit

4、初始化一个中心库
root登录到服务器,
#cd到你准备存放库的目录
git init --bare myproject.git
#这就会新建一个目录叫myproject.git,并且初始化了一个git库
#bare的意思就是不需要workspace,只要.git中的git信息内容

5、决定中心库git的访问方式
就是别人怎么check中心库的代码,方式很多,有ssh、git、http等方式,
可以参考服务器上的git
这里简单说下ssh方式。说白了就是用ssh(用过putty吧,输入用户名密码就登陆到远程机器了)登陆某个特定账户,
譬如新建一个账号git,ssh登陆该账号访问git中心库所在的目录。
5.1 创建git用户
adduser git
#将创建的中心库改为git用户所有
chown -R git:git myproject.git
#禁止用putty等用git账号登陆
vim /etc/passwd
#将git:x:1001:1001:,,,:/home/git:/bin/bash改为
git:x:1001:1001:,,,:/home/git:/usr/bin/git-shell

5.2 ssh登陆的两种方式,
一种是用户名密码,总不能告诉所有要check代码的人git账户的密码吧,略搓。
一种就是用授权key,让要check代码的人生成一个RSA密钥,将公钥传到中心服务器的authorized_keys中。
具体这些网上有很多说明,请参考:
服务器上的 Git - 生成 SSH 公钥

关于ssh登陆,请参考ssh原理

到了这里,别人就可以通过如:
ssh://git@xxx.net:222/mygitposition/myproject.git
的路径clone到代码了

6、进阶管理git的访问
人肉往authorized_keys加key的方式对于大一点的团队是个噩梦,这就要借助其他开源项目:
Gitosis、Gitolite(推荐,因为前者已经很久不更新了),用他们来帮助你管理ssh公钥。

7、初始化本地库
对于本地库,用惯svn的人都想用乌龟,好像git也有人搞了个乌龟,这里不介绍,介绍两种方式
一种用eclipse的插件egit(图形化操作哦,不比乌龟差)。
一种用命令行(略潮)。

7.1 clone远程库
如果远程库已经有详细的工程内容,可以直接clone下来玩。
对于egit,直接import projects from git,填入中心库的项目访问地址,如
ssh://git@xxx.net:222/mygitposition/myproject.git
对于命令行
#cd到你的workspace目录
git clone ssh://git@xxx.net:222/mygitposition/myproject.git
#工程会clone到myproject目录中

7.2 用本地库充实中心库
说白了也就是初次提交拉,你可以在本地建一个git库,从svn项目转换代码,或者人工加入一些初始文件,提交到本地git库,再统一push到远程中心库。

7.2.1 对于svn项目转换为git项目
#author.txt是将svn的账号转化为git账号
#内容如:ather = Ather Shu <shushenghong@gmail.com>
git svn clone http://localhost/svn/repository/myproject --authors-file=author.txt --no-metadata
cd myproject
#将svn ignore转化为git ignore
#可以导出到exclude
git svn show-ignore > .git/info/exclude
#或者生成.gitignore文件
git svn create-ignore
这样一个本地的git库就初始化好了,详细的svn转git的资料,请参考svn转git
下面就是把本地库push到远程中心库了
git remote add origin ssh://git@xxx.net:222/mygitposition/myproject.git
git push origin master --tags

7.2.2 用egit提交
在eclipse工程里新建文件或者做修改,然后commit(这只是commit到了本地库),再push即可。
egit使用起来比较简单,这里不多介绍,详细可以参考egit用户手册

参考资料汇总:
服务器上的git
搭建Git服务器
服务器上的 Git - 生成 SSH 公钥
ssh原理
Gitolite
svn转git
egit用户手册]]>
http://www.asarea.cn/diary/307http://www.asarea.cn/diary/307http://www.asarea.cn/diary/307#commentsshushenghong@gmail.com(A.S)2014年6月25日 18:47:00 +0800
<![cdata[email电子邮件概略]]>
1.email这个东东比http诞生的早的多,是老古董了。
据说70年代初世界上的第一封电子邮件发出。1987年9月14日中国第一封电子邮件是由"德国互联网之父"维纳·措恩与王运丰在北京的计算机应用技术研究所发往德国卡尔斯鲁厄大学的,"Across the Great Wall we can reach every corner in the world. "。

2.email中的几个概念
mailbox:可以想象成世界中真实的一个邮箱,每一封邮件最终都会存放在这里
MUA:mail user agent,你可以把它想象成本地邮件编辑器、阅读器,譬如outlook编辑器将你输入的各种信息打包成一个email,收到邮件后又可以通过MUA来阅读。
MTA:mail transport agent,这哥们就是个邮差:作为发送方,将MUA打包的邮件传送给下一站;作为接收方,将收到的邮件接收下来并RELAY或者扔给LDA。这个传送过程用的是smtp协议。
sendMail是最早的实现smtp传输的smtp服务器程序(有一个接收MTA和一个发送MTA)
smtp:Simple Mail Transfer Protocol,网络中传输email的协议,一般发送方MTA和接收方MTA会有一个握手过程。
RELAY:转发,当接收方MTA收到邮件后,发现目的邮箱不是本服务器的,会向目的邮箱服务器的MTA转发。
LDA:local delivery agent,接收方MTA收到邮件后,发现目的邮箱就是本服务器的,会调用LDA发送到一个具体的本机mailbox。
MX解析:DNS解析,将email的域名服务器解析到对应ip的具体服务器。
当有人发邮件给"vincen@example.com"时,系统将对"example.com"进行DNS中的MX记录解析。如果MX记录存在,系统就根据MX记录的优先级,将邮件转发到与该MX相应的邮件服务器上。

随着科技的发展,电脑越来越多,大部分电脑都通过ISP上网,并不是所有的电脑都有自己独立的域名,固定的ip。
于是出现了远程mailbox,譬如163邮箱,qq邮箱等,这些mail服务商会提供给你一个邮件地址,将你的邮件存放在他们的服务器上。
但是,我们又想在自己的电脑上阅读邮件怎么办?
于是,POP/IMAP出现了,可以从远程mail服务器上pull邮件到本机。
pop3:Post Office Protoco,POP3是把邮件从远程电子邮箱中传输到本地计算机的协议。
imap:Internet Message Access Protocol,比pop3更强大,是其替代者,本地邮件客户端做的操作(已读、移动、删除等)能同步到远程服务器。

配置过本地邮件客户端如outlook等的都会知道要配置pop3/imap,就是为了从远程邮件服务器拉email到本地来。
这里又有个疑问,既然pop3/imap都是取邮件的协议,那不是outlook这种都只能看邮件了?但是现实情况是我们也能发邮件啊。
这就是上面说道的RELAY功能了,本地邮件客户端outlook譬如使用163邮箱发送邮件时,也有一个MTA(只不过这个MTA只是发送,不能接收),跟163的MTA通讯,一般这个通讯会加入额外的用户名、密码验证(授权RELAY),再通过163的MTA RELAY邮件到真正的收件人邮件服务器MTA。

参考资料:
How Electronic Mail Works
簡易 Mail Server 架設 -- Sendmail 設定
简单邮件传输协议
电子邮件
什么是POP3、SMTP和IMAP?]]>
http://www.asarea.cn/diary/306http://www.asarea.cn/diary/306http://www.asarea.cn/diary/306#commentsshushenghong@gmail.com(A.S)2014年6月11日 04:27:35 +0800
<![cdata[oauth初探]]>
最近在项目中遇到facebook、twitter、fitbit的集成,又用到了OAuth,却有了很大的困惑,究其原因是twitter、fitbit用的是OAuth1,而facebook用的是2.0,相比较而言,2.0的使用会简单很多。这里简单的做一点自己理解的记录。

1、OAuth 1.0(1.0a)

1.1 用户访问网站某个url,这个url可能是用于读取第三方如facebook等的资源或者是绑定第三方账号。
1.2 网站检测本地数据库该用户是否有授权绑定过第三方账号(有就直接1.6)。
1.3 网站向第三方请求一个临时令牌(request token),请求时需要开发者申请的app id和app secret。第三方认证后,返回一个临时令牌。
1.4 网站收到临时令牌跳转到第三方的授权页面(需要传递临时令牌以及回调url),用户会在这个页面输入用户名密码登录并决定是否授权。(authorize)
1.5 授权成功后第三方会跳转到本地网站的回调url(会以get方式把临时令牌、授权信息oauth_verifier),此时网站就可以根据临时令牌以及返回的授权信息调用第三方接口转换为最终的访问令牌(access token)
1.6 此后就可以访问api了,当然需要在http调用api的头部加入access token以及token secret等等很多很多参数(很恶心)


参考:
OAuth维基百科
twitter oauth流程
fitbit流程

2、OAuth2
OAuth2.0的最大改变就是不需要临时token了,直接authorize生成授权code,用code就可以换取access token了,同时access token加入过期,刷新机制,为了安全,要求第三方的授权接口必须是https的。

2.1 用户访问网站某个url,这个url可能是用于读取第三方如facebook等的资源或者是绑定第三方账号。
2.2 网站检测本地数据库该用户是否有授权绑定过第三方账号(有就直接2.5)。
2.3 网站向第三方申请授权(authorize),请求时需要开发者申请的app id以及回调url,用户会在这个页面输入用户名密码登录并决定是否授权。(authorize)
2.4 授权成功后第三方会跳转到本地网站的回调url(会以get方式返回授权信息Authorization Code),网站根据code以及app id、appi secret换取最终的访问令牌(access token)
2.5 此后就可以访问api了,只需在http调用的url里以get方式传递access_token参数即可


参考:
The OAuth 2.0 Authorization Framework
facebook OAuth
QQ OAuth
baidu OAuth
微博 OAuth

其他一些的作者的日志:
Oauth1.0与oauth2.0的区别
OAuth的改变
新浪微博OAuth2.0 VS OAuth1.0 主要区别]]>
http://www.asarea.cn/diary/304http://www.asarea.cn/diary/304http://www.asarea.cn/diary/304#commentsshushenghong@gmail.com(A.S)2014年4月28日 04:05:57 +0800
<![cdata[解决vim小键盘不好使]]>

putty设置永不断线


virtualbox不显示虚拟机窗口启动,按住shift再点启动按钮。
As of VirtualBox 4.2, you can start a headless machine from the GUI by holding shift while you click the "Start" button.]]>
http://www.asarea.cn/diary/303http://www.asarea.cn/diary/303http://www.asarea.cn/diary/303#commentsshushenghong@gmail.com(A.S)2014年4月28日 08:50:06 +0800
<![cdata[eclipse里svn账号修改]]>]]>http://www.asarea.cn/diary/302http://www.asarea.cn/diary/302http://www.asarea.cn/diary/302#commentsshushenghong@gmail.com(A.S)2014年4月17日 16:12:24 +0800<![cdata[插卡无文件]]>]]>http://www.asarea.cn/diary/301http://www.asarea.cn/diary/301http://www.asarea.cn/diary/301#commentsshushenghong@gmail.com(A.S)2014年4月14日 10:42:13 +0800

AltStyle によって変換されたページ (->オリジナル) /