feat: omc原始代码

This commit is contained in:
TsMask
2024-03-12 10:58:33 +08:00
parent 5133c93971
commit 2d01bb86d1
432 changed files with 66597 additions and 1 deletions

View File

@@ -0,0 +1,77 @@
package midware
import (
"encoding/json"
"net/http"
"strings"
"time"
"nms_nbi/lib/dborm"
"nms_nbi/lib/services"
)
// 登录策略限制登录时间和访问ip范围
func ArrowIPAddr(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ipAddr := strings.Split(r.RemoteAddr, ":")[0]
// 读取配置信息 登录策略设置
result, err := dborm.XormGetConfig("Security", "loginSecurity")
if err != nil {
next.ServeHTTP(w, r)
return
}
data := make(map[string]any)
err = json.Unmarshal([]byte(result["value_json"].(string)), &data)
if err != nil {
next.ServeHTTP(w, r)
return
}
// 开关
switchStr := data["switch"].(string)
if switchStr == "0" {
next.ServeHTTP(w, r)
return
}
ipRange := data["ipRange"].(string)
logintimeRange := data["logintime_range"].(string)
// 检查ip
ips := strings.Split(ipRange, "/")
hasIP := false
for _, ip := range ips {
if ipAddr == ip {
hasIP = true
}
}
if !hasIP {
services.ResponseErrorWithJson(w, 502, "网关登录策略-IP限制: "+ipAddr)
return
}
// 检查开放时间
logintimeRangeArr := strings.Split(logintimeRange, " - ")
// 加载中国时区
loc, _ := time.LoadLocation("Asia/Shanghai")
// 获取当前时间
currentTime := time.Now().In(loc)
// 获取当前日期
currentDate := time.Date(currentTime.Year(), currentTime.Month(), currentTime.Day(), 0, 0, 0, 0, currentTime.Location())
ymd := currentDate.Format("2006-01-02")
// 定义开始时间和结束时间
startTime, _ := time.ParseInLocation("2006-01-02 15:04:05", ymd+" "+logintimeRangeArr[0], loc)
endTime, _ := time.ParseInLocation("2006-01-02 15:04:05", ymd+" "+logintimeRangeArr[1], loc)
// 判断当前时间是否在范围内
if currentTime.After(startTime) && currentTime.Before(endTime) {
next.ServeHTTP(w, r)
} else {
services.ResponseErrorWithJson(w, 502, "网关登录策略-不在开放时间范围内")
}
})
}

228
lib/midware/authorize.go Normal file
View File

@@ -0,0 +1,228 @@
package midware
import (
"context"
"fmt"
"net/http"
"nms_nbi/lib/core/cache"
"nms_nbi/lib/core/utils/ctx"
"nms_nbi/lib/core/vo"
"nms_nbi/lib/core/vo/result"
"nms_nbi/lib/dborm"
commonConstants "nms_nbi/src/framework/constants/common"
tokenUtils "nms_nbi/src/framework/utils/token"
)
// Authorize 用户身份授权认证校验
//
// 只需含有其中角色 "hasRoles": {"xxx"},
//
// 只需含有其中权限 "hasPerms": {"xxx"},
//
// 同时匹配其中角色 "matchRoles": {"xxx"},
//
// 同时匹配其中权限 "matchPerms": {"xxx"},
func Authorize(options map[string][]string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 获取请求头标识信息
tokenStr := ctx.Authorization(r)
// 获取请求头标识信息-旧头
accessToken := r.Header.Get("AccessToken")
if tokenStr == "" && accessToken != "" {
// 验证令牌 == 这里直接查数据库session
if !dborm.XormExistValidToken(accessToken, 0) {
ctx.JSON(w, 401, result.CodeMsg(401, "Invalid identity authorization valid error"))
return
}
se, err := dborm.XormUpdateSessionShakeTime(accessToken)
if err != nil {
ctx.JSON(w, 401, result.CodeMsg(401, "Invalid identity authorization shake error"))
return
}
// 获取缓存的用户信息
data, ok := cache.GetLocalTTL(se.AccountId)
if data == nil || !ok {
ctx.JSON(w, 401, result.CodeMsg(401, "Invalid identity authorization info error"))
return
}
loginUser := data.(vo.LoginUser)
// 登录用户角色权限校验
if options != nil {
var roles []string
for _, item := range loginUser.User.Roles {
roles = append(roles, item.RoleKey)
}
perms := loginUser.Permissions
verifyOk := verifyRolePermission(roles, perms, options)
if !verifyOk {
msg := fmt.Sprintf("Unauthorized access %s %s", r.Method, r.RequestURI)
ctx.JSON(w, 403, result.CodeMsg(403, msg))
return
}
}
// 在请求的 Context 中存储数据
rContext := r.Context()
rContext = context.WithValue(rContext, ctx.ContextKey(commonConstants.CTX_LOGIN_USER), loginUser)
// 继续处理请求
next.ServeHTTP(w, r.WithContext(rContext))
return
}
// 获取请求头标识信息
if tokenStr == "" {
ctx.JSON(w, 401, result.CodeMsg(401, "Invalid identity authorization token error"))
return
}
// 验证令牌
claims, err := tokenUtils.Verify(tokenStr)
if err != nil {
ctx.JSON(w, 401, result.CodeMsg(401, "Invalid identity authorization valid error"))
return
}
// 获取缓存的用户信息
loginUser := tokenUtils.LoginUser(claims)
if loginUser.UserID == "" {
ctx.JSON(w, 401, result.CodeMsg(401, "Invalid identity authorization shake error"))
return
}
// 检查刷新有效期后存入上下文
tokenUtils.RefreshIn(&loginUser)
// 登录用户角色权限校验
if options != nil {
var roles []string
for _, item := range loginUser.User.Roles {
roles = append(roles, item.RoleKey)
}
perms := loginUser.Permissions
verifyOk := verifyRolePermission(roles, perms, options)
if !verifyOk {
msg := fmt.Sprintf("Unauthorized access %s %s", r.Method, r.RequestURI)
ctx.JSON(w, 403, result.CodeMsg(403, msg))
return
}
}
// 在请求的 Context 中存储数据
rContext := r.Context()
rContext = context.WithValue(rContext, ctx.ContextKey(commonConstants.CTX_LOGIN_USER), loginUser)
// 继续处理请求
next.ServeHTTP(w, r.WithContext(rContext))
})
}
}
// verifyRolePermission 校验角色权限是否满足
//
// roles 角色字符数组
//
// perms 权限字符数组
//
// options 参数
func verifyRolePermission(roles, perms []string, options map[string][]string) bool {
// 直接放行 管理员角色或任意权限
if contains(roles, "admin") || contains(perms, "*:*:*") {
return true
}
opts := make([]bool, 4)
// 只需含有其中角色
hasRole := false
if arr, ok := options["hasRoles"]; ok && len(arr) > 0 {
hasRole = some(roles, arr)
opts[0] = true
}
// 只需含有其中权限
hasPerms := false
if arr, ok := options["hasPerms"]; ok && len(arr) > 0 {
hasPerms = some(perms, arr)
opts[1] = true
}
// 同时匹配其中角色
matchRoles := false
if arr, ok := options["matchRoles"]; ok && len(arr) > 0 {
matchRoles = every(roles, arr)
opts[2] = true
}
// 同时匹配其中权限
matchPerms := false
if arr, ok := options["matchPerms"]; ok && len(arr) > 0 {
matchPerms = every(perms, arr)
opts[3] = true
}
// 同时判断 含有其中
if opts[0] && opts[1] {
return hasRole || hasPerms
}
// 同时判断 匹配其中
if opts[2] && opts[3] {
return matchRoles && matchPerms
}
// 同时判断 含有其中且匹配其中
if opts[0] && opts[3] {
return hasRole && matchPerms
}
if opts[1] && opts[2] {
return hasPerms && matchRoles
}
return hasRole || hasPerms || matchRoles || matchPerms
}
// contains 检查字符串数组中是否包含指定的字符串
func contains(arr []string, target string) bool {
for _, str := range arr {
if str == target {
return true
}
}
return false
}
// some 检查字符串数组中含有其中一项
func some(origin []string, target []string) bool {
has := false
for _, t := range target {
for _, o := range origin {
if t == o {
has = true
break
}
}
if has {
break
}
}
return has
}
// every 检查字符串数组中同时包含所有项
func every(origin []string, target []string) bool {
match := true
for _, t := range target {
found := false
for _, o := range origin {
if t == o {
found = true
break
}
}
if !found {
match = false
break
}
}
return match
}

66
lib/midware/cors.go Normal file
View File

@@ -0,0 +1,66 @@
package midware
import (
"net/http"
"strings"
)
// Cors 跨域
func Cors(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 设置Vary头部
w.Header().Set("Vary", "Origin")
w.Header().Set("Keep-Alive", "timeout=5")
requestOrigin := r.Header.Get("Origin")
if requestOrigin == "" {
next.ServeHTTP(w, r)
return
}
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Credentials", "true")
// OPTIONS
if r.Method == "OPTIONS" {
requestMethod := r.Header.Get("Access-Control-Request-Method")
if requestMethod == "" {
next.ServeHTTP(w, r)
return
}
// 响应最大时间值
w.Header().Set("Access-Control-Max-Age", "31536000")
// 允许方法
allowMethods := []string{
"OPTIONS",
"HEAD",
"GET",
"POST",
"PUT",
"DELETE",
"PATCH",
}
w.Header().Set("Access-Control-Allow-Methods", strings.Join(allowMethods, ","))
// 允许请求头
allowHeaders := []string{
"Accesstoken",
"Content-Type",
"operationtype",
}
w.Header().Set("Access-Control-Allow-Headers", strings.Join(allowHeaders, ","))
w.WriteHeader(204)
return
}
// 暴露请求头
exposeHeaders := []string{"X-RepeatSubmit-Rest", "AccessToken"}
w.Header().Set("Access-Control-Expose-Headers", strings.Join(exposeHeaders, ","))
next.ServeHTTP(w, r)
})
}

80
lib/midware/midhandle.go Normal file
View File

@@ -0,0 +1,80 @@
package midware
import (
"net/http"
"strings"
"nms_nbi/lib/log"
"nms_nbi/lib/services"
tokenConst "nms_nbi/src/framework/constants/token"
"github.com/gorilla/mux"
)
func LoggerTrace(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Do stuff here
log.Trace("Http Trace Info:")
log.Trace(" From Host:", r.RemoteAddr)
log.Trace(" To Host:", r.Host)
log.Debug(" RequestUri:", r.RequestURI)
log.Trace(" Method:", r.Method)
log.Trace(" Proto:", r.Proto)
log.Trace(" ContentLength:", r.ContentLength)
log.Trace(" User-Agent:", r.Header.Get("User-Agent"))
log.Trace(" Content-Type:", r.Header.Get("Content-Type"))
log.Trace(" AccessToken:", r.Header.Get("AccessToken"))
log.Trace(" Authorization:", r.Header.Get(tokenConst.HEADER_KEY))
log.Trace("Trace End=====")
//body, _ := io.ReadAll(io.LimitReader(r.Body, global.RequestBodyMaxLen))
// nop-close to ready r.Body !!!
//r.Body = ioutil.NopCloser(bytes.NewReader(body))
//log.Trace("Body:", string(body))
// Call the next handler, which can be another middleware in the chain, or the final handler.
// if r.Method == "OPTIONS" {
// services.ResponseStatusOK201Accepted(w)
// return
// }
next.ServeHTTP(w, r)
})
}
// 已禁用
func OptionProcess(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == "OPTIONS" {
services.ResponseStatusOK201Accepted(w)
return
}
next.ServeHTTP(w, r)
})
}
// 已禁用
func CheckPermission(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("AccessToken")
vars := mux.Vars(r)
management := vars["managedType"]
element := vars["elementTypeValue"]
object := vars["objectTypeValue"]
pack := "*"
if token != "" && element != "oauth" {
log.Debugf("token:%s, method:%s, management:%s, element:%s, object:%s, pack:%s", token, r.Method, management, element, object, pack)
exist, err := services.CheckUserPermission(token, strings.ToLower(r.Method), management, element, object, pack)
if err != nil {
log.Error("Failed to get permission:", err)
services.ResponseForbidden403NotPermission(w)
return
}
if !exist {
log.Error("Not permission!")
services.ResponseForbidden403NotPermission(w)
return
}
}
next.ServeHTTP(w, r)
})
}

68
lib/midware/mml_log.go Normal file
View File

@@ -0,0 +1,68 @@
package midware
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"time"
"nms_nbi/lib/core/datasource"
"nms_nbi/lib/core/utils/ctx"
"nms_nbi/lib/core/utils/date"
"nms_nbi/lib/dborm"
"nms_nbi/lib/log"
)
// LogMML mml操作日志搜集
func LogMML(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 读取请求体内容
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
// 解析json内参数
var bodyArgs map[string]any
_ = json.Unmarshal(bytes.Clone(body), &bodyArgs)
// 将请求体内容存储在临时缓冲区中
buffer := bytes.NewBuffer(body)
r.Body = io.NopCloser(buffer)
next.ServeHTTP(w, r)
// 收尾存入数据库的参数
mmlCmd := bodyArgs["mml"].([]any)[0]
ipAddr := strings.Split(r.RemoteAddr, ":")[0]
neType := ctx.Param(r, "elementTypeValue")
neId := ctx.GetQuery(r, "ne_id")
timeStr := date.ParseDateToStr(time.Now(), date.YYYY_MM_DD_HH_MM_SS)
// 响应内容长度和状态码作为结果
str := strings.TrimSuffix(fmt.Sprintf("%v", w), "}")
strArr := strings.Split(str, " ")
size := strArr[1]
status := strArr[2]
contentType := w.Header().Get("Content-Type")
resultStr := fmt.Sprintf(`{"status":"%s","size":"%s","content-type":"%s"}`, status, size, contentType)
// 用户名
username := "-"
accessToken := r.Header.Get("AccessToken")
if accessToken != "" {
// 验证令牌 == 这里直接查数据库session
se, _ := dborm.XormUpdateSessionShakeTime(accessToken)
username = se.AccountId
}
// 执行插入
sql := "insert into mml_log (user,ip,ne_type,ne_id,mml,result,log_time)values(?,?,?,?,?,?,?)"
_, sqlerr := datasource.ExecDB("", sql, []any{username, ipAddr, neType, neId, mmlCmd, resultStr, timeStr})
if sqlerr != nil {
log.Errorf("insert row : %v", err.Error())
}
})
}

143
lib/midware/operate_log.go Normal file
View File

@@ -0,0 +1,143 @@
package midware
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"reflect"
"runtime"
"strings"
"time"
"nms_nbi/lib/core/utils/parse"
"nms_nbi/lib/dborm"
"nms_nbi/src/framework/constants/common"
"nms_nbi/src/framework/middleware/collectlogs"
"nms_nbi/src/framework/utils/ip2region"
"nms_nbi/src/modules/system/model"
"nms_nbi/src/modules/system/service"
)
// 敏感属性字段进行掩码
var maskProperties []string = []string{
"password",
"oldPassword",
"newPassword",
"confirmPassword",
}
// LogOperate 操作日志搜集
func LogOperate(options collectlogs.Options) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
startTime := time.Now()
// 只对前端发送的数据进行记录
appCodeStr := r.Header.Get("X-App-Code")
if appCodeStr == "" {
next.ServeHTTP(w, r)
return
}
// 函数名
funcName := runtime.FuncForPC(reflect.ValueOf(next).Pointer()).Name()
lastDotIndex := strings.LastIndex(funcName, "/")
funcName = funcName[lastDotIndex+1:]
// 用户名
username := "-"
accessToken := r.Header.Get("AccessToken")
if accessToken != "" {
// 验证令牌 == 这里直接查数据库session
se, _ := dborm.XormUpdateSessionShakeTime(accessToken)
username = se.AccountId
}
// 解析ip地址
ip := strings.Split(r.RemoteAddr, ":")[0]
ipaddr := ip2region.ClientIP(ip)
location := ip2region.RealAddressByIp(ipaddr)
// 操作日志记录
operLog := model.SysLogOperate{
Title: options.Title,
BusinessType: options.BusinessType,
OperatorType: collectlogs.OPERATOR_TYPE_MANAGE,
Method: funcName,
OperURL: r.RequestURI,
RequestMethod: r.Method,
OperIP: ipaddr,
OperLocation: location,
OperName: username,
DeptName: "",
}
// 是否需要保存request参数和值
if options.IsSaveRequestData && (r.Method == "POST" || r.Method == "PUT") {
// 读取请求体内容
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
// 解析json内参数
var bodyArgs map[string]any
_ = json.Unmarshal(bytes.Clone(body), &bodyArgs)
// 将请求体内容存储在临时缓冲区中
buffer := bytes.NewBuffer(body)
r.Body = io.NopCloser(buffer)
params := bodyArgs
for k, v := range params {
// 敏感属性字段进行掩码
for _, s := range maskProperties {
if s == k {
params[k] = parse.SafeContent(v.(string))
break
}
}
}
jsonStr, _ := json.Marshal(params)
paramsStr := string(jsonStr)
if len(paramsStr) > 2000 {
paramsStr = paramsStr[:2000]
}
operLog.OperParam = paramsStr
}
next.ServeHTTP(w, r)
// 响应内容长度和状态码作为结果
str := strings.TrimSuffix(fmt.Sprintf("%v", w), "}")
strArr := strings.Split(str, " ")
size := strArr[1]
status := strArr[2]
// 响应状态
if status == "200" || status == "204" {
operLog.Status = common.STATUS_YES
} else {
operLog.Status = common.STATUS_NO
}
// 是否需要保存response参数和值
if options.IsSaveResponseData {
contentDisposition := w.Header().Get("Content-Disposition")
contentType := w.Header().Get("Content-Type")
content := contentType + contentDisposition
msg := fmt.Sprintf(`{"status":"%s","size":"%s","content-type":"%s"}`, status, size, content)
operLog.OperMsg = msg
}
// 日志记录时间
duration := time.Since(startTime)
operLog.CostTime = duration.Milliseconds()
operLog.OperTime = time.Now().UnixMilli()
// 保存操作记录到数据库
service.NewSysLogOperateImpl.InsertSysLogOperate(operLog)
})
}
}