github.com/astaxie/beego@v1.12.3/plugins/apiauth/apiauth.go (about) 1 // Copyright 2014 beego Author. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package apiauth provides handlers to enable apiauth support. 16 // 17 // Simple Usage: 18 // import( 19 // "github.com/astaxie/beego" 20 // "github.com/astaxie/beego/plugins/apiauth" 21 // ) 22 // 23 // func main(){ 24 // // apiauth every request 25 // beego.InsertFilter("*", beego.BeforeRouter,apiauth.APIBaiscAuth("appid","appkey")) 26 // beego.Run() 27 // } 28 // 29 // Advanced Usage: 30 // 31 // func getAppSecret(appid string) string { 32 // // get appsecret by appid 33 // // maybe store in configure, maybe in database 34 // } 35 // 36 // beego.InsertFilter("*", beego.BeforeRouter,apiauth.APISecretAuth(getAppSecret, 360)) 37 // 38 // Information: 39 // 40 // In the request user should include these params in the query 41 // 42 // 1. appid 43 // 44 // appid is assigned to the application 45 // 46 // 2. signature 47 // 48 // get the signature use apiauth.Signature() 49 // 50 // when you send to server remember use url.QueryEscape() 51 // 52 // 3. timestamp: 53 // 54 // send the request time, the format is yyyy-mm-dd HH:ii:ss 55 // 56 package apiauth 57 58 import ( 59 "bytes" 60 "crypto/hmac" 61 "crypto/sha256" 62 "encoding/base64" 63 "fmt" 64 "net/url" 65 "sort" 66 "time" 67 68 "github.com/astaxie/beego" 69 "github.com/astaxie/beego/context" 70 ) 71 72 // AppIDToAppSecret is used to get appsecret throw appid 73 type AppIDToAppSecret func(string) string 74 75 // APIBasicAuth use the basic appid/appkey as the AppIdToAppSecret 76 func APIBasicAuth(appid, appkey string) beego.FilterFunc { 77 ft := func(aid string) string { 78 if aid == appid { 79 return appkey 80 } 81 return "" 82 } 83 return APISecretAuth(ft, 300) 84 } 85 86 // APIBaiscAuth calls APIBasicAuth for previous callers 87 func APIBaiscAuth(appid, appkey string) beego.FilterFunc { 88 return APIBasicAuth(appid, appkey) 89 } 90 91 // APISecretAuth use AppIdToAppSecret verify and 92 func APISecretAuth(f AppIDToAppSecret, timeout int) beego.FilterFunc { 93 return func(ctx *context.Context) { 94 if ctx.Input.Query("appid") == "" { 95 ctx.ResponseWriter.WriteHeader(403) 96 ctx.WriteString("miss query param: appid") 97 return 98 } 99 appsecret := f(ctx.Input.Query("appid")) 100 if appsecret == "" { 101 ctx.ResponseWriter.WriteHeader(403) 102 ctx.WriteString("not exist this appid") 103 return 104 } 105 if ctx.Input.Query("signature") == "" { 106 ctx.ResponseWriter.WriteHeader(403) 107 ctx.WriteString("miss query param: signature") 108 return 109 } 110 if ctx.Input.Query("timestamp") == "" { 111 ctx.ResponseWriter.WriteHeader(403) 112 ctx.WriteString("miss query param: timestamp") 113 return 114 } 115 u, err := time.Parse("2006-01-02 15:04:05", ctx.Input.Query("timestamp")) 116 if err != nil { 117 ctx.ResponseWriter.WriteHeader(403) 118 ctx.WriteString("timestamp format is error, should 2006-01-02 15:04:05") 119 return 120 } 121 t := time.Now() 122 if t.Sub(u).Seconds() > float64(timeout) { 123 ctx.ResponseWriter.WriteHeader(403) 124 ctx.WriteString("timeout! the request time is long ago, please try again") 125 return 126 } 127 if ctx.Input.Query("signature") != 128 Signature(appsecret, ctx.Input.Method(), ctx.Request.Form, ctx.Input.URL()) { 129 ctx.ResponseWriter.WriteHeader(403) 130 ctx.WriteString("auth failed") 131 } 132 } 133 } 134 135 // Signature used to generate signature with the appsecret/method/params/RequestURI 136 func Signature(appsecret, method string, params url.Values, RequestURL string) (result string) { 137 var b bytes.Buffer 138 keys := make([]string, len(params)) 139 pa := make(map[string]string) 140 for k, v := range params { 141 pa[k] = v[0] 142 keys = append(keys, k) 143 } 144 145 sort.Strings(keys) 146 147 for _, key := range keys { 148 if key == "signature" { 149 continue 150 } 151 152 val := pa[key] 153 if key != "" && val != "" { 154 b.WriteString(key) 155 b.WriteString(val) 156 } 157 } 158 159 stringToSign := fmt.Sprintf("%v\n%v\n%v\n", method, b.String(), RequestURL) 160 161 sha256 := sha256.New 162 hash := hmac.New(sha256, []byte(appsecret)) 163 hash.Write([]byte(stringToSign)) 164 return base64.StdEncoding.EncodeToString(hash.Sum(nil)) 165 }