【Golang】Golang + jwt 实现简易用户认证

待兔 等级 665 0 0

本文已同步发布到我的个人博客:https://glorin.xyz/2019/11/23/Golang-jwt-simple-auth/

前言

在开发app的时候,难免会需要有后台API,Golang是一门非常适合开发后台服务的高性能的语言。在使用Golang开发后台API的时候,经常需要有用户注册、登录的功能,例如为了保存用户数据、为了给不同用户提供不同服务等。本文便是介绍一种基于jwt的Golang的用户认证系统。

目标

我们的模板是实现三个API接口:

  • /api/account: 支持Post方法,用来注册一个用户
  • /api/account/accesstoken: 登录功能
  • /api/account/me: 获取用户信息,具有认证功能,如果没有用户认证信息(token),则获取失败,否则返回当前用户的信息。

工具原料

  • Golang + Goland IDE(非必须,VSCode等等也可以)
  • jwt-go:一个go语言的jwt实现
  • github.com/gorilla/mux: go语言的一个路由组件,用来提供http路由服务

实现步骤

搭建http服务,提供API

  1. 首先我们在自己的go语言目录下建立项目:golang-jwt-simple-auth(名字可自由决定),golang项目一般遵循golang的约定,放在GOPATH(默认是用户目录下的go文件夹)下面,比如我的项目放在github上,那目录就是
~/go/src/github.com/glorinli/golang-jwt-simple-auth
  1. 新建main.go问,作为程序的主入口,main.go内容如下:
package main

import ( "fmt" "github.com/glorinli/go-jwt-simple-auth/app" "github.com/glorinli/go-jwt-simple-auth/controllers" "log" "net/http" "os"

"github.com/gorilla/mux"

)

func init() { log.SetPrefix("simple-auth") }

func main() { // 新建路由器 router := mux.NewRouter()

// 注册jwt认证的中间件
router.Use(app.JwtAuthentication)

// 注册路由
router.HandleFunc("/api/account", controllers.CreateUser).Methods(http.MethodPost)
router.HandleFunc("/api/account/accesstoken", controllers.Login).Methods(http.MethodGet)
router.HandleFunc("/api/account/me", controllers.Me).Methods(http.MethodGet)

// 获取端口号
port := os.Getenv("golang-jwt-simple-auth-port")
if port == "" {
    port = "8001"
}

fmt.Println("Port is:", port)

// 开始服务
err := http.ListenAndServe(":"+port, router)
if err != nil {
    fmt.Print("Fail to start server", err)
}

}

代码的作用在注释中已经说明了,关于mux库的使用,可以参考 http://www.gorillatoolkit.org/pkg/mux, 这里我们只要知道它起到一个路由器的作用,负责把一个api请求映射到一个方法上。

关键就在于

router.Use(app.JwtAuthentication)

这相当与注册了一个中间件,也可以理解为拦截器,就是说所有的请求都会先经过这个中间件拦截处理,于是我们便可以在里面处理认证相关 的逻辑了,接下来就来说说这个JwtAuthentication。

JWT认证实现

首先还是贴上JwtAuthentication的代码:

package app

import ( ... 省略 )

var JwtAuthentication = func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

    // 只针对这个me接口开启认证
    needAuthPaths := []string{"/api/account/me"}
    requestPath := r.URL.Path

    var needAuth = false
    // 判断是否是需要认证的api,省略

    // 不需要认证,直接走下一步
    if !needAuth {
        next.ServeHTTP(w, r)
        return
    }

    // 从Header读取Token
    tokenHeader := r.Header.Get("Authorization")

    // Token is missing
    if tokenHeader == "" {
        sendInvalidTokenResponse(w, "Missing auth token")
        return
    }

    tk := &models.Token{}

    token, err := jwt.ParseWithClaims(tokenHeader, tk, func(token *jwt.Token) (interface{}, error) {
        return []byte(os.Getenv("token_password")), nil
    })

    fmt.Println("Parse token error:", err)

    if err != nil {
        sendInvalidTokenResponse(w, "Invalid auth token: "+err.Error())
        return
    }

    // Token is invalid
    if !token.Valid {
        sendInvalidTokenResponse(w, "Token is not valid")
        return
    }

    // Auth ok
    fmt.Println("User:", tk.UserId)
    ctx := context.WithValue(r.Context(), "user", tk.UserId)
    r = r.WithContext(ctx)
    next.ServeHTTP(w, r)
})

}

func sendInvalidTokenResponse(w http.ResponseWriter, message string) { response := u.Message(false, message) w.WriteHeader(http.StatusForbidden) w.Header().Set("Content-Type", "application/json") u.Respond(w, response) }

这个函数的作用就是解析客户端传递过来的token,将其解析为一个Token对象,Token对象的格式如下:

/*
JWT claims struct
*/
type Token struct {
    UserId uint
    jwt.StandardClaims
}

可以看到,除了jwt标准的数据,我们还添加了一个UserId字段,这是为了方便从Token确定用户的id。从这里我们也可以看出,Jwt Token是可以包含额外信息的。

关于这个Token是如何生程的,我们下面分析。

注册功能

返回去看main.go,我们发现注册接口被绑定到一个函数上:

router.HandleFunc("/api/account", controllers.CreateUser).Methods(http.MethodPost)

来看这个CreateUser函数,位于authController.go中:

var CreateUser = func(w http.ResponseWriter, r *http.Request) {
    account := &models.Account{}

err := json.NewDecoder(r.Body).Decode(account)

if err != nil {
    utils.Respond(w, utils.Message(false, "Invalid info: "+err.Error()))
    return
}

utils.Respond(w, account.Create())

}

最终的实现是在account.Create()函数,位于account.go中:

func (account *Account) Create() map[string]interface{} {
    // 校验 省略

// 将密码做一个加密
hashedPassword, _ := bcrypt.GenerateFromPassword([]byte(account.Password), bcrypt.DefaultCost)
account.Password = string(hashedPassword)

// 创建数据库数据
err := GetDB().Create(account).Error

// ...

// 创建Token
tk := &Token{UserId: account.ID}
token := jwt.NewWithClaims(jwt.GetSigningMethod("HS256"), tk)
tokenString, _ := token.SignedString([]byte(os.Getenv("token_password")))
account.Token = tokenString

account.Password = ""
response := u.MessageWithData(true, "Account has been created", account)
return response

}

关键就在于创建Token这一步,我们调用jwt.NewWithClaims来生成Token,参数有两个,第一个是签名方法,采用HS256,第二个就是一个Token对象,这个对象即将被编码到Token中,这也是我们在进行认证的时候,从Token解析出来的对象。

登录功能

登录功能与注册功能类似,只是把创建数据改为校验用户名密码。

运行部署

我们可以直接在Goland中运行程序,默认会运行在8081端口,然后便可以用Postman或者curl调试相应接口,这部分内容请读者自行研究。

注:笔者在mac os 10.15上,发现编译时需要添加-ldflags "-w"参数,否则会运行失败。

小结

本文介绍了如何使用Golang + jwt构建一个建议的认证系统,可以让大家对用户认证有一个基本的概念,详细的代码也已经同步到github上,读者们可以clone下来参考:https://github.com/glorinli/go-jwt-simple-auth

收藏
评论区

相关推荐

关于Golang的那些事(一) -- Node.js和Golang对比
之前一直用Node.js作为开发语言,用了差不多4年的Node.js,涉及前端和后端,最近看到Golang这个新兴之秀挺火的,于是想探究探究一下这门语言,对比了一下他们的Github repo,截止现在Node.js的repo有72.5K星, issue数量是859个,Golang的repo有75.7K星,issue数量是5K个。从趋势来看,Golang来势
【Golang】Golang + jwt 实现简易用户认证
<p本文已同步发布到我的个人博客:<a href"https://links.jianshu.com/go?tohttps%3A%2F%2Fglorin.xyz%2F2019%2F11%2F23%2FGolangjwtsimpleauth%2F" target"_blank"https://glorin.xyz/2019/11/23/Golang
godoc 命令和 golang 代码文档管理
介绍 godoc 是 golang 自带的文档查看器,更多的提供部署服务 go doc 和 godoc 在 golang 1.13 被移除了,可以自行安装 golang.org go1.13 godoc(https://links.jianshu.com/go?tohttps%3A%2F%2Fgolang.org%2Fdoc%2Fg
Mac安装Golang和vscode
Mac第一次安装golang和vscode一起使用,遇到了不少的坑,下面介绍一下正确的安装方式。 1、使用brew安装Golang 如果不知道brew是什么,或怎么安装请看这里 brew官网(https://brew.sh/index_zhcn) brew install golang 安装完成后可以使用
golang 分析调试高阶技巧
layout: post title: “golang 调试高阶技巧” date: 2020603 1:44:09 0800 categories: golang GC 垃圾回收 golang 高阶调试 Golang tools nm compile
.NET & JWT
使用 JWT 库 -------- JWT,a JWT(JSON Web Token) implementation for .NET 该库支持生成和解析[JSON Web Token](https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Ftools.ietf.org%2Fhtml%2Fdr
Go语言入门系列(一)之Go的安装和使用
1.安装环境 ====== 1. 进入[Golang官网](https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fgolang.org),进入下载页面。 (如果打不开可访问[Golang中国](https://www.oschina.net/action/GoToLink?u
Archlinux下Visual Studio Code配置Golang开发环境
一、Golang的安装 ----------- GoLang安装并验证一下: [cox@localhost ~]$ sudo pacman -S go [cox@localhost ~]$ go version go version go1.8.3 linux/amd64s 要注意,Golang的安装要确保两个环境变量,一个是G
Golang Gin实践 番外 请入门 Makefile
<h1>Golang Gin实践 番外 请入门 Makefile</h1> <p>原文地址:<a href="https://github.com/EDDYCJY/blog/blob/master/golang/gin/2018-08-26-Gin%E5%AE%9E%E8%B7%B5-%E7%95%AA%E5%A4%96-%E8%AF%B7%E5%85%A5
Golang In PingCAP
随着 Golang 在后端领域越来越流行,有越来越多的公司选择 Golang 作为主力开发语言。本次 GopherChina Beijing 2016 大会上,看到 Golang 在各家公司从人工智能到自动运维,从 Web 应用到基础架构都发挥着越来越多的作用。可以说 Golang 在这几年间,获得了长足的进步。 PingCAP 是一家由几名 Go
Golang 内存管理源码剖析
Golang 的内存管理基于 tcmalloc,可以说起点挺高的。但是 Golang 在实现的时候还做了很多优化,我们下面通过源码来看一下 Golang 的内存管理实现。下面的源码分析基于 go1.8rc3。 1.tcmalloc 介绍 ------------- 关于 tcmalloc 可以参考这篇文章 [tcmalloc 介绍](https://ww
Golang 开发环境搭建
Golang 是 Google 发布的开发语言,Go 编译的程序速度可以媲美 C/C++。 安装 -- sudo apt-get install golang sudo apt-get install golang-go.tools 使用 -- * 编译运行程序 go run main.go * 查看命令文
Golang 文章正文抽取(readability)
readability for golang Golang版本是根据[readabiliity for node.js](https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fgithub.com%2Fluin%2Freadability)以及[readability for python](h
Golang依赖管理工具:glide从入门到精通使用
介绍 -- 不论是开发Java还是你正在学习的Golang,都会遇到**依赖管理**问题。Java有牛逼轰轰的Maven和Gradle。 Golang亦有godep、govendor、glide、gvt、gopack等等,本文主要给大家介绍[gilde](https://www.oschina.net/action/GoToLink?url=https%3
Sentinel
![9.28头图.png](https://ucc.alicdn.com/pic/developer-ecology/af7ab6c27c3c4c3aa5dc2cce3c9e8ab9.png) > \*\*导读:\*\*2020年,Sentinel 推出 Go 原生版本[Sentinel-Golang](https://www.oschina.net/ac