Small program login authentication service, client low-level SDK, login authentication, business request, authentication retry module Typescript combat.

A series of

  1. Cloud native API Gateway, GRPC-Gateway V2 Exploration
  2. Go + GRPC-gateway (V2) Construction of micro-service combat series, small program login authentication service: the first chapter
  3. Go + GRPC-Gateway (V2) Construction of micro-service combat series, small program login authentication service: the second chapter
  4. Go + GRPC-gateway (V2) to build micro-service combat series, small program login authentication service (three) : RSA(RS512) signature JWT
  5. Go+ GRPC-gateway (V2) micro-service combat, small program login authentication service (four) : automatic generation of API TS type
  6. Go+ GRPC-gateway (V2) Authentication service: GrPC-interceptor authentication service

Demo: go-grpc-gateway-v2-microservice

Preliminary establishment of front-end base (SDK)

New client/miniprogram/service/SDK. Ts file, preliminary build our front end to the bottom of the public facilities.

To define aSDK namespace

export namespace SDK {
    
}
Copy the code

Define the related constant & 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
}
Copy the code

Based on current requirements, the following things have been done:

  • Extract server addressserverAddr
  • Define an authorization failure401❌ constants
  • tokenThe correlation is temporarily stored in memory
  • Defining the clientwx.requestThe required parameter type
  • Controls authorization request-related logic (whether or not to attachAuth Header& retry etc.)

wx.loginRewrite intoPromiseIn the form of

export function wxLogin() :Promise<WechatMiniprogram.LoginSuccessCallbackResult> {
        return new Promise((resolve, reject) = > {
            wx.login({
                success: resolve,
                fail: reject,
            })
        })
    }
Copy the code

Request common logicwx.requestwrite

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
            })
        })
    }
Copy the code

Login module (login) write

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}Copy the code

Business request automatic retry module writing

 export async function sendRequestWithAuthRetry<REQ.RES> (o: RequestOption
       
        , 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
        }
    }
}
Copy the code

Todo Service

The client-specific service layer, here is the Todo service.

We create a new document control client related logic: client/miniprogram/service/todo ts

To create aTodo

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
        })
    }
}
Copy the code

Once the lower layer is fixed, the upper heap business is much better.

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
I am weishao wechat: uuhells123 public number: hackers afternoon tea add my wechat (mutual learning exchange), pay attention to the public number (for more learning materials ~)Copy the code