Skip to content

Instantly share code, notes, and snippets.

@guotie
Created September 11, 2016 00:52
Show Gist options
  • Save guotie/248d6dd21d0b8903d3ddeb94f20bc41c to your computer and use it in GitHub Desktop.
Save guotie/248d6dd21d0b8903d3ddeb94f20bc41c to your computer and use it in GitHub Desktop.
package main
import (
"dxmall/models/user"
"dxmall/utils"
"fmt"
"net/http"
"strings"
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
)
var (
AcessTokenInvalid = fmt.Errorf("access token not found in header or invalid")
)
func getUser(c *gin.Context) (*user.DxUser, error) {
token, ok := getToken(c.Request)
if !ok {
return nil, AcessTokenInvalid
}
return user.GetUserByToken(utils.ContextDB(c), utils.ContextRedis(c), token)
}
// 从request头部提取access token
// Authorization: Bearer F_9.B5f-4.1JqM
func getToken(req *http.Request) (string, bool) {
auth := req.Header.Get("Authorization")
if auth == "" {
return "", false
}
return parseBearer(auth)
}
// parseBearer parses an Bearer Authentication string.
// "Bearer F_9.B5f-4.1JqM" returns F_9.B5f-4.1JqM, true
func parseBearer(auth string) (token string, ok bool) {
const prefix = "Bearer "
if !strings.HasPrefix(auth, prefix) {
return
}
return auth[len(prefix):], true
}
// 登出
func logoutEndpoint(c *gin.Context) {
token, ok := getToken(c.Request)
if !ok {
utils.JsonError(c, utils.ERROR_NO_ACCESS_TOKEN, "access token not exist or invalid")
return
}
rc := utils.ContextRedis(c)
err := user.RemoveTokens(rc, token)
if err != nil {
utils.JsonError(c, utils.ERROR_RM_TOKEN_FAILED, "remove token failed: %v", err)
return
}
utils.JsonData(c, "logout success")
}
type RegFrom struct {
Phone string `binding:"required" json:"phone"`
Password string `binding:"required" json:"password"`
Password2 string `binding:"required" json:"password2"`
UserName string `json:"user_name"`
}
// POST
// 注册
// 注册时, 客户端必须把客户端的Basic 认证提交
func registerEndpoint(c *gin.Context) {
var form RegFrom
db := utils.ContextDB(c)
rc := utils.ContextRedis(c)
client, err := getClient(db, c.Request)
if err != nil {
utils.JsonError(c, utils.ERROR_OAUTH_CLIENT, "get oauth2 client failed: %v", err)
return
}
err = c.BindJSON(&form)
if err != nil {
utils.JsonError(c, utils.ERROR_OAUTH_FORM, "register form invalid: %v", err)
return
}
if form.Password != form.Password2 {
utils.JsonError(c, utils.ERROR_PASSWD_NOT_EQUAL, "password not equal")
return
}
ipaddr := c.Request.RemoteAddr
u, code, err := user.CreateUser(db, form.Phone, form.Password, form.UserName, ipaddr, 0)
if err != nil {
utils.JsonError(c, code, "create user failed: %v", err)
return
}
// 这里生成token, 并同时把token下发
accessToken, refreshToken, err := u.GetTokens(utils.ContextRedis(c), client)
if err != nil {
utils.JsonError(c, utils.ERROR_CREATE_TOKEN, "create token for user %d client %s failed: %v",
u.Id, client.Name, err.Error())
return
}
utils.JsonData(c, map[string]interface{}{
"uid": u.Id,
"phone": u.UserPhone,
"name": u.UserName,
"access_token": accessToken,
"refresh_token": refreshToken,
})
}
type TokenForm struct {
GrantType string `binding:"required" json:"grant_type"`
Username string `binding:"required" json:"username"`
Password string `binding:"required" json:"password"`
}
type RefreshForm struct {
GrantType string `binding:"required" json:"grant_type"`
RefreshToken string `binding:"required" json:"refresh_token"`
}
// client
func getClient(db *gorm.DB, req *http.Request) (*user.DxClient, error) {
// client name, client secret
cname, secret, ok := req.BasicAuth()
if !ok {
return nil, fmt.Errorf("request header Authorization error")
}
return user.AuthenClient(db, cname, secret)
}
//
// POST /api/v1/auth/token
// oauth2用密码方式登陆
//
// 首先, 在request header中要有Basic Authorization 头部
// 然后, 根据头部的Basic Authorization字段验证用户的client
// 然后, 从request body中取出登陆用户的用户名(手机号码)和用户密码, 验证用户名、密码
// 最后, 返回token, user id等信息
//
// 该接口返回一个验证成功的token json结构体
func tokenEndpoint(c *gin.Context) {
var (
err error
u *user.DxUser
form TokenForm
)
db := utils.ContextDB(c)
client, err := getClient(db, c.Request)
if err != nil {
utils.JsonError(c, utils.ERROR_OAUTH_CLIENT, "get oauth2 client failed: %v", err)
return
}
// bind json to form
err = c.BindJSON(&form)
if err != nil {
utils.JsonError(c, utils.ERROR_OAUTH_FORM, "bind token form failed: %s", err.Error())
return
}
if strings.ToLower(form.GrantType) != "password" {
utils.JsonError(c, utils.ERROR_OAUTH_GRANT, "only accept grant type password")
return
}
// authenticate user
u, err = user.AuthenUser(db, form.Username, form.Password)
if err != nil {
utils.JsonError(c, utils.ERROR_OAUTH_USER, "Auth failed: %s", err.Error())
return
}
accessToken, refreshToken, err := u.GetTokens(utils.ContextRedis(c), client)
if err != nil {
utils.JsonError(c, utils.ERROR_CREATE_TOKEN, "create token for user %d client %s failed: %v",
u.Id, client.Name, err.Error())
return
}
utils.JsonData(c, map[string]interface{}{
"uid": u.Id,
"phone": u.UserPhone,
"name": u.UserName,
"token_type": accessToken.TokenType,
"expires_in": accessToken.ExpiresIn,
"access_token": accessToken.Token,
"refresh_token": refreshToken,
})
//c.JSON(200, gin.H{)
}
/*
http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html
如果用户访问的时候,客户端的"访问令牌"已经过期,则需要使用"更新令牌"申请一个新的访问令牌。
客户端发出更新令牌的HTTP请求,包含以下参数:
granttype:表示使用的授权模式,此处的值固定为"refreshtoken",必选项。
refresh_token:表示早前收到的更新令牌,必选项。
scope:表示申请的授权范围,不可以超出上一次申请的范围,如果省略该参数,则表示与上一次一致。
下面是一个例子。
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA
*/
func refreshEndpoint(c *gin.Context) {
db := utils.ContextDB(c)
client, err := getClient(db, c.Request)
if err != nil {
utils.JsonError(c, utils.ERROR_OAUTH_CLIENT, "get oauth2 client failed: %v", err)
return
}
var form RefreshForm
// bind json to form
err = c.BindJSON(&form)
if err != nil {
utils.JsonError(c, utils.ERROR_OAUTH_FORM, "bind token form failed: %s", err.Error())
return
}
if strings.ToLower(form.GrantType) != "refresh_token" {
utils.JsonError(c, utils.ERROR_OAUTH_GRANT, "only accept grant type refresh_token")
return
}
rc := utils.ContextRedis(c)
token, err := user.RefreshAccessToken(rc, client.Name, form.RefreshToken, "", client.AccessSeconds)
if err != nil {
utils.JsonError(c, utils.ERROR_REFRESH_TOKEN, "refresh token failed: %s", err.Error())
return
}
utils.JsonData(c, map[string]interface{}{
"token_type": token.TokenType,
"expires_in": token.ExpiresIn,
"access_token": token,
})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment