基于 RPC 微服务的 token 管理系统

        这是一个基于 RPC 微服务的 token 管理项目,实现了对 token 的创建、过滤、刷新和查询的操作。协议定义和方法调用通过 thrift 构建,token 缓存在 redis 中,有效期在业务中定义。日志写入 kafka,由其他组件消费后写入 clickhouse 后再通过 superset 汇总展示。打点操作写入 prometheus 系统,由 grafana 汇总展示。开发语言为 golang,代码为原创分享给大家。

thrift 定义

// tames.thrift

namespace go tames_thrift
namespace py tames_thrift
namespace java com.glb.tames.thrift

const string __VERSION__ = "v0.0.1"

// token 常量
const i64 EXPIRATION = 7200

// token 异常
const string ERR_LOGIN_STATUS_EXPIRED = "登录状态过期"
const string ERR_TOKEN_VERIFICATION_FAILED = "令牌验证失败"
const string ERR_TOKEN_EXPIRATION_OR_VALIDATION_FAILURE = "令牌过期或验证错误"

enum TamesStatus {
    FAILURE = -1
    SUCCESS = 0
}

exception TamesError {
    1: TamesStatus status           // 错误编码
    2: string message               // 错误信息
}

struct Token {
    1: required string  uuid        // 唯一标识
    2: required i64     user_id     // 用户 ID
    3: required string  user_name   // 用户名称
    4: required string  user_addr   // 用户地址
}

service TamesService {
    bool filter_token(1: string token) throws (1: TamesError err);
    string create_token(1: Token token) throws (1: TamesError err);

    void refresh_token(1: Token token) throws (1: TamesError err);
    string select_token(1: string token) throws (1: TamesError err);
}

数据结构

package macro

import "github.com/dgrijalva/jwt-go"

/**
 * Author: gonglibin
 * Description: 用户结构
 */

const (
	UserKey         string = "user_key"
	DetailsUserID   string = "user_id"
	DetailsUserName string = "username"
	LoginTokenKey   string = "login_tokens:"
	Secret          string = "abcdefghijklmnopqrstuvwxyz"
)

type LoginUser struct {
	jwt.StandardClaims
	UUID       string `json:"uuid"`        // 唯一标识
	UserID     int64  `json:"uid"`         // 用户 ID
	UserName   string `json:"user_name"`   // 用户名称
	UserAddr   string `json:"user_addr"`   // 用户地址
	LoginTime  int64  `json:"login_time"`  // 登录时间
	ExpireTime int64  `json:"expire_time"` // 过期时间
}

常量定义

package macro

const (
	CtxTimer string = "ctx:timer"
	CtxStart string = "ctx:start"

	CtxUserID string = "ctx:userid"
	CtxCToken string = "ctx:c:token"
	CtxSToken string = "ctx:s:token"
)

接口定义

package api

import (
	"context"
)

/**
 * Author: gonglibin
 * Description: API 接口定义
 */

type Api interface {
	Post(ctx context.Context, log string)
	Pre(ctx context.Context) context.Context
	Run(ctx context.Context) (interface{}, error)
	PreCustom(ctx context.Context, val interface{}) context.Context
}

创建 token

package impl

import (
	"context"
	"github.com/dgrijalva/jwt-go"
	"github.com/prometheus/client_golang/prometheus"
	"token/pkg/api"
	"token/pkg/log"
	"token/pkg/macro"
	tJedis "token/pkg/utils/jedis"
	tKafka "token/pkg/utils/kafka"
	tPrometheus "token/pkg/utils/prometheus"
)

/**
 * Author: gonglibin
 * Description: CreateTokenAPIImpl
 */

type CreateTokenAPIImpl struct {
	*APIImpl
}

var _ api.Api = (*CreateTokenAPIImpl)(nil)

var (
	CreateTokenAPI      api.Api
	CreateTokenTimer    prometheus.Gauge
	CreateTokenRequest  prometheus.Counter
	CreateTokenResponse prometheus.Counter
)

func init() {
	CreateTokenAPI = NewCreateTokenAPI()
	CreateTokenTimer = tPrometheus.NewPrometheusGauge("create_token_timer", "Create Token 耗时")
	CreateTokenRequest = tPrometheus.NewPrometheusCounter("create_token_request_counter", "Create Token Request 数量")
	CreateTokenResponse = tPrometheus.NewPrometheusCounter("create_token_response_counter", "Create Token Response 数量")

	tPrometheus.DefaultPrometheus.Collectors(
		CreateTokenTimer,
		CreateTokenRequest,
		CreateTokenResponse,
	)
}

func NewCreateTokenAPI() *CreateTokenAPIImpl {
	return &CreateTokenAPIImpl{
		APIImpl: &APIImpl{
			jedis:      tJedis.DefaultJedis,
			producer:   tKafka.DefaultProducer,
			prometheus: tPrometheus.DefaultPrometheus,
		},
	}
}

func (a *CreateTokenAPIImpl) PreCustom(ctx context.Context, val interface{}) context.Context {
	ctx = context.WithValue(ctx, macro.CtxTimer, CreateTokenTimer)
	return context.WithValue(ctx, macro.CtxCToken, val)
}

func (a *CreateTokenAPIImpl) Run(ctx context.Context) (interface{}, error) {
	tamesLog := log.TamesLog{}
	loginUser := a.getLoginUserByToken(ctx)
	tPrometheus.DefaultPrometheus.Inc(CreateTokenRequest)
	defer tPrometheus.DefaultPrometheus.Inc(CreateTokenResponse)

	tkn, err := a.getClaims(loginUser)
	go a.Post(ctx, tamesLog.GetTamesLog(loginUser, "create_token"))
	return tkn, err
}

func (a *CreateTokenAPIImpl) getClaims(loginUser *macro.LoginUser) (string, error) {
	tkn := jwt.NewWithClaims(jwt.SigningMethodHS256, loginUser)
	return tkn.SignedString([]byte(macro.Secret))
}

过滤 token

package impl

import (
	"context"
	"errors"
	"github.com/prometheus/client_golang/prometheus"
	tThrift "tames/gen-go/tames_thrift"
	"token/pkg/api"
	"token/pkg/log"
	"token/pkg/macro"
	tJedis "token/pkg/utils/jedis"
	tKafka "token/pkg/utils/kafka"
	tPrometheus "token/pkg/utils/prometheus"
	"time"
)

/**
 * Author: gonglibin
 * Description: FilterTokenAPIImpl
 */

type FilterTokenAPIImpl struct {
	*APIImpl
}

var _ api.Api = (*FilterTokenAPIImpl)(nil)

var (
	FilterTokenAPI      api.Api
	FilterTokenTimer    prometheus.Gauge
	FilterTokenRequest  prometheus.Counter
	FilterTokenResponse prometheus.Counter
)

func init() {
	FilterTokenAPI = NewFilterTokenAPI()
	FilterTokenTimer = tPrometheus.NewPrometheusGauge("filter_token_timer", "Filter Token 耗时")
	FilterTokenRequest = tPrometheus.NewPrometheusCounter("filter_token_request_counter", "Filter Token Request 数量")
	FilterTokenResponse = tPrometheus.NewPrometheusCounter("filter_token_response_counter", "Filter Token Response 数量")

	tPrometheus.DefaultPrometheus.Collectors(
		FilterTokenTimer,
		FilterTokenRequest,
		FilterTokenResponse,
	)
}

func NewFilterTokenAPI() *FilterTokenAPIImpl {
	return &FilterTokenAPIImpl{
		APIImpl: &APIImpl{
			jedis:      tJedis.DefaultJedis,
			producer:   tKafka.DefaultProducer,
			prometheus: tPrometheus.DefaultPrometheus,
		},
	}
}

func (a *FilterTokenAPIImpl) PreCustom(ctx context.Context, val interface{}) context.Context {
	ctx = context.WithValue(ctx, macro.CtxTimer, FilterTokenTimer)
	return context.WithValue(ctx, macro.CtxSToken, val)
}

func (a *FilterTokenAPIImpl) Run(ctx context.Context) (interface{}, error) {
	tamesLog := log.TamesLog{}
	loginUser, _ := a.getLoginUserByString(ctx)
	tPrometheus.DefaultPrometheus.Inc(FilterTokenRequest)
	defer tPrometheus.DefaultPrometheus.Inc(FilterTokenResponse)

	if loginUser == nil {
		return false, errors.New(tThrift.ERR_TOKEN_EXPIRATION_OR_VALIDATION_FAILURE)
	}
	if loginUser.ExpireTime < time.Now().Unix() {
		return false, errors.New(tThrift.ERR_LOGIN_STATUS_EXPIRED)
	}
	if loginUser.UserID == 0 || loginUser.UserName == "" {
		return false, errors.New(tThrift.ERR_TOKEN_VERIFICATION_FAILED)
	}
	go a.Post(ctx, tamesLog.GetTamesLog(loginUser, "filter_token"))
	return true, nil
}

刷新 token

package impl

import (
	"context"
	"encoding/json"
	"github.com/prometheus/client_golang/prometheus"
	"strconv"
	tThrift "tames/gen-go/tames_thrift"
	"token/pkg/api"
	"token/pkg/log"
	"token/pkg/macro"
	tJedis "token/pkg/utils/jedis"
	tKafka "token/pkg/utils/kafka"
	tPrometheus "token/pkg/utils/prometheus"
	"time"
)

/**
 * Author: gonglibin
 * Description: RefreshTokenAPIImpl
 */

type RefreshTokenAPIImpl struct {
	*APIImpl
}

var _ api.Api = (*RefreshTokenAPIImpl)(nil)

var (
	RefreshTokenAPI      api.Api
	RefreshTokenTimer    prometheus.Gauge
	RefreshTokenRequest  prometheus.Counter
	RefreshTokenResponse prometheus.Counter
)

func init() {
	RefreshTokenAPI = NewRefreshTokenAPI()
	RefreshTokenTimer = tPrometheus.NewPrometheusGauge("refresh_token_timer", "Refresh Token 耗时")
	RefreshTokenRequest = tPrometheus.NewPrometheusCounter("refresh_token_request_counter", "Refresh Token Request 数量")
	RefreshTokenResponse = tPrometheus.NewPrometheusCounter("refresh_token_response_counter", "Refresh Token Response 数量")

	tPrometheus.DefaultPrometheus.Collectors(
		RefreshTokenTimer,
		RefreshTokenRequest,
		RefreshTokenResponse,
	)
}

func NewRefreshTokenAPI() *RefreshTokenAPIImpl {
	return &RefreshTokenAPIImpl{
		APIImpl: &APIImpl{
			jedis:      tJedis.DefaultJedis,
			producer:   tKafka.DefaultProducer,
			prometheus: tPrometheus.DefaultPrometheus,
		},
	}
}

func (a *RefreshTokenAPIImpl) PreCustom(ctx context.Context, val interface{}) context.Context {
	ctx = context.WithValue(ctx, macro.CtxTimer, RefreshTokenTimer)
	return context.WithValue(ctx, macro.CtxCToken, val)
}

func (a *RefreshTokenAPIImpl) Run(ctx context.Context) (interface{}, error) {
	tamesLog := log.TamesLog{}
	loginUser := a.getLoginUserByToken(ctx)
	tPrometheus.DefaultPrometheus.Inc(RefreshTokenRequest)
	defer tPrometheus.DefaultPrometheus.Inc(RefreshTokenResponse)

	a.setToken(loginUser)
	go a.Post(ctx, tamesLog.GetTamesLog(loginUser, "refresh_token"))
	return nil, nil
}

func (a *RefreshTokenAPIImpl) setToken(loginUser *macro.LoginUser) {
	val, _ := json.Marshal(loginUser)
	key := macro.LoginTokenKey + strconv.FormatInt(loginUser.UserID, 10)
	a.jedis.Client.Set(
		key,
		val,
		time.Duration(tThrift.EXPIRATION)*time.Second,
	)
}

查询 token

package impl

import (
	"context"
	"encoding/json"
	"github.com/prometheus/client_golang/prometheus"
	"token/pkg/api"
	"token/pkg/log"
	"token/pkg/macro"
	tJedis "token/pkg/utils/jedis"
	tKafka "token/pkg/utils/kafka"
	tPrometheus "token/pkg/utils/prometheus"
)

/**
 * Author: gonglibin
 * Description: SelectTokenAPIImpl
 */

type SelectTokenAPIImpl struct {
	*APIImpl
}

var _ api.Api = (*SelectTokenAPIImpl)(nil)

var (
	SelectTokenAPI      api.Api
	SelectTokenTimer    prometheus.Gauge
	SelectTokenRequest  prometheus.Counter
	SelectTokenResponse prometheus.Counter
)

func init() {
	SelectTokenAPI = NewSelectTokenAPI()
	SelectTokenTimer = tPrometheus.NewPrometheusGauge("select_token_timer", "Select Token 耗时")
	SelectTokenRequest = tPrometheus.NewPrometheusCounter("select_token_request_counter", "Select Token Request 数量")
	SelectTokenResponse = tPrometheus.NewPrometheusCounter("select_token_response_counter", "Select Token Response 数量")

	tPrometheus.DefaultPrometheus.Collectors(
		SelectTokenTimer,
		SelectTokenRequest,
		SelectTokenResponse,
	)
}

func NewSelectTokenAPI() *SelectTokenAPIImpl {
	return &SelectTokenAPIImpl{
		APIImpl: &APIImpl{
			jedis:      tJedis.DefaultJedis,
			producer:   tKafka.DefaultProducer,
			prometheus: tPrometheus.DefaultPrometheus,
		},
	}
}

func (a *SelectTokenAPIImpl) PreCustom(ctx context.Context, val interface{}) context.Context {
	ctx = context.WithValue(ctx, macro.CtxTimer, SelectTokenTimer)
	return context.WithValue(ctx, macro.CtxUserID, val)
}

func (a *SelectTokenAPIImpl) Run(ctx context.Context) (interface{}, error) {
	tamesLog := log.TamesLog{}
	uid := ctx.Value(macro.CtxUserID).(string)
	tPrometheus.DefaultPrometheus.Inc(SelectTokenRequest)
	defer tPrometheus.DefaultPrometheus.Inc(SelectTokenResponse)

	val := a.getToken(uid)
	loginUser := &macro.LoginUser{}
	json.Unmarshal([]byte(val), loginUser)
	go a.Post(ctx, tamesLog.GetTamesLog(loginUser, "select_token"))
	return val, nil
}

func (a *SelectTokenAPIImpl) getToken(uid string) string {
	return a.jedis.Client.Get(macro.LoginTokenKey + uid).Val()
}

server 定义

package service

import (
	"context"
	"fmt"
	"github.com/getsentry/raven-go"
	tThrift "tames/gen-go/tames_thrift"
	"tames/pkg/api"
	"tames/pkg/api/impl"
)

type TamesServiceImpl struct {
	DefaultCreateToken  api.Api
	DefaultFilterToken  api.Api
	DefaultSelectToken  api.Api
	DefaultRefreshToken api.Api
}

var _ tThrift.TamesService = (*TamesServiceImpl)(nil)

func NewTamesService() *TamesServiceImpl {
	return &TamesServiceImpl{
		DefaultCreateToken:  impl.CreateTokenAPI,
		DefaultFilterToken:  impl.FilterTokenAPI,
		DefaultSelectToken:  impl.SelectTokenAPI,
		DefaultRefreshToken: impl.RefreshTokenAPI,
	}
}

func (p *TamesServiceImpl) FilterToken(ctx context.Context, tkn string) (bool, error) {
	defer func() {
		if r := recover(); r != nil {
			raven.CaptureError(fmt.Errorf("FilterToken panic: %v", r), nil)
		}
	}()
	ctx = p.DefaultFilterToken.Pre(ctx)
	ctx = p.DefaultFilterToken.PreCustom(ctx, tkn)
	rst, err := p.DefaultFilterToken.Run(ctx)
	return rst.(bool), err
}

func (p *TamesServiceImpl) CreateToken(ctx context.Context, tkn *tThrift.Token) (string, error) {
	defer func() {
		if r := recover(); r != nil {
			raven.CaptureError(fmt.Errorf("CreateToken panic: %v", r), nil)
		}
	}()
	ctx = p.DefaultCreateToken.Pre(ctx)
	ctx = p.DefaultCreateToken.PreCustom(ctx, tkn)
	rst, err := p.DefaultCreateToken.Run(ctx)
	return rst.(string), err
}

func (p *TamesServiceImpl) RefreshToken(ctx context.Context, tkn *tThrift.Token) error {
	defer func() {
		if r := recover(); r != nil {
			raven.CaptureError(fmt.Errorf("RefreshToken panic: %v", r), nil)
		}
	}()
	ctx = p.DefaultRefreshToken.Pre(ctx)
	ctx = p.DefaultRefreshToken.PreCustom(ctx, tkn)
	p.DefaultRefreshToken.Run(ctx)
	return nil
}

func (p *TamesServiceImpl) SelectToken(ctx context.Context, tkn string) (string, error) {
	defer func() {
		if r := recover(); r != nil {
			raven.CaptureError(fmt.Errorf("RefreshToken panic: %v", r), nil)
		}
	}()
	ctx = p.DefaultSelectToken.Pre(ctx)
	ctx = p.DefaultSelectToken.PreCustom(ctx, tkn)
	rst, err := p.DefaultSelectToken.Run(ctx)
	return rst.(string), err
}

主程入口

package main

import (
	"fmt"
	"github.com/apache/thrift/lib/go/thrift"
	"os"
	tThrift "tames/gen-go/tames_thrift"
	"tames/pkg/service"
)

func main() {
	listen := "0.0.0.0:8202"
	tamesService := service.NewTamesService()
	serviceProcessor := tThrift.NewTamesServiceProcessor(tamesService)
	serverSocket, err := thrift.NewTServerSocket(listen)
	if err != nil {
		fmt.Println("启动失败:", err)
		os.Exit(1)
	}

	server := thrift.NewTSimpleServer2(serviceProcessor, serverSocket)
	if err := server.Serve(); err != nil {
		panic(err)
	}
}

        以上为核心功能代码,仅供参考。

  • 7
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

厉力文武

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值