This commit is contained in:
tcq 2023-04-26 14:42:59 +08:00
parent 53d6e2a94d
commit 3eade1c2c0
10 changed files with 596 additions and 0 deletions

7
.gitignore vendored
View File

@ -14,4 +14,11 @@
# Dependency directories (remove the comment below to include it)
# vendor/
demo2
*.exe
logs/
go.sum
.DS_Store
.vscode/
resource/

167
consul/consul.go Normal file
View File

@ -0,0 +1,167 @@
package consul
import (
"errors"
"fmt"
"math/rand"
"time"
consulapi "github.com/hashicorp/consul/api"
)
var (
_client *consulapi.Client
conf *Config
)
type Config struct {
Address string
}
// Init 初始化consul连接
func Init(config *Config) error {
if config == nil {
config = &Config{
Address: "127.0.0.1:8500",
}
}
conf = config
if conf.Address == "" {
conf.Address = "127.0.0.1:8500"
}
return nil
}
func New() (*consulapi.Client, error) {
if _client != nil {
return _client, nil
}
// 创建连接consul服务配置
config := consulapi.DefaultConfig()
config.Address = conf.Address
client, err := consulapi.NewClient(config)
if err != nil {
return nil, err
}
_client = client
return client, nil
}
// RegisterAPI 注册api服务到consul
func RegisterAPI(name string, addr string, port int, tags ...string) error {
client, err := New()
if err != nil {
return err
}
if client == nil {
return errors.New("consul 实例空")
}
// 创建注册到consul的服务到
registration := new(consulapi.AgentServiceRegistration)
registration.ID = fmt.Sprintf("%s-%s:%d", name, addr, port)
registration.Name = name
registration.Port = port
registration.Tags = tags
registration.Address = addr
// 增加consul健康检查回调函数
check := new(consulapi.AgentServiceCheck)
check.HTTP = fmt.Sprintf("http://%s:%d/health/check", registration.Address, registration.Port)
check.Timeout = "5s"
check.Interval = "5s"
check.DeregisterCriticalServiceAfter = "30s" // 故障检查失败30s后 consul自动将注册服务删除
registration.Check = check
// 注册服务到consul
if err := client.Agent().ServiceRegister(registration); err != nil {
return err
}
return nil
}
// DeRegister 取消consul注册的服务
func DeRegister(name string, addr string, port int) error {
client, err := New()
if err != nil {
return err
}
if client == nil {
return errors.New("consul 实例空")
}
client.Agent().ServiceDeregister(fmt.Sprintf("%s-%s:%d", name, addr, port))
return nil
}
// FindNode 查找节点
func FindNode(servicename, tag string) (*consulapi.AgentService, error) {
client, err := New()
if err != nil {
return nil, err
}
if client == nil {
return nil, errors.New("consul 实例空")
}
services, _, err := client.Health().Service(servicename, tag, true, nil)
if err != nil {
return nil, err
}
l := len(services)
if l == 0 {
return nil, nil
}
if l == 1 {
return services[0].Service, nil
}
r := rand.New(rand.NewSource(time.Now().UnixNano()))
return services[r.Intn(l)%l].Service, nil
}
// CheckHeath 健康检查
func CheckHeath(serviceid string) error {
client, err := New()
if err != nil {
return err
}
if client == nil {
return errors.New("consul 实例空")
}
// 健康检查
// a, b, _ := client.Agent().AgentHealthServiceByID(serviceid)
return nil
}
// KVPut test
func KVPut(key string, values *[]byte, flags uint64) (*consulapi.WriteMeta, error) {
client, err := New()
if err != nil {
return nil, err
}
if client == nil {
return nil, errors.New("consul 实例空")
}
return client.KV().Put(&consulapi.KVPair{Key: key, Flags: flags, Value: *values}, nil)
}
// KVGet 获取值
func KVGet(key string, flags uint64) (*[]byte, error) {
client, err := New()
if err != nil {
return nil, err
}
if client == nil {
return nil, errors.New("consul 实例空")
}
// KV get值
data, _, _ := client.KV().Get(key, nil)
if data != nil {
return &data.Value, nil
}
return nil, nil
}

84
gin/auth-filter.go Normal file
View File

@ -0,0 +1,84 @@
package gin
import (
"encoding/json"
"github.com/gin-gonic/gin"
"myschools.me/suguo/snippet/redis"
)
//从redis中认证用户
func AuthUser() gin.HandlerFunc {
return func(c *gin.Context) {
token := c.GetHeader("Authorization")
claims := cacheGet(&token)
if claims == nil {
c.Abort()
return
}
c.Set("user", claims)
c.Next()
}
}
//从redis中获取用户信息最佳实践经验建议把此代码放service层
func cacheGet(token *string) interface{} {
var user interface{}
b, err := redis.GetBytes(token)
if err != nil {
return nil
}
if err := json.Unmarshal(*b, &user); err != nil {
return nil
}
return &user
}
// gin拦截基于微服务的拦截
// func AuthUserBS() gin.HandlerFunc {
// return func(c *gin.Context) {
// token := c.GetHeader("Authorization")
// claims := userAuthWithGrpc(&token)
// if claims == nil {
// yy.RespUnauth(c, "token无效或过期请重新登录", nil, nil)
// c.Abort()
// }
// c.Set("user", claims)
// c.Next()
// }
// }
// func userAuthWithGrpc(token *string) *yy.UserClaims {
// srv, err := consul.FindService("oauth", "v1")
// if err != nil {
// logrus.WithFields(logrus.Fields{
// "func": "userAuthWithGrpc",
// }).Errorf("consul.FindServer: %s", err.Error())
// return nil
// }
// defer srv.Close()
// client := pb.NewCertificationClient(srv)
// resp, err := client.Auth(context.Background(), &pb.CertificationAuthRequest{
// Token: *token,
// })
// if err != nil {
// logrus.WithFields(logrus.Fields{
// "func": "userAuthWithGrpc",
// }).Errorf("client.Auth: %s", err.Error())
// return nil
// }
// if resp.Result == "ok" {
// r := &yy.UserClaims{}
// if err := json.Unmarshal(resp.Data.Value, r); err != nil {
// logrus.WithFields(logrus.Fields{
// "func": "userAuthWithGrpc",
// }).Errorf("json.Unmarshal: %s", err.Error())
// return nil
// }
// return r
// }
// logrus.WithFields(logrus.Fields{
// "func": "userAuthWithGrpc",
// }).Warnln("nil")
// return nil
// }

11
gin/config.go Normal file
View File

@ -0,0 +1,11 @@
package gin
// GIN 配置
type Config struct {
RootPath string
Addr string
Port int
Ssl bool
SslPem string
SslKey string
}

66
gin/gin.go Normal file
View File

@ -0,0 +1,66 @@
package gin
import (
"fmt"
"log"
"net/http"
"strconv"
"time"
"github.com/gin-gonic/gin"
"github.com/unrolled/secure"
)
func Service(conf *Config) *Config {
if conf == nil {
conf = &Config{
RootPath: "/",
Addr: "0.0.0.0",
Port: 8080,
Ssl: false,
SslPem: "server.pem",
SslKey: "server.key",
}
}
go func() {
router := gin.New()
routerSetup(router, &conf.RootPath)
if conf.Ssl {
router.Use(tlsHandler(conf))
}
s := &http.Server{
Addr: fmt.Sprintf("%s:%d", conf.Addr, conf.Port),
Handler: router,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
}
log.Printf("start service on %s", fmt.Sprintf("%s:%d", conf.Addr, conf.Port))
if conf.Ssl {
log.Fatal(s.ListenAndServeTLS(conf.SslPem, conf.SslKey))
} else {
log.Fatal(s.ListenAndServe())
}
}()
return conf
}
func tlsHandler(conf *Config) gin.HandlerFunc {
return func(c *gin.Context) {
secureMiddleware := secure.New(secure.Options{
SSLRedirect: true,
SSLHost: ":" + strconv.Itoa(conf.Port),
})
err := secureMiddleware.Process(c.Writer, c.Request)
// If there was an error, do not continue.
if err != nil {
return
}
c.Next()
}
}

34
gin/router.go Normal file
View File

@ -0,0 +1,34 @@
package gin
import (
"fmt"
"github.com/gin-gonic/gin"
"myschools.me/campus/demo-2/handler"
)
// 路由配置
func routerSetup(router *gin.Engine, rootpath *string) {
router.Use(gin.Recovery())
router.GET(`/health/check`, handler.HealthCheck)
r := router.Group(fmt.Sprintf("/%s", *rootpath))
{
r.POST(`/register`)
r.GET(`/accountcheck/:accname`)
r.POST(`/login`)
r.POST(`/forgot`)
}
ug := router.Group(`/user`)
{
ug.GET(`/choose/:orgid`)
ug.GET(`/detail`)
ug.POST(`/update`)
}
user := router.Group("user")
{
user.GET("login", handler.UserLogin)
}
}

53
go.mod Normal file
View File

@ -0,0 +1,53 @@
module myschools.me/campus/demo-2
go 1.19
require (
github.com/gin-gonic/gin v1.9.0
github.com/hashicorp/consul/api v1.20.0
github.com/unrolled/secure v1.13.0
myschools.me/campus/campus-core v0.1.68
myschools.me/suguo/snippet v1.0.4
)
require (
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da // indirect
github.com/bytedance/sonic v1.8.0 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/fatih/color v1.9.0 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.11.2 // indirect
github.com/goccy/go-json v0.10.0 // indirect
github.com/gomodule/redigo v1.8.9 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.1 // indirect
github.com/hashicorp/go-hclog v0.12.0 // indirect
github.com/hashicorp/go-immutable-radix v1.0.0 // indirect
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/hashicorp/serf v0.10.1 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/mattn/go-colorable v0.1.6 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.4.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.9 // indirect
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
golang.org/x/crypto v0.5.0 // indirect
golang.org/x/net v0.8.0 // indirect
golang.org/x/sys v0.6.0 // indirect
golang.org/x/text v0.8.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gorm.io/gorm v1.24.3 // indirect
)

80
handler/user-handler.go Normal file
View File

@ -0,0 +1,80 @@
package handler
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"github.com/gin-gonic/gin"
"myschools.me/campus/campus-core/campus"
"myschools.me/campus/demo-2/consul"
"myschools.me/campus/demo-2/tools"
)
func HealthCheck(c *gin.Context) {
c.JSON(http.StatusOK, nil)
}
func getPort(key string) (string, error) {
client, err := consul.New()
if err != nil {
fmt.Println(err.Error())
}
service, _, err := client.Health().Service(key, "", true, nil)
if err != nil {
return "", err
}
fmt.Println("Service", service[0].Service.Address, "port", service[0].Service.Port)
if len(service) > 0 {
return service[0].Service.Address + ":" + fmt.Sprintf("%v", service[0].Service.Port), nil
} else {
return "", nil
}
}
func UserLogin(c *gin.Context) {
username, _ := c.GetQuery("username")
pwd, _ := c.GetQuery("password")
str := c.GetHeader("Authorization")
reqid := campus.NewRequestID(nil)
if username != "admin" || pwd != "admin" {
campus.RespBadRequest(c, "账户密码错误", errors.New("账户密码错误"), &reqid)
return
}
url, err := getPort("demo1")
url = "http://" + url + "/token/verify"
fmt.Println("URL:", url)
if err != nil {
panic(err)
}
headerMap := make(map[string]string, 0)
headerMap["Authorization"] = str
headerMap["Content-Type"] = "application/json"
fmt.Println("Authorization:" + str)
err2, bytes := tools.GetPost(url, "get", headerMap, nil)
if err2 != nil {
panic(err2)
}
base := struct {
Code int `json:"code"`
Msg string `json:"msg"`
Requestid string `json:"requestid"`
Data struct{}
}{}
_ = json.Unmarshal(bytes, &base)
if base.Code == 0 {
campus.RespSuccess(c, "密码正确,请求头正确", "欢迎登录", &reqid)
return
} else {
campus.RespBadRequest(c, "请求头错误", errors.New("请求头错误"), &reqid)
return
}
}

51
main.go Normal file
View File

@ -0,0 +1,51 @@
package main
import (
"context"
"fmt"
"os"
"os/signal"
"time"
"myschools.me/campus/campus-core/exceptionless"
"myschools.me/campus/demo-2/consul"
"myschools.me/campus/demo-2/gin"
)
func main() {
// 日志初始化
// exceptionless.Init(&exceptionless.Config{
// ApiKey: os.Getenv("EXCEPTIONLESS_KEY"),
// ServerURL: os.Getenv("EXCEPTIONLESS_URL"),
// UpdateSettingsWhenIdleInterval: 0,
// })
//gin初始化,基于docker部署不再需要调整默认值(80)
consul.Init(&consul.Config{
Address: "192.168.0.214:8500",
})
cnf := gin.Service(&gin.Config{
RootPath: "/",
Addr: "0.0.0.0",
Port: 8082,
Ssl: false,
SslPem: "server.pem",
SslKey: "server.key",
})
if err := consul.RegisterAPI("demo2", "192.168.0.214", cnf.Port, ""); err != nil {
fmt.Println("注册失败", err.Error())
}
// exceptionless.SubmitLog(fmt.Sprintf("campus-srv[%s:%d%s] service is running...", conf.Addr, conf.Port, conf.RootPath), "info")
// 服务停止相应
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
<-c
_, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
exceptionless.SubmitLog("campus-demo2 service shutting down", "info")
os.Exit(0)
}

43
tools/http-client.go Normal file
View File

@ -0,0 +1,43 @@
package tools
import (
"io/ioutil"
"net/http"
"strings"
)
/*
请求示例
url := "https://blog.csdn.net/weixin_60232039/article/details/128861515"
headers := make(map[string]string)
headers["Content-Type"] = "application/json"
meth := "get"
err, bytes := GetPost(url, meth, headers, nil)
if err != nil {
panic(err)
}
// fmt.Println(string(bytes))
*/
func GetPost(url string, meth string, header map[string]string, data []byte) (error, []byte) {
client := &http.Client{}
newMeth := strings.ToUpper(meth)
req, err := http.NewRequest(newMeth, url, strings.NewReader(string(data)))
if err != nil {
return err, nil
}
if len(header) > 0 {
for k, v := range header {
req.Header.Add(k, v)
}
}
resp, _ := client.Do(req)
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
return nil, body
}