Git Product home page Git Product logo

jwt_auth's Introduction

= 功能简介 =

本模块实现了基于 JWT (JSON Web Token) 的访问认证功能. 
每位合法用户在每次访问某种服务前, 需要先利用自己的 RSA 私钥签名生成一个 JWT, 
然后将该 JWT 放到 HTTP Header 中的 Authorization 域中. 
服务器依据此信息来判定用户是否合法. 为避免 JWT 被冒充以及实现访问次数的限制, 
每次访问用户必须在 JWT 中中间部分包含一个访问次数整数, 每次访问后, 该次数必须加一.
只有该次数与服务器中记录的当前访问次数吻合时, 该访问才被允许. 
为此, 同一个用户只能串行进行用户身份认证 (模块内部会对用户的身份认证行为加线程锁).
在建立用户时, 每位用户会被设置一个最大访问次数限制. 
当访问次数超过该限制时, 该用户也将被禁止访问. 管理员可以定期将所记录的用户访问次数归零.
该 JWT 的主体 JSON 结构 (中间部分) 可描述为
{
    "username" : 用户名,
    "uid" : 用户Id,
    "seq" : 访问次数整数
}

每位用户都有一个唯一的整数用户 Id. 0 号 Id 表示管理员账户, 用于添加删除用户等管理员操作. 
所有整数用户 Id 由代码库内部维护. 管理员可向服务器查询一个新的可用用户 Id, 然后交给用户, 
让用户绑定其公钥后生成一个专用于添加用户的 JSON 文件. 然后由管理员将该文件提交给服务器以完成用户的添加. 
管理员也需要通过 RSA 密钥对和 JWT 来验证身份, 需要在服务器启动前生成并在服务器启动时告知服务器.
但与普通用户不同, 管理员只需要生成一个 JWT, 每次向服务器发送用户管理指令时, 均使用该 JWT 进行认证,
而普通用户由于每次向服务器发送指令都要更新 "seq" 值, 需要每次都签名生成一个新的 JWT.
但为实现简便, 管理员的 JWT 的结构与普通用户是相同的, 
其中的访问次数整数可设为 0 值. 用户名和用户 Id 必须设为 "admin" 和 0.

本软件包包含 5 个部分, 分别为

* package jwt_auth:  一个 go package, 提供用于实现上述功能的函数.
* server: 基于 gin 实现的一个服务器程序, 作为服务器端的示例来提供认证服务.
  这实质上是对上述 jwt_auth 包的一个 RESTFul API 封装.
* generator_user_add: 一个生成添加用户信息的程序, 
  用于向服务器添加一个新用户时帮助用户生成需要交给服务器处理的 JSON 文件. 
* make_pem_keypair.sh: 一个生成 rsa 密钥对 (pem 格式) 的脚本.
* generator: 一个生成每次访问所需的 JWT 的程序. 

测试运行需要依赖工具: curl, ssh-keygen

= 编译方法 =

go build 
cd server ; go build ; cd ..
cd generator ; go build ; cd ..
cd generator_user_add ; go build ; cd ..

= 命令行参数 =

4 个可执行程序的命令行参数分别为
make_pem_keypair.sh user_name
server user_info_path admin_pubkey_path
generator_user_add user_id(int) user_name rsa_pubkey_path
generator user_id(int) comm_seq user_name rsa_key_path

其中, 生成 rsa 密钥对的脚本 make_pem_keypair.sh 位于 generator_user_add/ 目录中. 
脚本会分别生成私钥和公钥文件, 并分别命名为 <user_name>.pem 和 <user_name>_pub.pem.

server 命令用于启动服务器, 其中, user_info_path 为一个管理员目录, 用于保存所有用户信息. 
admin_pubkey_path 为管理员的公钥文件. 
其中, user_info_path 目录中包含
  1) 所有用户的公钥文件, 文件名为 <user_name>_<user_id(%04d)>.pem.
  2) 用户信息文本文件 user_acc.dat: 每行表示一个用户, 包含 4 个字段, 其含义为:
    用户编号  用户名  用户访问次数  用户最大访问次数
在服务器启动前, 服务器必须确保 user_info_path 目录存在, 
并且 user_info_path/user_acc.dat 文件也存在(可以为空), 否则会报错. 
注意 user_info_path 中不包含管理员的信息.
当服务器未运行时, 管理员可以手动修改 user_info_path 目录中的内容,
但务必确保 1), 2) 两者信息的一致性.

generator_user_add 用于生成添加用户时所用到的 JSON 文件. 
其中 user_id 为用户编号, user_name 为用户名称, rsa_pubkey_path 指明用户的公钥文件. 
输出的 JSON 内容直接输出到标准输出.

generator 用于生成用户验证访问权限时需要用到的 JWT, 并将该 JWT 写到一个 http Header 文件中. 
其中, user_id 为用户编号, comm_seq 为该用户访问服务器的当前次数编号, 
该编号必须与服务器中的记录吻合才能通过验证. 
user_name 为用户名. rsa_key_path 为用户的私钥, 用于为生成的 JWT 签名. 
输出的 header 内容直接输出到标准输出.

对于管理员用户, 可通过
generator 0 0 admin admin_key_path > admin.header
生成一个专用于验证管理员身份的 header 文件. 
而普通用户可通过 > user_name.header 生成属于自己的 header 文件.

server 服务器提供了两类 RESTful API 调用, 一类用于管理员管理用户信息, 
另一类用于普通用户执行实质功能命令. 管理用户信息的 API 以 /admin 开头, 
而普通用户的实质功能命令由 /api 开头.

管理员可通过如下命令
curl -X POST/GET -H @admin.header --json <包含命令参数的JSON文件> http://localhost:8080/admin/...
向服务器发送用户管理指令.

而普通用户每次向服务器发送实质指令需要分解为 2 步.

1) 生成 JWT:
generator user_id cmd_seq user_name rsa_key_path > user_name.header
2) 向服务器发送具有实质功能的指令:
curl -X POST/GET -H @user_name.header --json <包含命令参数的JSON文件> http://localhost:8080/api/...

每发送一次, cmd_seq 必须 +1, 否则无法通过认证.

= server API 介绍 = 

用于管理用户信息的 API 包括

* GET: /admin/list_user: (对应 jwt_auth.ListUsers 函数)
  列出所有用户信息(数组). 每条用户信息包括 
  {
      "id": 唯一用户编号,
      "username": 用户名称,
      "acc_time": 用户访问次数,
      "acc_limit": 允许的用户最大访问次数
  }

* GET: /admin/get_new_id:  (对应 jwt_auth.NewUserId 函数)       
  获取一个未被占用的用户 id 编号, 用于添加新用户. 获得的 Json 格式为
  { "id": 新用户编号 }

* POST: /admin/add_user: (对应 jwt_auth.UserAdd 函数)
  添加新用户, 命令参数的 Json 格式为
  {
    "id" : 用户编号
    "username": 用户名称,
    "pubkey": 用户的公钥 (pem 格式)
  }
  该 JSON 文件可通过 generator_user_add 命令生成. 
  但管理员需要先调用 get_new_id 命令获取一个新的用户编号, 然后把该编号告诉用户. 
  用户首先通过 make_pem_keypair.sh 脚本在本地生成密钥对. 
  然后再使用 generator_user_add 命令生成 JSON 文件, 并把生成的 JSON 文件传给管理员. 
  最后管理员再调用 add_user 命令并传入该 JSON 文件来完成用户的添加.

* POST: /admin/remove_user: (对应 jwt_auth.UserRemove 函数)
  删除一个用户, 命令参数 Json 格式为
  { "id" : 用户编号 }

* POST: /admin/clear_access_time (对应 jwt_auth.ClearAccessTime 函数)
  将所有用户的访问次数归零, 无需提供 JSON 参数文件.

* POST: /admin/save_and_quit (对应 jwt_auth.SaveUserInfo 函数)
  保存所有用户信息并退出服务器, 无需提供 JSON 参数文件. 
  下次服务器启动时, 只要指定正确的 user_info_path, 即可恢复所有用户的信息. 

* GET: /api/test 
  用于作为示例表示普通用户的实质功能调用接口. 
  用户在进入该功能前, 必须以 gin middlewire 的方式通过访问权限认证, 
  服务器在进行用户认证时会调用 jwt_auth.UserAccess 函数.

另外, 管理员在调用上述所有以 /admin 开头的功能接口时, 也需要通过权限认证, 
认证时可通过 jwt_auth.ValidateJWT 函数验证管理员的身份, 
具体流程可参见 server/main.go 代码中的 auth_admin_mw 函数.
需要注意管理员的公钥在初始化服务器时被保存到了 jwt_auth.Users[0].PubKey. 

= jwt_auth 中的其他函数 =

package jwt_auth 中还包含一些上述未提到的函数, 现介绍如下.

func LoadRsaPublicKey (key_path string) *rsa.PublicKey 
用于根据 pem 格式公钥文件生成内部的公钥结构.

func LoadRsaPrivateKey (key_path string) *rsa.PrivateKey 
用于根据 pem 格式私钥文件生成内部的私钥结构.

func LoadUserInfo (user_dir string, admin_pubkey_path string)
用于初始化服务器, 包括从用户信息目录中读取所有用户信息.

func GenerateJWT (id int, seq int, uname string, rsa_key_path string) (token_str string)
用于生成一个 JWT, 其中 seq 为 JWT 中包含的当前用户访问次数.

func GetInfoFromToken (token_str string) (id int, seq int, uname string) 
用于从 JWT 中提取用户 id, 用户访问次数和用户名信息. 
注意该函数仅提取 JWT 中的信息, 并不验证 JWT 签名的有效性. 而函数
func ValidateJWT (token_str string, rsa_pub_key *rsa.PublicKey) (id int, seq int, uname string) {
则在获取信息的同时利用 rsa_pub_key 验证 JWT 签名的有效性, 如果签名无效, 则返回 id = -1.

const MAX_ID 
为用户编号的上限

jwt_auth's People

Contributors

tfcolin avatar

Stargazers

 avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.