1. 程式人生 > >golang簡單實現jwt驗證(beego、xorm、jwt)

golang簡單實現jwt驗證(beego、xorm、jwt)

程式目錄結構

簡單實現,使用者登入後返回一個jwt的token,下次請求帶上token請求使用者資訊介面並返回資訊。

app.conf檔案內容(可以用個beego直接讀取裡面的內容)寫的是一個jwt的secretkey

jwtkey="12345678"

config.json裡面儲存的是連線資料庫的使用者名稱和密碼(這裡只是學習如何讀取json的配置檔案,可以整合到beego的app.conf檔案裡)

{
"sqltype":"mssql"
,"connstring":"server=.;port=1433;user id=sa;password=123;database=table1"
}

MSSqlHelper.go實現連線mssqlserver的資料庫

package mssqlhelper
 
import (
	"fmt"
 
	"github.com/akkuman/parseConfig"
	_ "github.com/denisenkom/go-mssqldb"
	"github.com/go-xorm/core"
	"github.com/go-xorm/xorm"
)
 
// 建立 XORM 客戶端
func CreateClient() *xorm.Engine {
	var config = parseConfig.New("config.json")
	sqltype := config.Get("sqltype")
	fmt.Println(sqltype)
	connstring := config.Get("connstring")
	fmt.Println(connstring)
	engine, err := xorm.NewEngine(sqltype.(string), connstring.(string))
	if err != nil {
		println("open error:", &err)
	}
	engine.SetMapper(core.SameMapper{})      //表示Struct的類的名稱和資料庫中相同
	engine.ShowSQL(true)                     //顯示SQL語句
	engine.Logger().SetLevel(core.LOG_DEBUG) //列印SQL語句
 
	return engine
}

AuthorizeController.go實現使用者登入、獲取使用者資訊介面

package controller
 
import (
	"GoApi/DAL"
	"GoApi/Model"
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"
	"strings"
	"time"
 
	"github.com/astaxie/beego/context"
 
	"github.com/astaxie/beego"
	jwt "github.com/dgrijalva/jwt-go"
	"github.com/go-xorm/xorm"
)
 
var engine *xorm.Engine
 
type AuthorizeController struct {
	beego.Controller
}
 
var filterUser = func(ctx *context.Context) {
	token := ctx.Input.Header("Authorization")
 
	b, _ := CheckToken(token)
 
	//驗證Token是否合法
	if !b {
		http.Error(ctx.ResponseWriter, "Token verification not pass", http.StatusBadRequest)
		return
	}
	fmt.Println("Request token:", token)
}
 
func init() {
	engine = mssqlhelper.CreateClient()
    //訪問介面前驗證token
	beego.InsertFilter("/Authorize/Userinfo", beego.BeforeRouter, filterUser)
}
 
type Token struct {
	Token string `json:"token"`
}
 
func fatal(err error) {
	if err != nil {
		beego.Error(err)
	}
}
 
//登入
func (this *AuthorizeController) Login() {
	var user Model.LoginModel
    // url?username=111&password=222 這種形式
	user.UserName = this.GetString("username")
	user.PassWord = this.GetString("password")
 
    //err := this.ParseForm(&user) //接收application/x-www-form-urlencoded形式POST傳遞資料,如Username=111&Password=2222
    // err := json.NewDecoder(this.Ctx.Request.Body).Decode(&user) //接收json形式Post的資料
 
	loginuser := &Model.Usertable{Userloginname: user.UserName}
	has, err := engine.Get(loginuser)
	if err != nil {
		fatal(err)
	}
	if !has {
		fatal(err)
		http.Error(this.Ctx.ResponseWriter, "User Not Exist", http.StatusBadRequest)
		return
	}
 
	if user.PassWord != loginuser.Userloginpwd {
		this.Ctx.Output.Header("SetStatus", strconv.Itoa(http.StatusBadRequest))
 
		http.Error(this.Ctx.ResponseWriter, "Password Wrong", http.StatusBadRequest)
		return
	}
 
	claims := make(jwt.MapClaims)
	claims["exp"] = time.Now().Add(time.Hour * time.Duration(1)).Unix()
	claims["iat"] = time.Now().Unix()
	claims["nameid"] = loginuser.Userloginname
	claims["User"] = "true"
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
 
	tokenString, err := token.SignedString([]byte(beego.AppConfig.String("jwtkey")))
	if err != nil {
		this.Ctx.Output.Header("SetStatus", strconv.Itoa(http.StatusInternalServerError))
		fatal(err)
		http.Error(this.Ctx.ResponseWriter, "Server is Wrong", http.StatusInternalServerError)
		return
	}
 
	fmt.Println("Token:", tokenString)
	this.Ctx.WriteString(fmt.Sprintf("{\"Token\":\"%s\"}", tokenString))
}
 
 
func (this *AuthorizeController) Userinfo() {
	token := this.Ctx.Input.Header("Authorization")
 
	b, t := CheckToken(token)
	if !b {
		this.Ctx.WriteString(fmt.Sprintf("Error:%s", token))
		return
	}
	loginuser := &Model.Usertable{Userloginname: claims["nameid"].(string)}
	has, err := engine.Get(loginuser)
	if err != nil {
		fatal(err)
	}
	if !has {
		fatal(err)
		http.Error(this.Ctx.ResponseWriter, "User Not Exist", http.StatusBadRequest)
		return
	}
	data, err := json.Marshal(loginuser)
	if err != nil {
		fmt.Println(err)
	}
	this.Ctx.WriteString(fmt.Sprintf("{\"Token\":\"%s\",\"User\":%s}", token, string(data)))
}
 
// 校驗token是否有效
func CheckToken(token string) (b bool, t *jwt.Token) {
	kv := strings.Split(token, " ")
	if len(kv) != 2 || kv[0] != "Bearer" {
		beego.Error("AuthString invalid:", token)
		return false, nil
	}
	t, err := jwt.Parse(kv[1], func(*jwt.Token) (interface{}, error) {
		return []byte(beego.AppConfig.String("jwtkey")), nil
	})
	fmt.Println(t)
	if err != nil {
		fmt.Println("轉換為jwt claims失敗.", err)
		return false, nil
	}
	return true, t
}

LoginModel.go 提交登入使用者名稱和密碼的結構

package Model
 
type LoginModel struct {
	UserName string `xorm:"VARCHAR(50)"`
	PassWord string `xorm:"VARCHAR(50)"`
}

UserTable.go 使用者資訊實體結構

package Model
 
type Usertable struct {
	Userid        int    `xorm:"not null pk INT(4)"`
	Userloginname string `xorm:"VARCHAR(50)"`
	Userloginpwd  string `xorm:"VARCHAR(50)"`
	Username      string `xorm:"NVARCHAR(200)"`
	Usermobile    string `xorm:"VARCHAR(11)"`
	Userislock    int    `xorm:"BIT(1)"`
}

main.go中通過beego啟動http的web服務

package main
 
import (
	"GoApi/controller"
	"github.com/astaxie/beego"
)
 
func main() {
	beego.AutoRouter(&controller.AuthorizeController{})
	beego.Run()
}

下一步

1、學習如何在攔截器裡攔截驗證後把某個結果值傳遞給要訪問的介面(目前是在接口裡重新解析一遍jwt的token)

2、beego如何實現只允許post訪問某個controller的介面(AutoRouter模式下)

3、Struct如何實現中文的說明(就是滑鼠放上去會顯示中文描述,類似C#類的說明那種)