support api proxy for amap
This commit is contained in:
+34
-14
@@ -152,27 +152,35 @@ func startWebServer(c *cli.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
if config.MapProvider == settings.AmapProvider && config.AmapSecurityVerificationMethod == settings.AmapSecurityVerificationInternalProxyMethod {
|
||||
amapApiProxyRoute := router.Group("/_AMapService")
|
||||
amapApiProxyRoute.Use(bindMiddleware(middlewares.JWTAuthorizationByCookie))
|
||||
{
|
||||
amapApiProxyRoute.GET("/*action", bindProxy(api.AmapApis.AmapApiProxyHandler))
|
||||
}
|
||||
}
|
||||
|
||||
apiRoute := router.Group("/api")
|
||||
|
||||
apiRoute.Use(bindMiddleware(middlewares.RequestId(config)))
|
||||
apiRoute.Use(bindMiddleware(middlewares.RequestLog))
|
||||
{
|
||||
apiRoute.POST("/authorize.json", bindApi(api.Authorizations.AuthorizeHandler))
|
||||
apiRoute.POST("/authorize.json", bindApiWithTokenUpdate(api.Authorizations.AuthorizeHandler, config))
|
||||
|
||||
if config.EnableTwoFactor {
|
||||
twoFactorRoute := apiRoute.Group("/2fa")
|
||||
twoFactorRoute.Use(bindMiddleware(middlewares.JWTTwoFactorAuthorization))
|
||||
{
|
||||
twoFactorRoute.POST("/authorize.json", bindApi(api.Authorizations.TwoFactorAuthorizeHandler))
|
||||
twoFactorRoute.POST("/recovery.json", bindApi(api.Authorizations.TwoFactorAuthorizeByRecoveryCodeHandler))
|
||||
twoFactorRoute.POST("/authorize.json", bindApiWithTokenUpdate(api.Authorizations.TwoFactorAuthorizeHandler, config))
|
||||
twoFactorRoute.POST("/recovery.json", bindApiWithTokenUpdate(api.Authorizations.TwoFactorAuthorizeByRecoveryCodeHandler, config))
|
||||
}
|
||||
}
|
||||
|
||||
if config.EnableUserRegister {
|
||||
apiRoute.POST("/register.json", bindApi(api.Users.UserRegisterHandler))
|
||||
apiRoute.POST("/register.json", bindApiWithTokenUpdate(api.Users.UserRegisterHandler, config))
|
||||
}
|
||||
|
||||
apiRoute.GET("/logout.json", bindApi(api.Tokens.TokenRevokeCurrentHandler))
|
||||
apiRoute.GET("/logout.json", bindApiWithTokenUpdate(api.Tokens.TokenRevokeCurrentHandler, config))
|
||||
|
||||
apiV1Route := apiRoute.Group("/v1")
|
||||
apiV1Route.Use(bindMiddleware(middlewares.JWTAuthorization))
|
||||
@@ -181,17 +189,17 @@ func startWebServer(c *cli.Context) error {
|
||||
apiV1Route.GET("/tokens/list.json", bindApi(api.Tokens.TokenListHandler))
|
||||
apiV1Route.POST("/tokens/revoke.json", bindApi(api.Tokens.TokenRevokeHandler))
|
||||
apiV1Route.POST("/tokens/revoke_all.json", bindApi(api.Tokens.TokenRevokeAllHandler))
|
||||
apiV1Route.POST("/tokens/refresh.json", bindApi(api.Tokens.TokenRefreshHandler))
|
||||
apiV1Route.POST("/tokens/refresh.json", bindApiWithTokenUpdate(api.Tokens.TokenRefreshHandler, config))
|
||||
|
||||
// Users
|
||||
apiV1Route.GET("/users/profile/get.json", bindApi(api.Users.UserProfileHandler))
|
||||
apiV1Route.POST("/users/profile/update.json", bindApi(api.Users.UserUpdateProfileHandler))
|
||||
apiV1Route.POST("/users/profile/update.json", bindApiWithTokenUpdate(api.Users.UserUpdateProfileHandler, config))
|
||||
|
||||
// Two Factor Authorization
|
||||
if config.EnableTwoFactor {
|
||||
apiV1Route.GET("/users/2fa/status.json", bindApi(api.TwoFactorAuthorizations.TwoFactorStatusHandler))
|
||||
apiV1Route.POST("/users/2fa/enable/request.json", bindApi(api.TwoFactorAuthorizations.TwoFactorEnableRequestHandler))
|
||||
apiV1Route.POST("/users/2fa/enable/confirm.json", bindApi(api.TwoFactorAuthorizations.TwoFactorEnableConfirmHandler))
|
||||
apiV1Route.POST("/users/2fa/enable/confirm.json", bindApiWithTokenUpdate(api.TwoFactorAuthorizations.TwoFactorEnableConfirmHandler, config))
|
||||
apiV1Route.POST("/users/2fa/disable.json", bindApi(api.TwoFactorAuthorizations.TwoFactorDisableHandler))
|
||||
apiV1Route.POST("/users/2fa/recovery/regenerate.json", bindApi(api.TwoFactorAuthorizations.TwoFactorRecoveryCodeRegenerateHandler))
|
||||
}
|
||||
@@ -291,6 +299,23 @@ func bindApi(fn core.ApiHandlerFunc) gin.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
func bindApiWithTokenUpdate(fn core.ApiHandlerFunc, config *settings.Config) gin.HandlerFunc {
|
||||
return func(ginCtx *gin.Context) {
|
||||
c := core.WrapContext(ginCtx)
|
||||
result, err := fn(c)
|
||||
|
||||
if err == nil && config.MapProvider == settings.AmapProvider && config.AmapSecurityVerificationMethod == settings.AmapSecurityVerificationInternalProxyMethod {
|
||||
middlewares.AmapApiProxyAuthCookie(c, config)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
utils.PrintJsonErrorResult(c, err)
|
||||
} else {
|
||||
utils.PrintJsonSuccessResult(c, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func bindCsv(fn core.DataHandlerFunc) gin.HandlerFunc {
|
||||
return func(ginCtx *gin.Context) {
|
||||
c := core.WrapContext(ginCtx)
|
||||
@@ -307,12 +332,7 @@ func bindCsv(fn core.DataHandlerFunc) gin.HandlerFunc {
|
||||
func bindProxy(fn core.ProxyHandlerFunc) gin.HandlerFunc {
|
||||
return func(ginCtx *gin.Context) {
|
||||
c := core.WrapContext(ginCtx)
|
||||
proxy, err := fn(c)
|
||||
|
||||
if err != nil {
|
||||
utils.PrintDataErrorResult(c, "text/text", err)
|
||||
} else {
|
||||
proxy := fn(c)
|
||||
proxy.ServeHTTP(c.Writer, c.Request)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-3
@@ -126,12 +126,19 @@ baidu_map_ak =
|
||||
# For "amap" only, Amap JavaScript API application key, please visit https://lbs.amap.com/api/javascript-api/guide/abc/prepare for more information
|
||||
amap_application_key =
|
||||
|
||||
# For "amap" only, Amap JavaScript API security verification method, supports "plain" (not recommend).
|
||||
amap_security_verification_method = plain
|
||||
# For "amap" only, Amap JavaScript API security verification method, supports the following methods:
|
||||
# "internal_proxy": use the internal proxy to request amap api with amap application secret (default)
|
||||
# "external_proxy": use an external proxy to request amap api (amap application secret should be set by external proxy)
|
||||
# "plain_text": append amap application secret directly to frontend request (insecurity for public network)
|
||||
# Please visit https://developer.amap.com/api/jsapi-v2/guide/abc/load for more information
|
||||
amap_security_verification_method = plain_text
|
||||
|
||||
# For "amap" only, Amap JavaScript API application secret, this setting must be provided when "amap_security_verification_method" is set to "plain", please visit https://lbs.amap.com/api/javascript-api/guide/abc/prepare for more information
|
||||
# For "amap" only, Amap JavaScript API application secret, this setting must be provided when "amap_security_verification_method" is set to "internal_proxy" or "plain_text", please visit https://lbs.amap.com/api/javascript-api/guide/abc/prepare for more information
|
||||
amap_application_secret =
|
||||
|
||||
# For "amap" only, Amap JavaScript API external proxy url, this setting must be provided when "amap_security_verification_method" is set to "external_proxy"
|
||||
amap_api_external_proxy_url =
|
||||
|
||||
[exchange_rates]
|
||||
# Exchange rates data source, supports the following types:
|
||||
# "euro_central_bank"
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/settings"
|
||||
)
|
||||
|
||||
const amapCustomMapStylesUrl = "https://webapi.amap.com/v4/map/styles"
|
||||
const amapOverseasMapUrl = "https://fmap01.amap.com/v3/vectormap"
|
||||
const amapRestApiUrl = "https://restapi.amap.com/"
|
||||
|
||||
// AmapApiProxy represents amap api proxy
|
||||
type AmapApiProxy struct {
|
||||
}
|
||||
|
||||
// Initialize a amap api proxy singleton instance
|
||||
var (
|
||||
AmapApis = &AmapApiProxy{}
|
||||
)
|
||||
|
||||
// AmapApiProxyHandler returns amap api response
|
||||
func (p *AmapApiProxy) AmapApiProxyHandler(c *core.Context) *httputil.ReverseProxy {
|
||||
var targetUrl string
|
||||
|
||||
if strings.HasPrefix(c.Request.RequestURI, "/_AMapService/v4/map/styles") {
|
||||
targetUrl = amapCustomMapStylesUrl + strings.TrimPrefix(c.Request.URL.Path, "/_AMapService/v4/map/styles")
|
||||
} else if strings.HasPrefix(c.Request.RequestURI, "/_AMapService/v3/vectormap") {
|
||||
targetUrl = amapOverseasMapUrl + strings.TrimPrefix(c.Request.URL.Path, "/_AMapService/v3/vectormap")
|
||||
} else {
|
||||
targetUrl = amapRestApiUrl + strings.TrimPrefix(c.Request.URL.Path, "/_AMapService/")
|
||||
}
|
||||
|
||||
director := func(req *http.Request) {
|
||||
targetRawUrl := fmt.Sprintf("%s?%s&jscode=%s", targetUrl, req.URL.RawQuery, settings.Container.Current.AmapApplicationSecret)
|
||||
targetUrl, _ := url.Parse(targetRawUrl)
|
||||
|
||||
oldCookies := req.Cookies()
|
||||
req.Header.Del("Cookie")
|
||||
|
||||
for i := 0; i < len(oldCookies); i++ {
|
||||
if strings.HasPrefix(oldCookies[i].Name, "ebk_") {
|
||||
continue
|
||||
}
|
||||
|
||||
req.AddCookie(oldCookies[i])
|
||||
}
|
||||
|
||||
req.URL = targetUrl
|
||||
req.RequestURI = req.URL.RequestURI()
|
||||
req.Host = targetUrl.Host
|
||||
}
|
||||
|
||||
return &httputil.ReverseProxy{Director: director}
|
||||
}
|
||||
@@ -74,6 +74,10 @@ func (a *AuthorizationsApi) AuthorizeHandler(c *core.Context) (interface{}, *err
|
||||
return nil, errs.ErrTokenGenerating
|
||||
}
|
||||
|
||||
if !twoFactorEnable {
|
||||
c.SetTextualToken(token)
|
||||
}
|
||||
|
||||
c.SetTokenClaims(claims)
|
||||
|
||||
log.InfofWithRequestId(c, "[authorizations.AuthorizeHandler] user \"uid:%d\" has logined, token type is %d, token will be expired at %d", user.Uid, claims.Type, claims.ExpiresAt)
|
||||
@@ -126,6 +130,7 @@ func (a *AuthorizationsApi) TwoFactorAuthorizeHandler(c *core.Context) (interfac
|
||||
return nil, errs.ErrTokenGenerating
|
||||
}
|
||||
|
||||
c.SetTextualToken(token)
|
||||
c.SetTokenClaims(claims)
|
||||
|
||||
log.InfofWithRequestId(c, "[authorizations.TwoFactorAuthorizeHandler] user \"uid:%d\" has authorized two factor via passcode, token will be expired at %d", user.Uid, claims.ExpiresAt)
|
||||
@@ -184,6 +189,7 @@ func (a *AuthorizationsApi) TwoFactorAuthorizeByRecoveryCodeHandler(c *core.Cont
|
||||
return nil, errs.ErrTokenGenerating
|
||||
}
|
||||
|
||||
c.SetTextualToken(token)
|
||||
c.SetTokenClaims(claims)
|
||||
|
||||
log.InfofWithRequestId(c, "[authorizations.TwoFactorAuthorizeByRecoveryCodeHandler] user \"uid:%d\" has authorized two factor via recovery code \"%s\", token will be expired at %d", user.Uid, credential.RecoveryCode, claims.ExpiresAt)
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"net/url"
|
||||
|
||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||
)
|
||||
|
||||
const openStreetMapTileImageUrlFormat = "https://tile.openstreetmap.org/%s/%s/%s" // https://tile.openstreetmap.org/{z}/{x}/{y}.png
|
||||
@@ -22,7 +21,7 @@ var (
|
||||
)
|
||||
|
||||
// OpenStreetMapTileImageProxyHandler returns open street map tile image
|
||||
func (p *MapImageProxy) OpenStreetMapTileImageProxyHandler(c *core.Context) (*httputil.ReverseProxy, *errs.Error) {
|
||||
func (p *MapImageProxy) OpenStreetMapTileImageProxyHandler(c *core.Context) *httputil.ReverseProxy {
|
||||
director := func(req *http.Request) {
|
||||
zoomLevel := c.Param("zoomLevel")
|
||||
coordinateX := c.Param("coordinateX")
|
||||
@@ -36,5 +35,5 @@ func (p *MapImageProxy) OpenStreetMapTileImageProxyHandler(c *core.Context) (*ht
|
||||
req.Host = imageUrl.Host
|
||||
}
|
||||
|
||||
return &httputil.ReverseProxy{Director: director}, nil
|
||||
return &httputil.ReverseProxy{Director: director}
|
||||
}
|
||||
|
||||
@@ -191,6 +191,7 @@ func (a *TokensApi) TokenRefreshHandler(c *core.Context) (interface{}, *errs.Err
|
||||
CreatedUnixTime: oldTokenClaims.IssuedAt,
|
||||
}
|
||||
|
||||
c.SetTextualToken(token)
|
||||
c.SetTokenClaims(claims)
|
||||
|
||||
log.InfofWithRequestId(c, "[token.TokenRefreshHandler] user \"uid:%d\" token refreshed, new token will be expired at %d", user.Uid, claims.ExpiresAt)
|
||||
|
||||
@@ -195,6 +195,7 @@ func (a *TwoFactorAuthorizationsApi) TwoFactorEnableConfirmHandler(c *core.Conte
|
||||
return confirmResp, nil
|
||||
}
|
||||
|
||||
c.SetTextualToken(token)
|
||||
c.SetTokenClaims(claims)
|
||||
|
||||
log.InfofWithRequestId(c, "[twofactor_authorizations.TwoFactorEnableConfirmHandler] user \"uid:%d\" token refreshed, new token will be expired at %d", user.Uid, claims.ExpiresAt)
|
||||
|
||||
@@ -85,6 +85,7 @@ func (a *UsersApi) UserRegisterHandler(c *core.Context) (interface{}, *errs.Erro
|
||||
}
|
||||
|
||||
authResp.Token = token
|
||||
c.SetTextualToken(token)
|
||||
c.SetTokenClaims(claims)
|
||||
|
||||
log.InfofWithRequestId(c, "[users.UserRegisterHandler] user \"uid:%d\" has logined, token will be expired at %d", user.Uid, claims.ExpiresAt)
|
||||
@@ -272,6 +273,7 @@ func (a *UsersApi) UserUpdateProfileHandler(c *core.Context) (interface{}, *errs
|
||||
}
|
||||
|
||||
resp.NewToken = token
|
||||
c.SetTextualToken(token)
|
||||
c.SetTokenClaims(claims)
|
||||
|
||||
log.InfofWithRequestId(c, "[users.UserUpdateProfileHandler] user \"uid:%d\" token refreshed, new token will be expired at %d", user.Uid, claims.ExpiresAt)
|
||||
|
||||
+18
-1
@@ -9,6 +9,7 @@ import (
|
||||
)
|
||||
|
||||
const requestIdFieldKey = "REQUEST_ID"
|
||||
const textualTokenFieldKey = "TOKEN_STRING"
|
||||
const tokenClaimsFieldKey = "TOKEN_CLAIMS"
|
||||
const responseErrorFieldKey = "RESPONSE_ERROR"
|
||||
|
||||
@@ -37,7 +38,23 @@ func (c *Context) GetRequestId() string {
|
||||
return requestId.(string)
|
||||
}
|
||||
|
||||
// SetTokenClaims sets the given user token id to context
|
||||
// SetTextualToken sets the given user token to context
|
||||
func (c *Context) SetTextualToken(token string) {
|
||||
c.Set(textualTokenFieldKey, token)
|
||||
}
|
||||
|
||||
// GetTextualToken returns the current user textual token
|
||||
func (c *Context) GetTextualToken() string {
|
||||
token, exists := c.Get(textualTokenFieldKey)
|
||||
|
||||
if !exists {
|
||||
return ""
|
||||
}
|
||||
|
||||
return token.(string)
|
||||
}
|
||||
|
||||
// SetTokenClaims sets the given user token to context
|
||||
func (c *Context) SetTokenClaims(claims *UserTokenClaims) {
|
||||
c.Set(tokenClaimsFieldKey, claims)
|
||||
}
|
||||
|
||||
+1
-1
@@ -16,4 +16,4 @@ type ApiHandlerFunc func(*Context) (interface{}, *errs.Error)
|
||||
type DataHandlerFunc func(*Context) ([]byte, string, *errs.Error)
|
||||
|
||||
// ProxyHandlerFunc represents the reverse proxy handler function
|
||||
type ProxyHandlerFunc func(*Context) (*httputil.ReverseProxy, *errs.Error)
|
||||
type ProxyHandlerFunc func(*Context) *httputil.ReverseProxy
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||
"github.com/mayswind/ezbookkeeping/pkg/settings"
|
||||
)
|
||||
|
||||
const tokenCookieParam = "ebk_auth_token"
|
||||
|
||||
// AmapApiProxyAuthCookie adds amap api proxy auth cookie to cookies in response
|
||||
func AmapApiProxyAuthCookie(c *core.Context, config *settings.Config) {
|
||||
token := c.GetTextualToken()
|
||||
|
||||
if token != "" {
|
||||
c.SetCookie(tokenCookieParam, token, int(config.TokenExpiredTime), "/_AMapService", "", false, true)
|
||||
} else {
|
||||
c.SetCookie(tokenCookieParam, "", -1, "/_AMapService", "", false, true)
|
||||
}
|
||||
}
|
||||
@@ -17,58 +17,24 @@ type TokenSourceType byte
|
||||
const (
|
||||
TOKEN_SOURCE_TYPE_HEADER TokenSourceType = 1
|
||||
TOKEN_SOURCE_TYPE_ARGUMENT TokenSourceType = 2
|
||||
TOKEN_SOURCE_TYPE_COOKIE TokenSourceType = 3
|
||||
)
|
||||
|
||||
const tokenQueryStringParam = "token"
|
||||
|
||||
// JWTAuthorization verifies whether current request is valid by jwt token
|
||||
// JWTAuthorization verifies whether current request is valid by jwt token in header
|
||||
func JWTAuthorization(c *core.Context) {
|
||||
claims, err := getTokenClaims(c, TOKEN_SOURCE_TYPE_HEADER)
|
||||
|
||||
if err != nil {
|
||||
utils.PrintJsonErrorResult(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
if claims.Type == core.USER_TOKEN_TYPE_REQUIRE_2FA {
|
||||
log.WarnfWithRequestId(c, "[authorization.JWTAuthorization] user \"uid:%d\" token requires 2fa", claims.Uid)
|
||||
utils.PrintJsonErrorResult(c, errs.ErrCurrentTokenRequire2FA)
|
||||
return
|
||||
}
|
||||
|
||||
if claims.Type != core.USER_TOKEN_TYPE_NORMAL {
|
||||
log.WarnfWithRequestId(c, "[authorization.JWTAuthorization] user \"uid:%d\" token type is invalid", claims.Uid)
|
||||
utils.PrintJsonErrorResult(c, errs.ErrCurrentInvalidTokenType)
|
||||
return
|
||||
}
|
||||
|
||||
c.SetTokenClaims(claims)
|
||||
c.Next()
|
||||
jwtAuthorization(c, TOKEN_SOURCE_TYPE_HEADER)
|
||||
}
|
||||
|
||||
// JWTAuthorizationByQueryString verifies whether current request is valid by jwt token
|
||||
// JWTAuthorizationByQueryString verifies whether current request is valid by jwt token in query string
|
||||
func JWTAuthorizationByQueryString(c *core.Context) {
|
||||
claims, err := getTokenClaims(c, TOKEN_SOURCE_TYPE_ARGUMENT)
|
||||
jwtAuthorization(c, TOKEN_SOURCE_TYPE_ARGUMENT)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
utils.PrintJsonErrorResult(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
if claims.Type == core.USER_TOKEN_TYPE_REQUIRE_2FA {
|
||||
log.WarnfWithRequestId(c, "[authorization.JWTAuthorizationByQueryString] user \"uid:%d\" token requires 2fa", claims.Uid)
|
||||
utils.PrintJsonErrorResult(c, errs.ErrCurrentTokenRequire2FA)
|
||||
return
|
||||
}
|
||||
|
||||
if claims.Type != core.USER_TOKEN_TYPE_NORMAL {
|
||||
log.WarnfWithRequestId(c, "[authorization.JWTAuthorizationByQueryString] user \"uid:%d\" token type is invalid", claims.Uid)
|
||||
utils.PrintJsonErrorResult(c, errs.ErrCurrentInvalidTokenType)
|
||||
return
|
||||
}
|
||||
|
||||
c.SetTokenClaims(claims)
|
||||
c.Next()
|
||||
// JWTAuthorizationByCookie verifies whether current request is valid by jwt token in cookie
|
||||
func JWTAuthorizationByCookie(c *core.Context) {
|
||||
jwtAuthorization(c, TOKEN_SOURCE_TYPE_COOKIE)
|
||||
}
|
||||
|
||||
// JWTTwoFactorAuthorization verifies whether current request is valid by 2fa passcode
|
||||
@@ -90,6 +56,30 @@ func JWTTwoFactorAuthorization(c *core.Context) {
|
||||
c.Next()
|
||||
}
|
||||
|
||||
func jwtAuthorization(c *core.Context, source TokenSourceType) {
|
||||
claims, err := getTokenClaims(c, source)
|
||||
|
||||
if err != nil {
|
||||
utils.PrintJsonErrorResult(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
if claims.Type == core.USER_TOKEN_TYPE_REQUIRE_2FA {
|
||||
log.WarnfWithRequestId(c, "[authorization.jwtAuthorization] user \"uid:%d\" token requires 2fa", claims.Uid)
|
||||
utils.PrintJsonErrorResult(c, errs.ErrCurrentTokenRequire2FA)
|
||||
return
|
||||
}
|
||||
|
||||
if claims.Type != core.USER_TOKEN_TYPE_NORMAL {
|
||||
log.WarnfWithRequestId(c, "[authorization.jwtAuthorization] user \"uid:%d\" token type is invalid", claims.Uid)
|
||||
utils.PrintJsonErrorResult(c, errs.ErrCurrentInvalidTokenType)
|
||||
return
|
||||
}
|
||||
|
||||
c.SetTokenClaims(claims)
|
||||
c.Next()
|
||||
}
|
||||
|
||||
func getTokenClaims(c *core.Context, source TokenSourceType) (*core.UserTokenClaims, *errs.Error) {
|
||||
token, claims, err := parseToken(c, source)
|
||||
|
||||
@@ -114,6 +104,8 @@ func getTokenClaims(c *core.Context, source TokenSourceType) (*core.UserTokenCla
|
||||
func parseToken(c *core.Context, source TokenSourceType) (*jwt.Token, *core.UserTokenClaims, error) {
|
||||
if source == TOKEN_SOURCE_TYPE_ARGUMENT {
|
||||
return services.Tokens.ParseTokenByArgument(c, tokenQueryStringParam)
|
||||
} else if source == TOKEN_SOURCE_TYPE_COOKIE {
|
||||
return services.Tokens.ParseTokenByCookie(c, tokenCookieParam)
|
||||
}
|
||||
|
||||
return services.Tokens.ParseTokenByHeader(c)
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||
@@ -19,27 +21,31 @@ func ServerSettingsCookie(config *settings.Config) core.MiddlewareHandlerFunc {
|
||||
buildStringSetting("m", config.MapProvider),
|
||||
}
|
||||
|
||||
if config.EnableMapDataFetchProxy {
|
||||
if config.MapProvider == settings.OpenStreetMapProvider && config.EnableMapDataFetchProxy {
|
||||
settingsArr = append(settingsArr, buildBooleanSetting("mp", config.EnableMapDataFetchProxy))
|
||||
}
|
||||
|
||||
if config.GoogleMapAPIKey != "" {
|
||||
settingsArr = append(settingsArr, buildStringSetting("gmak", config.GoogleMapAPIKey))
|
||||
if config.MapProvider == settings.GoogleMapProvider && config.GoogleMapAPIKey != "" {
|
||||
settingsArr = append(settingsArr, buildEncodedStringSetting("gmak", config.GoogleMapAPIKey))
|
||||
}
|
||||
|
||||
if config.BaiduMapAK != "" {
|
||||
settingsArr = append(settingsArr, buildStringSetting("bmak", config.BaiduMapAK))
|
||||
if config.MapProvider == settings.BaiduMapProvider && config.BaiduMapAK != "" {
|
||||
settingsArr = append(settingsArr, buildEncodedStringSetting("bmak", config.BaiduMapAK))
|
||||
}
|
||||
|
||||
if config.AMapApplicationKey != "" {
|
||||
settingsArr = append(settingsArr, buildStringSetting("amak", config.AMapApplicationKey))
|
||||
if config.MapProvider == settings.AmapProvider && config.AmapApplicationKey != "" {
|
||||
settingsArr = append(settingsArr, buildEncodedStringSetting("amak", config.AmapApplicationKey))
|
||||
}
|
||||
|
||||
if config.AMapSecurityVerificationMethod != "" {
|
||||
settingsArr = append(settingsArr, buildStringSetting("amsv", config.AMapSecurityVerificationMethod))
|
||||
if config.MapProvider == settings.AmapProvider && config.AmapSecurityVerificationMethod != "" {
|
||||
settingsArr = append(settingsArr, buildStringSetting("amsv", strings.Replace(config.AmapSecurityVerificationMethod, "_", "", -1)))
|
||||
|
||||
if config.AMapSecurityVerificationMethod == settings.AmapSecurityVerificationPlainMethod {
|
||||
settingsArr = append(settingsArr, buildStringSetting("amas", config.AMapApplicationSecret))
|
||||
if config.AmapSecurityVerificationMethod == settings.AmapSecurityVerificationExternalProxyMethod {
|
||||
settingsArr = append(settingsArr, buildEncodedStringSetting("amep", config.AmapApiExternalProxyUrl))
|
||||
}
|
||||
|
||||
if config.AmapSecurityVerificationMethod == settings.AmapSecurityVerificationPlainTextMethod {
|
||||
settingsArr = append(settingsArr, buildEncodedStringSetting("amas", config.AmapApplicationSecret))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +57,13 @@ func ServerSettingsCookie(config *settings.Config) core.MiddlewareHandlerFunc {
|
||||
}
|
||||
|
||||
func buildStringSetting(key string, value string) string {
|
||||
return fmt.Sprintf("%s.%s", key, strings.Replace(value, ".", "-", -1))
|
||||
return fmt.Sprintf("%s.%s", key, value)
|
||||
}
|
||||
|
||||
func buildEncodedStringSetting(key string, value string) string {
|
||||
urlEncodedValue := url.QueryEscape(value)
|
||||
base64Value := base64.StdEncoding.EncodeToString([]byte(urlEncodedValue))
|
||||
return fmt.Sprintf("%s.%s", key, base64Value)
|
||||
}
|
||||
|
||||
func buildBooleanSetting(key string, value bool) string {
|
||||
|
||||
@@ -73,6 +73,11 @@ func (s *TokenService) ParseTokenByArgument(c *core.Context, tokenParameterName
|
||||
return s.parseToken(c, request.ArgumentExtractor{tokenParameterName})
|
||||
}
|
||||
|
||||
// ParseTokenByCookie returns the token model according to request data
|
||||
func (s *TokenService) ParseTokenByCookie(c *core.Context, tokenCookieName string) (*jwt.Token, *core.UserTokenClaims, error) {
|
||||
return s.parseToken(c, utils.CookieExtractor{tokenCookieName})
|
||||
}
|
||||
|
||||
// CreateToken generates a new normal token and saves to database
|
||||
func (s *TokenService) CreateToken(user *models.User, ctx *core.Context) (string, *core.UserTokenClaims, error) {
|
||||
return s.createToken(user, core.USER_TOKEN_TYPE_NORMAL, s.getUserAgent(ctx), s.CurrentConfig().TokenExpiredTimeDuration)
|
||||
|
||||
+16
-8
@@ -72,7 +72,9 @@ const (
|
||||
|
||||
// Amap security verification method
|
||||
const (
|
||||
AmapSecurityVerificationPlainMethod string = "plain"
|
||||
AmapSecurityVerificationInternalProxyMethod string = "internal_proxy"
|
||||
AmapSecurityVerificationExternalProxyMethod string = "external_proxy"
|
||||
AmapSecurityVerificationPlainTextMethod string = "plain_text"
|
||||
)
|
||||
|
||||
// Exchange rates data source types
|
||||
@@ -186,9 +188,10 @@ type Config struct {
|
||||
MapProvider string
|
||||
GoogleMapAPIKey string
|
||||
BaiduMapAK string
|
||||
AMapApplicationKey string
|
||||
AMapSecurityVerificationMethod string
|
||||
AMapApplicationSecret string
|
||||
AmapApplicationKey string
|
||||
AmapSecurityVerificationMethod string
|
||||
AmapApplicationSecret string
|
||||
AmapApiExternalProxyUrl string
|
||||
EnableMapDataFetchProxy bool
|
||||
|
||||
// Exchange Rates
|
||||
@@ -462,15 +465,20 @@ func loadMapConfiguration(config *Config, configFile *ini.File, sectionName stri
|
||||
config.EnableMapDataFetchProxy = getConfigItemBoolValue(configFile, sectionName, "map_data_fetch_proxy", false)
|
||||
config.GoogleMapAPIKey = getConfigItemStringValue(configFile, sectionName, "google_map_api_key")
|
||||
config.BaiduMapAK = getConfigItemStringValue(configFile, sectionName, "baidu_map_ak")
|
||||
config.AMapApplicationKey = getConfigItemStringValue(configFile, sectionName, "amap_application_key")
|
||||
config.AmapApplicationKey = getConfigItemStringValue(configFile, sectionName, "amap_application_key")
|
||||
|
||||
if getConfigItemStringValue(configFile, sectionName, "amap_security_verification_method") == AmapSecurityVerificationPlainMethod {
|
||||
config.AMapSecurityVerificationMethod = AmapSecurityVerificationPlainMethod
|
||||
if getConfigItemStringValue(configFile, sectionName, "amap_security_verification_method") == AmapSecurityVerificationInternalProxyMethod {
|
||||
config.AmapSecurityVerificationMethod = AmapSecurityVerificationInternalProxyMethod
|
||||
} else if getConfigItemStringValue(configFile, sectionName, "amap_security_verification_method") == AmapSecurityVerificationExternalProxyMethod {
|
||||
config.AmapSecurityVerificationMethod = AmapSecurityVerificationExternalProxyMethod
|
||||
} else if getConfigItemStringValue(configFile, sectionName, "amap_security_verification_method") == AmapSecurityVerificationPlainTextMethod {
|
||||
config.AmapSecurityVerificationMethod = AmapSecurityVerificationPlainTextMethod
|
||||
} else {
|
||||
return errs.ErrInvalidAmapSecurityVerificationMethod
|
||||
}
|
||||
|
||||
config.AMapApplicationSecret = getConfigItemStringValue(configFile, sectionName, "amap_application_secret")
|
||||
config.AmapApplicationSecret = getConfigItemStringValue(configFile, sectionName, "amap_application_secret")
|
||||
config.AmapApiExternalProxyUrl = getConfigItemStringValue(configFile, sectionName, "amap_api_external_proxy_url")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5/request"
|
||||
)
|
||||
|
||||
// CookieExtractor extracts a token from request cookies
|
||||
type CookieExtractor []string
|
||||
|
||||
func (e CookieExtractor) ExtractToken(req *http.Request) (string, error) {
|
||||
for _, arg := range e {
|
||||
if cookie, _ := req.Cookie(arg); cookie != nil {
|
||||
return cookie.Value, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", request.ErrNoTokenInRequest
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
const baseApiUrlPath = '/api';
|
||||
const baseProxyUrlPath = '/proxy';
|
||||
const baseAmapApiProxyUrlPath = '/_AMapService';
|
||||
const googleMapJavascriptUrl = 'https://maps.googleapis.com/maps/api/js';
|
||||
const baiduMapJavascriptUrl = 'https://api.map.baidu.com/api?v=3.0';
|
||||
const amapJavascriptUrl = 'https://webapi.amap.com/maps?v=2.0';
|
||||
@@ -7,6 +8,7 @@ const amapJavascriptUrl = 'https://webapi.amap.com/maps?v=2.0';
|
||||
export default {
|
||||
baseApiUrlPath: baseApiUrlPath,
|
||||
baseProxyUrlPath: baseProxyUrlPath,
|
||||
baseAmapApiProxyUrlPath: baseAmapApiProxyUrlPath,
|
||||
googleMapJavascriptUrl: googleMapJavascriptUrl,
|
||||
baiduMapJavascriptUrl: baiduMapJavascriptUrl,
|
||||
amapJavascriptUrl: amapJavascriptUrl
|
||||
|
||||
+8
-4
@@ -1,6 +1,6 @@
|
||||
import { asyncLoadAssets } from "@/lib/misc.js";
|
||||
import services from "@/lib/services.js";
|
||||
import settings from "@/lib/settings.js";
|
||||
import { asyncLoadAssets } from '@/lib/misc.js';
|
||||
import services from '@/lib/services.js';
|
||||
import settings from '@/lib/settings.js';
|
||||
import logger from '@/lib/logger.js';
|
||||
|
||||
const amapHolder = {
|
||||
@@ -15,7 +15,11 @@ export function loadAmapAssets() {
|
||||
if (!window._AMapSecurityConfig) {
|
||||
const amapSecurityConfig = {};
|
||||
|
||||
if (settings.getAmapSecurityVerificationMethod() === 'plain') {
|
||||
if (settings.getAmapSecurityVerificationMethod() === 'internalproxy') {
|
||||
amapSecurityConfig.serviceHost = services.generateAmapApiInternalProxyUrl();
|
||||
} else if (settings.getAmapSecurityVerificationMethod() === 'externalproxy') {
|
||||
amapSecurityConfig.serviceHost = settings.getAmapApiExternalProxyUrl();
|
||||
} else if (settings.getAmapSecurityVerificationMethod() === 'plaintext') {
|
||||
amapSecurityConfig.securityJsCode = settings.getAmapApplicationSecret();
|
||||
}
|
||||
|
||||
|
||||
@@ -423,5 +423,8 @@ export default {
|
||||
},
|
||||
generateAmapJavascriptUrl: (callbackFnName) => {
|
||||
return `${api.amapJavascriptUrl}&key=${settings.getAmapApplicationKey()}&callback=${callbackFnName}`;
|
||||
},
|
||||
generateAmapApiInternalProxyUrl: () => {
|
||||
return `${window.location.origin}${api.baseAmapApiProxyUrlPath}`;
|
||||
}
|
||||
};
|
||||
|
||||
+17
-4
@@ -3,6 +3,8 @@ import Cookies from 'js-cookie';
|
||||
import currencyConstants from '@/consts/currency.js';
|
||||
import statisticsConstants from '@/consts/statistics.js';
|
||||
|
||||
import { base64decode } from '@/lib/common.js';
|
||||
|
||||
const settingsLocalStorageKey = 'ebk_app_settings';
|
||||
const serverSettingsCookieKey = 'ebk_server_settings';
|
||||
|
||||
@@ -119,6 +121,16 @@ function getServerSetting(key) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getServerDecodedSetting(key) {
|
||||
const value = getServerSetting(key);
|
||||
|
||||
if (!value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return decodeURIComponent(base64decode(value));
|
||||
}
|
||||
|
||||
function clearSettings() {
|
||||
localStorage.removeItem(settingsLocalStorageKey);
|
||||
}
|
||||
@@ -169,10 +181,11 @@ export default {
|
||||
isDataExportingEnabled: () => getServerSetting('e') === '1',
|
||||
getMapProvider: () => getServerSetting('m'),
|
||||
isMapDataFetchProxyEnabled: () => getServerSetting('mp') === '1',
|
||||
getGoogleMapAPIKey: () => getServerSetting('gmak'),
|
||||
getBaiduMapAK: () => getServerSetting('bmak'),
|
||||
getAmapApplicationKey: () => getServerSetting('amak'),
|
||||
getGoogleMapAPIKey: () => getServerDecodedSetting('gmak'),
|
||||
getBaiduMapAK: () => getServerDecodedSetting('bmak'),
|
||||
getAmapApplicationKey: () => getServerDecodedSetting('amak'),
|
||||
getAmapSecurityVerificationMethod: () => getServerSetting('amsv'),
|
||||
getAmapApplicationSecret: () => getServerSetting('amas'),
|
||||
getAmapApiExternalProxyUrl: () => getServerDecodedSetting('amep'),
|
||||
getAmapApplicationSecret: () => getServerDecodedSetting('amas'),
|
||||
clearSettings: clearSettings
|
||||
};
|
||||
|
||||
@@ -139,6 +139,10 @@ export default defineConfig(async () => {
|
||||
'/proxy': {
|
||||
target: 'http://127.0.0.1:8080/',
|
||||
changeOrigin: true
|
||||
},
|
||||
'/_AMapService': {
|
||||
target: 'http://127.0.0.1:8080/',
|
||||
changeOrigin: true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user