作者 yangfu

feat: user auth integrated

... ... @@ -6,6 +6,7 @@ require (
github.com/ajg/form v1.5.1 // indirect
github.com/beego/beego/v2 v2.0.1
github.com/bwmarrin/snowflake v0.3.0
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/extrame/xls v0.0.1
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072 // indirect
github.com/fatih/structs v1.1.0 // indirect
... ... @@ -20,6 +21,7 @@ require (
github.com/moul/http2curl v1.0.0 // indirect
github.com/onsi/ginkgo v1.16.5
github.com/onsi/gomega v1.18.1
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/sergi/go-diff v1.2.0 // indirect
github.com/shopspring/decimal v1.3.1
github.com/smartystreets/goconvey v1.7.2 // indirect
... ...
... ... @@ -24,6 +24,8 @@ var METADATA_BASTION_HOST = "http://127.0.0.1:8080"
var BYTE_CORE_HOST = "http://192.168.100.34:8303"
var AUTH_SERVER_HOST = "http://digital-platform-dev.fjmaimaimai.com/"
//var CUSTOMER_ACCOUNT = []int64{3129687560814592, 3129687690100739, 3492238958608384}
//const CUSTOMER_ACCOUNT_DELIMITER = ","
... ...
package constant
var JWTSecretKey = "digital-platform"
var JWTExpiresIn = 60 * 60 * 24 //单位:秒
var JWTCacheExpiresIn = 60 * 60 * 24 //单位:秒
... ...
package domain
import (
"errors"
jwt "github.com/dgrijalva/jwt-go"
"gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/constant"
"time"
)
type UserToken struct {
UserId int64 `json:"userId"`
CompanyId int64 `json:"companyId"`
CompanyType int `json:"companyType"`
jwt.StandardClaims
}
func (tk UserToken) GenerateToken() (string, error) {
tk.StandardClaims = jwt.StandardClaims{
ExpiresAt: time.Now().Add(time.Duration(constant.JWTExpiresIn) * time.Second).Unix(),
Issuer: "digital-platform",
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, tk)
return token.SignedString([]byte(constant.JWTSecretKey))
}
func (tk *UserToken) ParseToken(str string) error {
tokenClaims, err := jwt.ParseWithClaims(
str,
tk,
func(token *jwt.Token) (interface{}, error) {
return []byte(constant.JWTSecretKey), nil
})
if err != nil {
return err
}
if claim, ok := tokenClaims.Claims.(*UserToken); ok && tokenClaims.Valid {
*tk = *claim
return nil
}
return errors.New("token 解析失败")
}
... ...
package authlib
import (
"github.com/beego/beego/v2/core/logs"
"gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/infrastructure/api"
"net/http"
"time"
)
type ApiAuthLib struct {
Token string
api.BaseServiceGateway
}
func (gateway *ApiAuthLib) WithToken(token string) *ApiAuthLib {
gateway.Token = token
return gateway
}
func (gateway *ApiAuthLib) DefaultHeader() http.Header {
var header = make(map[string][]string)
header["x-mmm-accesstoken"] = []string{gateway.Token}
return header
}
func NewApiAuthLib(host string) *ApiAuthLib {
gt := api.NewBaseServiceGateway(host)
gt.ConnectTimeout = 360 * time.Second
gt.ReadWriteTimeout = 360 * time.Second
gt.Interceptor = func(msg string) {
//log.Logger.Info(msg)
logs.Debug(msg)
}
gt.ServiceName = "【授权中心】"
return &ApiAuthLib{
BaseServiceGateway: gt,
}
}
func (gateway *ApiAuthLib) MeInfo(param RequestUserMeQuery) (*DataUserMe, error) {
url := gateway.Host() + "/v1/user/me"
method := "get"
var data DataUserMe
err := gateway.FastDoRequest(url, method, param, &data, api.WithHeader(gateway.DefaultHeader()))
if err != nil {
return nil, err
}
return &data, nil
}
... ...
package authlib
type RequestUserMeQuery struct {
Token string
}
type DataUserMe struct {
User *struct {
ID string `json:"id"`
Phone string `json:"phone"`
NickName string `json:"nickName"`
Avatar string `json:"avatar"`
} `json:"user"`
CompanyList []*struct {
ID string `json:"id"`
Name string `json:"name"`
Logo string `json:"logo"`
DefaultLogin int `json:"defaultLogin"`
Types int `json:"types"`
} `json:"companyList"`
CurrentCompany *struct {
ID string `json:"id"`
Name string `json:"name"`
Logo string `json:"logo"`
DefaultLogin int `json:"defaultLogin"`
Types int `json:"types"`
} `json:"currentCompany"`
Workbench []*struct {
ID int `json:"id"`
Name string `json:"name"`
Code string `json:"code"`
CoverImage string `json:"coverImage"`
URL string `json:"url"`
} `json:"workbench"`
Menus []*struct {
MenuID int `json:"menuId"`
ParentID int `json:"parentId"`
MenuName string `json:"menuName"`
Code string `json:"code"`
Types string `json:"types"`
} `json:"menus"`
}
... ...
... ... @@ -36,7 +36,7 @@ type Request struct {
Param interface{}
}
func (gateway BaseServiceGateway) CreateRequest(url string, method string) *httplib.BeegoHTTPRequest {
func (gateway BaseServiceGateway) CreateRequest(url string, method string, options *RequestOptions) *httplib.BeegoHTTPRequest {
var request *httplib.BeegoHTTPRequest
method = strings.ToUpper(method)
switch method {
... ... @@ -53,6 +53,11 @@ func (gateway BaseServiceGateway) CreateRequest(url string, method string) *http
default:
request = httplib.Get(url)
}
if len(options.Header) > 0 {
for k, v := range options.Header {
request.Header(k, strings.Join(v, ";"))
}
}
return request.SetTimeout(gateway.ConnectTimeout, gateway.ReadWriteTimeout)
}
... ... @@ -64,7 +69,7 @@ func (gateway BaseServiceGateway) GetResponseData(result Response, data interfac
return nil
}
func (gateway BaseServiceGateway) FastDoRequest(url, method string, param interface{}, data interface{}) error {
func (gateway BaseServiceGateway) FastDoRequest(url, method string, param interface{}, data interface{}, options ...Option) error {
begin := time.Now()
var err error
var result = "success"
... ... @@ -82,19 +87,23 @@ func (gateway BaseServiceGateway) FastDoRequest(url, method string, param interf
))
}
}()
var requestOptions = &RequestOptions{}
for _, option := range options {
option(requestOptions)
}
err = gateway.DoRequest(Request{
Url: url,
Method: method,
Param: param,
}, &data)
}, &data, requestOptions)
if err != nil {
return err
}
return nil
}
func (gateway BaseServiceGateway) DoRequest(requestParam Request, val interface{}) error {
r := gateway.CreateRequest(requestParam.Url, requestParam.Method)
func (gateway BaseServiceGateway) DoRequest(requestParam Request, val interface{}, options *RequestOptions) error {
r := gateway.CreateRequest(requestParam.Url, requestParam.Method, options)
req, err := r.JSONBody(requestParam.Param)
if err != nil {
return err
... ... @@ -123,3 +132,15 @@ func NewBaseServiceGateway(host string) BaseServiceGateway {
host: host,
}
}
type RequestOptions struct {
Header http.Header
}
type Option func(o *RequestOptions)
func WithHeader(header http.Header) Option {
return func(o *RequestOptions) {
o.Header = header
}
}
... ...
package cache
import (
"fmt"
cache "github.com/patrickmn/go-cache"
"time"
)
var DefaultCache = cache.New(12*time.Hour, 1*time.Hour)
func KeyCompanyUser(companyId int, userId int) string {
return fmt.Sprintf("local:cache:user:%d:%d", companyId, userId)
}
... ...
... ... @@ -6,8 +6,11 @@ import (
"github.com/beego/beego/v2/server/web"
"github.com/beego/beego/v2/server/web/context"
"github.com/linmadan/egglib-go/web/beego/filters"
"gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/domain"
"net/http"
"os"
"strconv"
"strings"
. "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/log"
_ "gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/port/beego/routers"
... ... @@ -48,6 +51,7 @@ func init() {
}
web.InsertFilter("/*", web.BeforeRouter, filters.AllowCors())
web.InsertFilter("/*", web.BeforeRouter, JwtFilter())
web.InsertFilter("/*", web.BeforeExec, CreateRequestLogFilter(true)) // filters.CreateRequstLogFilter(Logger)
web.InsertFilter("/*", web.AfterExec, filters.CreateResponseLogFilter(Logger), web.WithReturnOnOutput(false))
}
... ... @@ -61,3 +65,24 @@ func CreateRequestLogFilter(console bool) func(ctx *context.Context) {
}
}
}
func JwtFilter() func(ctx *context.Context) {
return func(ctx *context.Context) {
token := ctx.Request.Header.Get("Authorization")
if len(token) > 0 {
token = strings.TrimPrefix(token, "Bearer ")
userToken := &domain.UserToken{}
err := userToken.ParseToken(token)
if err != nil {
ctx.Output.SetStatus(http.StatusOK)
ctx.Output.JSON(map[string]interface{}{
"msg": "token 过期或无效,需刷新令牌",
"code": 901,
"data": struct{}{},
}, false, false)
return
}
ctx.Input.SetData("UserToken", userToken)
}
}
}
... ...
... ... @@ -4,7 +4,10 @@ import (
"github.com/beego/beego/v2/server/web/context"
"github.com/linmadan/egglib-go/web/beego"
"github.com/linmadan/egglib-go/web/beego/utils"
"gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/constant"
"gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/domain"
"gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/infrastructure/api/authlib"
"gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/infrastructure/cache"
"gitlab.fjmaimaimai.com/allied-creation/character-library-metadata-bastion/pkg/log"
"strconv"
)
... ... @@ -39,10 +42,37 @@ func Must(err error) {
}
func ParseContext(c beego.BaseController) *domain.Context {
var companyId int = 1
var userId int = 1
var userName string = "管理员"
if token := c.Ctx.Input.GetData("UserToken"); token != nil {
userToken, ok := token.(*domain.UserToken)
if ok {
// cache user info
key := cache.KeyCompanyUser(companyId, userId)
if cacheItem, ok := cache.DefaultCache.Get(key); ok {
v := cacheItem.(*authlib.DataUserMe)
userName = v.User.NickName
} else {
requestToken, _ := userToken.GenerateToken()
authLib := authlib.NewApiAuthLib(constant.AUTH_SERVER_HOST).WithToken(requestToken)
userInfo, err := authLib.MeInfo(authlib.RequestUserMeQuery{})
if err != nil || userInfo == nil || userInfo.User == nil {
goto END
}
userName = userInfo.User.NickName
cache.DefaultCache.SetDefault(key, userInfo)
}
// assign user
companyId = int(userToken.CompanyId)
userId = int(userToken.UserId)
}
}
END:
ctx := &domain.Context{
CompanyId: 1,
OperatorId: 1,
OperatorName: "管理员",
CompanyId: companyId,
OperatorId: userId,
OperatorName: userName,
TenantId: 1,
}
return ctx
... ...