1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
| package auth
import (
"context"
"encoding/json"
"fmt"
"github.com/containous/traefik/v2/pkg/config/dynamic"
"github.com/containous/traefik/v2/pkg/log"
"github.com/containous/traefik/v2/pkg/middlewares"
"github.com/containous/traefik/v2/pkg/tracing"
"github.com/opentracing/opentracing-go/ext"
"io/ioutil"
"net/http"
"net/url"
"strings"
"time"
)
const (
tokenTypeName = "TokenAuthType"
)
type tokenAuth struct {
address string
next http.Handler
name string
client http.Client
}
type commonResponse struct {
Status int32 `json:"status"`
Message string `json:"message"`
}
// NewToken creates a passport auth middleware.
func NewToken(ctx context.Context, next http.Handler, config dynamic.TokenAuth, name string) (http.Handler, error) {
log.FromContext(middlewares.GetLoggerCtx(ctx, name, tokenTypeName)).Debug("Creating middleware")
// 插件结构体
ta := &tokenAuth{
address: config.Address,
next: next,
name: name,
}
// 创建请求其他服务的 http client
ta.client = http.Client{
CheckRedirect: func(r *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
Timeout: 30 * time.Second,
}
return ta, nil
}
func (ta *tokenAuth) GetTracingInformation() (string, ext.SpanKindEnum) {
return ta.name, ext.SpanKindRPCClientEnum
}
func (ta tokenAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
logger := log.FromContext(middlewares.GetLoggerCtx(req.Context(), ta.name, tokenTypeName))
errorMsg := []byte("{\"code\":10000,\"message\":\"token校验失败!\"}")
// 从 header 中获取 token
token := req.Header.Get("token")
if token == "" {
logMessage := fmt.Sprintf("Error calling %s. Cause token is empty", ta.address)
traceAndResponseDebug(logger, rw, req, logMessage, []byte("{\"statue\":10000,\"message\":\"token is empty\"}"), http.StatusBadRequest)
return
}
// 以下都是请求其他服务验证 token
// 构建请求体
form := url.Values{}
form.Add("token", token)
passportReq, err := http.NewRequest(http.MethodPost, ta.address, strings.NewReader(form.Encode()))
tracing.LogRequest(tracing.GetSpan(req), passportReq)
if err != nil {
logMessage := fmt.Sprintf("Error calling %s. Cause %s", ta.address, err)
traceAndResponseDebug(logger, rw, req, logMessage, errorMsg, http.StatusBadRequest)
return
}
tracing.InjectRequestHeaders(req)
passportReq.Header.Set("Content-Type", "application/x-www-form-urlencoded")
// post 请求
passportResponse, forwardErr := ta.client.Do(passportReq)
if forwardErr != nil {
logMessage := fmt.Sprintf("Error calling %s. Cause: %s", ta.address, forwardErr)
traceAndResponseError(logger, rw, req, logMessage, errorMsg, http.StatusBadRequest)
return
}
logger.Info(fmt.Sprintf("Passport auth calling %s. Response: %+v", ta.address, passportResponse))
// 读 body
body, readError := ioutil.ReadAll(passportResponse.Body)
if readError != nil {
logMessage := fmt.Sprintf("Error reading body %s. Cause: %s", ta.address, readError)
traceAndResponseError(logger, rw, req, logMessage, errorMsg, http.StatusBadRequest)
return
}
defer passportResponse.Body.Close()
if passportResponse.StatusCode != http.StatusOK {
logMessage := fmt.Sprintf("Remote error %s. StatusCode: %d", ta.address, passportResponse.StatusCode)
traceAndResponseDebug(logger, rw, req, logMessage, errorMsg, http.StatusBadRequest)
return
}
// 解析 body
var commonRes commonResponse
err = json.Unmarshal(body, &commonRes)
if err != nil {
logMessage := fmt.Sprintf("Body unmarshal error. Body: %s", body)
traceAndResponseError(logger, rw, req, logMessage, errorMsg, http.StatusBadRequest)
return
}
// 判断返回值,非0代表验证失败
if commonRes.Status != 0 {
logMessage := fmt.Sprintf("Body status is not success. Status: %d", commonRes.Status)
traceAndResponseDebug(logger, rw, req, logMessage, errorMsg, http.StatusBadRequest)
return
}
ta.next.ServeHTTP(rw, req)
}
func traceAndResponseDebug(logger log.Logger, rw http.ResponseWriter, req *http.Request, logMessage string, errorMsg []byte, status int) {
logger.Debug(logMessage)
tracing.SetErrorWithEvent(req, logMessage)
rw.Header().Set("Content-Type", "application/json;charset=UTF-8")
rw.WriteHeader(status)
_, _ = rw.Write(errorMsg)
}
func traceAndResponseInfo(logger log.Logger, rw http.ResponseWriter, req *http.Request, logMessage string, errorMsg []byte, status int) {
logger.Info(logMessage)
tracing.SetErrorWithEvent(req, logMessage)
rw.Header().Set("Content-Type", "application/json;charset=UTF-8")
rw.WriteHeader(status)
_, _ = rw.Write(errorMsg)
}
func traceAndResponseError(logger log.Logger, rw http.ResponseWriter, req *http.Request, logMessage string, errorMsg []byte, status int) {
logger.Debug(logMessage)
tracing.SetErrorWithEvent(req, logMessage)
rw.Header().Set("Content-Type", "application/json;charset=UTF-8")
rw.WriteHeader(status)
_, _ = rw.Write(errorMsg)
}
|