小程式登入鑑權服務,客戶端底層 SDK,登入鑑權、業務請求、鑑權重試模組 Typescript 實戰。
系列
- 雲原生 API 閘道器,gRPC-Gateway V2 初探
- Go + gRPC-Gateway(V2) 構建微服務實戰系列,小程式登入鑑權服務:第一篇
- Go + gRPC-Gateway(V2) 構建微服務實戰系列,小程式登入鑑權服務:第二篇
- Go + gRPC-Gateway(V2) 構建微服務實戰系列,小程式登入鑑權服務(三):RSA(RS512) 簽名 JWT
- Go+gRPC-Gateway(V2) 微服務實戰,小程式登入鑑權服務(四):自動生成 API TS 型別
- Go+gRPC-Gateway(V2) 微服務實戰,小程式登入鑑權服務(五):鑑權 gRPC-Interceptor 攔截器實戰
Demo: go-grpc-gateway-v2-microservice
前端底層初步搭建(SDK)
新建 client/miniprogram/service/sdk.ts
檔案,來初步搭建一下我們前端的底層公共設施。
定義一個 SDK
namespace
export namespace SDK {
}
定義相關常量 & Interface
const serverAddr = 'http://localhost:8080'
const AUTH_ERR= 'AUTH_ERR'
const authData = {
token: '',
expiryMs: 0
}
interface RequestOption<REQ, RES> {
method: 'GET'|'PUT'|'POST'|'DELETE'
path: string
data: REQ
respMarshaller: (r: object)=>RES
}
interface AuthOption {
attachAuthHeader: boolean
retryOnAuthError: boolean
}
這裡主要根據當前需求,做了如下事情:
- 抽出伺服器地址
serverAddr
- 定義一個授權失敗
401
❌常量 token
相關暫時存到記憶體中- 定義客戶端
wx.request
所必須的引數型別 - 控制授權請求相關邏輯(是否附加
Auth Header
& 重試等)
wx.login
改寫成 Promise
形式
export function wxLogin(): Promise<WechatMiniprogram.LoginSuccessCallbackResult> {
return new Promise((resolve, reject) => {
wx.login({
success: resolve,
fail: reject,
})
})
}
請求公共邏輯 wx.request
編寫
export function sendRequest<REQ, RES>(o: RequestOption<REQ, RES>, a: AuthOption): Promise<RES> {
const authOpt = a || {
attachAuthHeader: true,
}
return new Promise((resolve, reject) => {
const header: Record<string, any> = {}
if (authOpt.attachAuthHeader) {
if (authData.token && authData.expiryMs >= Date.now()) {
header.authorization = 'Bearer '+ authData.token
} else {
reject(AUTH_ERR)
return
}
}
wx.request({
url: serverAddr + o.path,
method: o.method,
data: o.data,
header,
success: res => {
if(res.statusCode === 401) {
reject(AUTH_ERR)
} else if (res.statusCode >= 400) {
reject(res)
} else {
resolve(
o.respMarshaller(
camelcaseKeys(res.data as object, { deep: true }),
)
)
}
},
fail: reject
})
})
}
登入模組(login
)編寫
export async function login() {
if (authData.token && authData.expiryMs >= Date.now()) {
return
}
const wxResp = await wxLogin()
const reqTimeMs = Date.now()
const resp = await sendRequest<auth.v1.ILoginRequest, auth.v1.ILoginResponse>({
method: "POST",
path: "/v1/auth/login",
data: {
code: wxResp.code,
},
respMarshaller: auth.v1.LoginResponse.fromObject
}, {
attachAuthHeader: false,
retryOnAuthError: false,
})
authData.token = resp.accessToken!
authData.expiryMs = reqTimeMs + resp.expiresIn! * 1000
}
業務請求自動重試模組編寫
export async function sendRequestWithAuthRetry<REQ, RES>(o: RequestOption<REQ, RES>, a?: AuthOption): Promise<RES> {
const authOpt = a || {
attachAuthHeader: true,
retryOnAuthError: true,
}
try {
await login()
return sendRequest(o, authOpt)
} catch(err) {
if(err === AUTH_ERR && authOpt.retryOnAuthError) {
authData.token = ''
authData.expiryMs = 0
return sendRequestWithAuthRetry(o, {
attachAuthHeader: authOpt.attachAuthHeader,
retryOnAuthError: false
})
} else {
throw err
}
}
}
Todo Service
客戶端具體服務層,這裡是 Todo
這個服務。
我們新建一個檔案控制客戶端相關邏輯:client/miniprogram/service/todo.ts
建立一個 Todo
export namespace TodoService {
export function CreateTodo(req: todo.v1.ICreateTodoRequest): Promise<todo.v1.ICreateTodoResponse>{
return SDK.sendRequestWithAuthRetry({
method: "POST",
path: "/v1/todo",
data: req,
respMarshaller: todo.v1.CreateTodoResponse.fromObject
})
}
}
低層弄好後,上層堆業務就爽很多了。
Refs
- grpc-ecosystem/go-grpc-middleware
- API Security : API key is dead..Long live Distributed Token by value
- Demo: go-grpc-gateway-v2-microservice
- gRPC-Gateway
- gRPC-Gateway Docs
我是為少
微信:uuhells123
公眾號:黑客下午茶
加我微信(互相學習交流),關注公眾號(獲取更多學習資料~)