middleware.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. package middleware
  2. import (
  3. "context"
  4. "fmt"
  5. "github.com/google/uuid"
  6. "github.com/pkg/errors"
  7. "github.com/sirupsen/logrus"
  8. "go-micro.dev/v4"
  9. "go-micro.dev/v4/auth"
  10. "go-micro.dev/v4/metadata"
  11. "go-micro.dev/v4/server"
  12. "sghgogs.com/sghblog/authorization-service/utils/authutil"
  13. "sghgogs.com/sghblog/common"
  14. "sghgogs.com/sghblog/common/errorcode"
  15. "sort"
  16. "strings"
  17. )
  18. const (
  19. // BearerScheme used for Authorization header.
  20. BearerScheme = "Bearer "
  21. // ScopePublic is the scope applied to a rule to allow access to the public.
  22. ScopePublic = ""
  23. // ScopeAccount is the scope applied to a rule to limit to users with any valid account.
  24. ScopeAccount = "*"
  25. name = "authorizationservice"
  26. )
  27. var (
  28. // ErrInvalidToken is when the token provided is not valid.
  29. ErrInvalidToken = errors.New("invalid token provided")
  30. // ErrForbidden is when a user does not have the necessary scope to access a resource.
  31. ErrForbidden = errors.New("resource forbidden")
  32. )
  33. // type Access int
  34. const (
  35. // AccessGranted to a resource.
  36. AccessGranted auth.Access = iota
  37. // AccessDenied to a resource.
  38. AccessDenied
  39. )
  40. func Verify(rules []*auth.Rule, acc *auth.Account, res *auth.Resource) error {
  41. // the rule is only to be applied if the type matches the resource or is catch-all (*)
  42. validTypes := []string{"*", res.Type}
  43. // the rule is only to be applied if the name matches the resource or is catch-all (*)
  44. validNames := []string{"*", res.Name}
  45. // rules can have wildcard excludes on endpoints since this can also be a path for web services,
  46. // e.g. /foo/* would include /foo/bar. We also want to check for wildcards and the exact endpoint
  47. validEndpoints := []string{"*", res.Endpoint}
  48. if comps := strings.Split(res.Endpoint, "/"); len(comps) > 1 {
  49. for i := 1; i < len(comps)+1; i++ {
  50. wildcard := fmt.Sprintf("%v/*", strings.Join(comps[0:i], "/"))
  51. validEndpoints = append(validEndpoints, wildcard)
  52. }
  53. }
  54. // filter the rules to the ones which match the criteria above
  55. filteredRules := make([]*auth.Rule, 0)
  56. for _, rule := range rules {
  57. if !include(validTypes, rule.Resource.Type) {
  58. continue
  59. }
  60. if !include(validNames, rule.Resource.Name) {
  61. continue
  62. }
  63. if !include(validEndpoints, rule.Resource.Endpoint) {
  64. continue
  65. }
  66. filteredRules = append(filteredRules, rule)
  67. }
  68. // sort the filtered rules by priority, highest to lowest
  69. sort.SliceStable(filteredRules, func(i, j int) bool {
  70. return filteredRules[i].Priority > filteredRules[j].Priority
  71. })
  72. // loop through the rules and check for a rule which applies to this account
  73. for _, rule := range filteredRules {
  74. // a blank scope indicates the rule applies to everyone, even nil accounts
  75. if rule.Scope == ScopePublic && rule.Access == AccessDenied {
  76. return ErrForbidden
  77. } else if rule.Scope == ScopePublic && rule.Access == AccessGranted {
  78. return nil
  79. }
  80. // all further checks require an account
  81. if acc == nil {
  82. continue
  83. }
  84. // this rule applies to any account
  85. if rule.Scope == ScopeAccount && rule.Access == AccessDenied {
  86. return ErrForbidden
  87. } else if rule.Scope == ScopeAccount && rule.Access == AccessGranted {
  88. return nil
  89. }
  90. // 去掉首尾的方括号
  91. // if the account has the necessary scope
  92. if include(acc.Scopes, rule.Scope) && rule.Access == AccessDenied {
  93. return ErrForbidden
  94. } else if include(acc.Scopes, rule.Scope) && rule.Access == AccessGranted {
  95. return nil
  96. }
  97. }
  98. // if no rules matched then return forbidden
  99. return ErrForbidden
  100. }
  101. // include is a helper function which checks to see if the slice contains the value. includes is
  102. // not case sensitive.
  103. func include(slice []string, val string) bool {
  104. // str := slice
  105. if len(slice) > 0 {
  106. if strings.Contains(slice[0], ",") {
  107. data := strings.Split(slice[0], ",")
  108. // 打印结果
  109. for _, s := range data {
  110. if s == "super_admin" {
  111. return true
  112. }
  113. if strings.EqualFold(s, val) {
  114. return true
  115. }
  116. }
  117. // 判断超级管理员
  118. } else {
  119. // 判断超级管理员
  120. for _, s := range slice {
  121. if s == "super_admin" {
  122. return true
  123. }
  124. if strings.EqualFold(s, val) {
  125. return true
  126. }
  127. }
  128. }
  129. return false
  130. }
  131. return false
  132. }
  133. var (
  134. catchallResource = &auth.Resource{
  135. Type: "*",
  136. Name: "*",
  137. Endpoint: "*",
  138. }
  139. adminLogout = &auth.Resource{
  140. Type: "user",
  141. Name: name,
  142. Endpoint: "CommonService.AdminLogout",
  143. }
  144. adminProfile = &auth.Resource{
  145. Type: "user",
  146. Name: name,
  147. Endpoint: "CommonService.AdminProfile",
  148. }
  149. listAdminRoles = &auth.Resource{
  150. Type: "user",
  151. Name: name,
  152. Endpoint: "AdminRoleService.ListAdminRoles",
  153. }
  154. updateAdminRole = &auth.Resource{
  155. Type: "user",
  156. Name: name,
  157. Endpoint: "AdminRoleService.UpdateAdminRole",
  158. }
  159. createAdminRole = &auth.Resource{
  160. Type: "user",
  161. Name: name,
  162. Endpoint: "AdminRoleService.CreateAdminRole",
  163. }
  164. deleteAdminRole = &auth.Resource{
  165. Type: "user",
  166. Name: name,
  167. Endpoint: "AdminRoleService.DeleteAdminRole",
  168. }
  169. getAdminRole = &auth.Resource{
  170. Type: "user",
  171. Name: name,
  172. Endpoint: "AdminRoleService.GetAdminRole",
  173. }
  174. toggleAdminRole = &auth.Resource{
  175. Type: "user",
  176. Name: name,
  177. Endpoint: "AdminRoleService.ToggleAdminRole",
  178. }
  179. retrieveEnabledRoles = &auth.Resource{
  180. Type: "user",
  181. Name: name,
  182. Endpoint: "AdminRoleService.RetrieveEnabledRoles",
  183. }
  184. getUnassignedAdminRoles = &auth.Resource{
  185. Type: "user",
  186. Name: name,
  187. Endpoint: "AdminUserService.GetUnassignedAdminRoles",
  188. }
  189. getAdminUser = &auth.Resource{
  190. Type: "user",
  191. Name: name,
  192. Endpoint: "AdminUserService.GetAdminUser",
  193. }
  194. createAdminUser = &auth.Resource{
  195. Type: "user",
  196. Name: name,
  197. Endpoint: "AdminUserService.CreateAdminUser",
  198. }
  199. associateAdminUserWithRole = &auth.Resource{
  200. Type: "user",
  201. Name: name,
  202. Endpoint: "AdminUserService.AssociateAdminUserWithRole",
  203. }
  204. updateAdminUser = &auth.Resource{
  205. Type: "user",
  206. Name: name,
  207. Endpoint: "AdminUserService.UpdateAdminUser",
  208. }
  209. revokeAdminUserWithRole = &auth.Resource{
  210. Type: "user",
  211. Name: name,
  212. Endpoint: "AdminUserService.RevokeAdminUserWithRole",
  213. }
  214. listAdminUsers = &auth.Resource{
  215. Type: "user",
  216. Name: name,
  217. Endpoint: "AdminUserService.ListAdminUsers",
  218. }
  219. retrieveEnabledUsers = &auth.Resource{
  220. Type: "user",
  221. Name: name,
  222. Endpoint: "AdminUserService.RetrieveEnabledUsers",
  223. }
  224. deleteAdminUser = &auth.Resource{
  225. Type: "user",
  226. Name: name,
  227. Endpoint: "AdminUserService.DeleteAdminUser",
  228. }
  229. toggleAdminUser = &auth.Resource{
  230. Type: "user",
  231. Name: name,
  232. Endpoint: "AdminUserService.ToggleAdminUser",
  233. }
  234. // catchallResource
  235. rulesItems = []*auth.Rule{
  236. // {Scope: "*", Resource: catchallResource}, toggleAdminRole
  237. {Scope: "admin", Resource: adminLogout, ID: uuid.New().String(), Priority: 1},
  238. {Scope: "admin", Resource: adminProfile, ID: uuid.New().String(), Priority: 1},
  239. {Scope: "admin", Resource: listAdminRoles, ID: uuid.New().String(), Priority: 1},
  240. {Scope: "admin", Resource: updateAdminRole, ID: uuid.New().String(), Priority: 1},
  241. {Scope: "admin", Resource: createAdminRole, ID: uuid.New().String(), Priority: 1},
  242. {Scope: "admin", Resource: toggleAdminRole, ID: uuid.New().String(), Priority: 1},
  243. {Scope: "admin", Resource: getUnassignedAdminRoles, ID: uuid.New().String(), Priority: 1},
  244. {Scope: "admin", Resource: deleteAdminRole, ID: uuid.New().String(), Priority: 1},
  245. {Scope: "admin", Resource: getAdminRole, ID: uuid.New().String(), Priority: 1},
  246. {Scope: "admin", Resource: retrieveEnabledRoles, ID: uuid.New().String(), Priority: 1},
  247. {Scope: "admin", Resource: getAdminUser, ID: uuid.New().String(), Priority: 1},
  248. {Scope: "admin", Resource: createAdminUser, ID: uuid.New().String(), Priority: 1},
  249. {Scope: "admin", Resource: associateAdminUserWithRole, ID: uuid.New().String(), Priority: 1},
  250. {Scope: "admin", Resource: updateAdminUser, ID: uuid.New().String(), Priority: 1},
  251. {Scope: "admin", Resource: revokeAdminUserWithRole, ID: uuid.New().String(), Priority: 1},
  252. {Scope: "admin", Resource: listAdminUsers, ID: uuid.New().String(), Priority: 1},
  253. {Scope: "admin", Resource: retrieveEnabledUsers, ID: uuid.New().String(), Priority: 1},
  254. {Scope: "admin", Resource: deleteAdminUser, ID: uuid.New().String(), Priority: 1},
  255. {Scope: "admin", Resource: toggleAdminUser, ID: uuid.New().String(), Priority: 1},
  256. }
  257. )
  258. func NewAuthWrapper(service micro.Service) server.HandlerWrapper {
  259. return func(h server.HandlerFunc) server.HandlerFunc {
  260. return func(ctx context.Context, req server.Request, rsp interface{}) error {
  261. logrus.Infof("[wrapper] server request: %v", req.Endpoint())
  262. if req.Endpoint() == "CommonService.AdminLogin" {
  263. return h(ctx, req, rsp)
  264. }
  265. if req.Endpoint() == "AdminUserService.GetAdminUserAssociatedRoles" {
  266. return h(ctx, req, rsp)
  267. }
  268. // Fetch metadata from context (request headers).
  269. md, b := metadata.FromContext(ctx)
  270. if !b {
  271. return errorcode.Unauthorized("authorization service", common.ErrorMessage[common.UnauthorizedErrorCode])
  272. // errors.New("no metadata found")
  273. }
  274. // local ip of service
  275. fmt.Println("local ip is", md["Local"])
  276. // remote ip of caller
  277. fmt.Println("remote ip is", md["Remote"])
  278. // Get auth header.
  279. authHeader, ok := md["Authorization"]
  280. if !ok || !strings.HasPrefix(authHeader, auth.BearerScheme) {
  281. logrus.Error("no auth token provided")
  282. return errorcode.Unauthorized("authorization service", common.ErrorMessage[common.UnauthorizedErrorCode])
  283. }
  284. // Extract auth token.
  285. token := strings.TrimPrefix(authHeader, auth.BearerScheme)
  286. // Extract account from token.
  287. token = strings.TrimSpace(token)
  288. a := service.Options().Auth
  289. acc, err := a.Inspect(token)
  290. fmt.Println("acc", acc)
  291. if err != nil {
  292. return errorcode.Unauthorized("authorization service", common.ErrorMessage[common.TokenInvalidErrorCode])
  293. }
  294. // 校验redis 存储数据
  295. blacklisted, err := authutil.JWTAuthService.IsBlacklisted(token)
  296. if err == nil && blacklisted {
  297. return errorcode.Unauthorized("authorization service", common.ErrorMessage[common.ExpiredLonInAgainErrorCode])
  298. }
  299. // Create resource for current endpoint from request headers.
  300. currentResource := auth.Resource{
  301. Type: "user",
  302. Name: md["Micro-Service"],
  303. Endpoint: md["Micro-Endpoint"],
  304. }
  305. // Verify if account has access. 验证帐户是否具有访问权限。
  306. if err = Verify(rulesItems, acc, &currentResource); err != nil {
  307. return errorcode.Unauthorized("authorization service", common.ErrorMessage[common.NoAccessErrorCode])
  308. }
  309. // 验证通过后记录操作日志
  310. logrus.Infof("User %s is performing operation %s body %v", acc.ID, req.Endpoint(), req.Body())
  311. return h(ctx, req, rsp)
  312. }
  313. }
  314. }