merge: 合并OMC分支

This commit is contained in:
TsMask
2024-07-10 14:18:48 +08:00
parent 17c0011c6b
commit 625ed57a50
260 changed files with 9167 additions and 14857 deletions

View File

@@ -2,56 +2,24 @@ package security
import (
"encoding/json"
"fmt"
"image/color"
"io"
"net/http"
"strconv"
"strings"
"time"
"nms_cxy/features/security/service"
sysConfigService "nms_cxy/features/sys_config/service"
"nms_cxy/lib/core/account"
"nms_cxy/lib/core/cache"
"nms_cxy/lib/core/constants/cachekey"
"nms_cxy/lib/core/utils/ctx"
"nms_cxy/lib/core/vo/result"
"nms_cxy/lib/dborm"
"nms_cxy/lib/global"
"nms_cxy/lib/log"
"nms_cxy/lib/oauth"
"nms_cxy/lib/services"
"nms_cxy/omc/config"
srcConfig "nms_cxy/src/framework/config"
"nms_cxy/src/framework/i18n"
"nms_cxy/src/framework/redis"
"github.com/mojocn/base64Captcha"
)
var (
UriOauthToken2 = config.DefaultUriPrefix + "/securityManagement/{apiVersion}/{elementTypeValue}/token2"
UriOauthToken = config.DefaultUriPrefix + "/securityManagement/{apiVersion}/{elementTypeValue}/token"
UriOauthHandshake = config.DefaultUriPrefix + "/securityManagement/{apiVersion}/{elementTypeValue}/handshake"
CustomUriOauthToken = config.UriPrefix + "/securityManagement/{apiVersion}/{elementTypeValue}/token"
CustomUriOauthHandshake = config.UriPrefix + "/securityManagement/{apiVersion}/{elementTypeValue}/handshake"
// 系统登录
UriLogin = config.DefaultUriPrefix + "/securityManagement/{apiVersion}/login"
CustomUriLogin = config.UriPrefix + "/securityManagement/{apiVersion}/login"
// 获取验证码
UriCaptchaImage = config.DefaultUriPrefix + "/securityManagement/{apiVersion}/captchaImage"
CustomUriCaptchaImage = config.UriPrefix + "/securityManagement/{apiVersion}/captchaImage"
// 登录用户信息
UriUserInfo = config.DefaultUriPrefix + "/securityManagement/{apiVersion}/getUserInfo"
CustomUriUserInfo = config.UriPrefix + "/securityManagement/{apiVersion}/getUserInfo"
// 登录用户路由信息
UriRouters = config.DefaultUriPrefix + "/securityManagement/{apiVersion}/getRouters"
CustomUriRouters = config.UriPrefix + "/securityManagement/{apiVersion}/getRouters"
)
func LoginFromOMC(w http.ResponseWriter, r *http.Request) {
@@ -136,14 +104,8 @@ func LoginFromOMC(w http.ResponseWriter, r *http.Request) {
}
if user != nil {
// 缓存用户信息
account.CacheLoginUser(user)
redis.SetByExpire("", "session_token", token, time.Second*1800)
// 角色权限集合,管理员拥有所有权限
userId := fmt.Sprint(user.Id)
isAdmin := srcConfig.IsAdmin(userId)
roles, perms := service.NewServiceAccount.RoleAndMenuPerms(userId, isAdmin)
services.ResponseStatusOK200LoginWhitRP(w, token, user, roles, perms)
empty := []string{}
services.ResponseStatusOK200LoginWhitRP(w, token, user, empty, empty)
return
}
services.ResponseBadRequest400IncorrectLogin(w)
@@ -225,212 +187,3 @@ func HandshakeFromOMC(w http.ResponseWriter, r *http.Request) {
}
services.ResponseStatusOK200Null(w)
}
// 系统登录
//
// POST /login
func LoginOMC(w http.ResponseWriter, r *http.Request) {
log.Info("LoginOMC processing... ")
var body struct {
Username string `json:"username" binding:"required"` // Username 用户名
Password string `json:"password" binding:"required"` // Password 用户密码
Code string `json:"code"` // Code 验证码
UUID string `json:"uuid"` // UUID 验证码唯一标识
}
err := ctx.ShouldBindJSON(r, &body)
if err != nil {
log.Error("Invalid Json Format")
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
return
}
// response 400-5
if body.Username == "" || body.Password == "" {
log.Error("Wrong parameter value")
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
return
}
// 校验验证码
// 从数据库配置获取验证码开关 true开启false关闭
captchaEnabledStr := sysConfigService.NewServiceSysConfig.SelectConfigValueByKey("sys.account.captchaEnabled")
captchaEnabled, err := strconv.ParseBool(captchaEnabledStr)
if err != nil {
captchaEnabled = false
}
if captchaEnabled {
if body.Code == "" || body.UUID == "" {
log.Error("Authentication failed, mismatch captcha")
ctx.JSON(w, 400, result.CodeMsg(400, "Verification code information error"))
return
}
verifyKey := cachekey.CAPTCHA_CODE_KEY + body.UUID
captcha, ok := cache.GetLocalTTL(verifyKey)
if captcha == nil || !ok {
log.Error("Authentication failed, captcha emtry")
ctx.JSON(w, 400, result.CodeMsg(400, "The verification code has expired"))
return
}
cache.DeleteLocalTTL(verifyKey)
if captcha.(string) != body.Code {
log.Error("Authentication failed, not match captcha")
ctx.JSON(w, 400, result.CodeMsg(400, "Verification code error"))
return
}
}
validUser, user, err := dborm.XormCheckLoginUser(body.Username, body.Password, config.GetYamlConfig().Auth.Crypt)
if !validUser || err != nil {
// response 400-4
log.Error("Authentication failed, mismatch user or password")
ctx.JSON(w, 400, result.CodeMsg(400, err.Error()))
return
}
token := oauth.GenRandToken("omc") // Generate new token to session ID
sourceAddr := r.RemoteAddr[:strings.Index(r.RemoteAddr, ":")]
affected, err := dborm.XormInsertSession(body.Username, sourceAddr, token,
config.GetExpiresFromConfig(), config.GetYamlConfig().Auth.Session)
if err != nil {
log.Error("Failed to XormInsertSession:", err)
if affected == -1 {
services.ResponseForbidden403MultiLoginNotAllowed(w)
} else {
services.ResponseBadRequest400IncorrectLogin(w)
}
return
}
if user != nil {
// 缓存用户信息
account.CacheLoginUser(user)
redis.SetByExpire("", "session_token", token, time.Second*1800)
ctx.JSON(w, 200, result.OkData(map[string]any{
"accessToken": token,
}))
return
}
ctx.JSON(w, 200, result.Err(nil))
}
// 获取验证码
//
// GET /captchaImage
func CaptchaImage(w http.ResponseWriter, r *http.Request) {
configService := sysConfigService.NewServiceSysConfig
// 从数据库配置获取验证码开关 true开启false关闭
captchaEnabledStr := configService.SelectConfigValueByKey("sys.account.captchaEnabled")
captchaEnabled, err := strconv.ParseBool(captchaEnabledStr)
if err != nil {
captchaEnabled = false
}
if !captchaEnabled {
ctx.JSON(w, 200, result.Ok(map[string]any{
"captchaEnabled": captchaEnabled,
}))
return
}
// 生成唯一标识
verifyKey := ""
data := map[string]any{
"captchaEnabled": captchaEnabled,
"uuid": "",
"img": "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",
}
// char 字符验证
driverCaptcha := &base64Captcha.DriverString{
//Height png height in pixel.
Height: 40,
// Width Captcha png width in pixel.
Width: 120,
//NoiseCount text noise count.
NoiseCount: 4,
//Length random string length.
Length: 4,
//Source is a unicode which is the rand string from.
Source: "023456789abcdefghjkmnprstuvwxyz",
//ShowLineOptions := OptionShowHollowLine | OptionShowSlimeLine | OptionShowSineLine .
ShowLineOptions: base64Captcha.OptionShowHollowLine,
//BgColor captcha image background color (optional)
BgColor: &color.RGBA{
R: 250,
G: 250,
B: 250,
A: 255, // 不透明
},
}
// 验证码生成
id, question, answer := driverCaptcha.GenerateIdQuestionAnswer()
// 验证码表达式解析输出
item, err := driverCaptcha.DrawCaptcha(question)
if err != nil {
log.Infof("Generate Id Question Answer %s : %v", question, err)
} else {
data["uuid"] = id
data["img"] = item.EncodeB64string()
verifyKey = cachekey.CAPTCHA_CODE_KEY + id
cache.SetLocalTTL(verifyKey, answer, 120*time.Second)
}
// 本地开发下返回验证码结果,方便接口调试
// text, ok := cache.GetLocalTTL(verifyKey)
// if ok {
// data["text"] = text.(string)
// }
ctx.JSON(w, 200, result.Ok(data))
}
// 登录用户信息
func UserInfo(w http.ResponseWriter, r *http.Request) {
loginUser, err := ctx.LoginUser(r)
if err != nil {
ctx.JSON(w, 200, result.OkData(err.Error()))
}
// 角色权限集合,管理员拥有所有权限
userId := fmt.Sprint(loginUser.UserID)
isAdmin := srcConfig.IsAdmin(userId)
roles, perms := service.NewServiceAccount.RoleAndMenuPerms(userId, isAdmin)
ctx.JSON(w, 200, result.OkData(map[string]any{
"user": loginUser.User,
"roles": roles,
"permissions": perms,
}))
}
// 登录用户路由信息
func Routers(w http.ResponseWriter, r *http.Request) {
userID := ctx.LoginUserToUserID(r)
// 前端路由,管理员拥有所有
isAdmin := srcConfig.IsAdmin(userID)
buildMenus := service.NewServiceAccount.RouteMenus(userID, isAdmin)
ctx.JSON(w, 200, result.OkData(buildMenus))
}
// 鉴权登录接口
var UriOauthToken = "/api/rest/securityManagement/{apiVersion}/oauth/token"
// 鉴权登录接口
func OauthToken(w http.ResponseWriter, r *http.Request) {
language := ctx.AcceptLanguage(r)
var querys struct {
Group string `form:"group" binding:"required"`
Type string `form:"type" binding:"omitempty,oneof=node edge combo"`
}
if err := ctx.ShouldBindQuery(r, &querys); err != nil {
ctx.JSON(w, 400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
apiVersion := ctx.Param(r, "apiVersion")
if apiVersion == "v1" {
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
return
}
ctx.JSON(w, 400, result.CodeMsg(400, "parameter error"))
}

View File

@@ -1,56 +0,0 @@
package service
import (
menuService "nms_cxy/features/sys_menu/service"
roleService "nms_cxy/features/sys_role/service"
userService "nms_cxy/features/sys_user/service"
"nms_cxy/lib/core/utils/parse"
"nms_cxy/lib/core/vo"
)
// 实例化服务层 ServiceAccount 结构体
var NewServiceAccount = &ServiceAccount{
sysUserService: userService.NewServiceSysUser,
sysRoleService: roleService.NewServiceSysRole,
sysMenuService: menuService.NewServiceSysMenu,
}
// 账号身份操作服务 服务层处理
type ServiceAccount struct {
// 用户信息服务
sysUserService *userService.ServiceSysUser
// 角色服务
sysRoleService *roleService.ServiceSysRole
// 菜单服务
sysMenuService *menuService.ServiceSysMenu
}
// RoleAndMenuPerms 角色和菜单数据权限
func (s *ServiceAccount) RoleAndMenuPerms(userId string, isAdmin bool) ([]string, []string) {
if isAdmin {
return []string{"admin"}, []string{"*:*:*"}
} else {
// 角色key
roleGroup := []string{}
roles := s.sysRoleService.SelectRoleListByUserId(userId)
for _, role := range roles {
roleGroup = append(roleGroup, role.RoleKey)
}
// 菜单权限key
perms := s.sysMenuService.SelectMenuPermsByUserId(userId)
return parse.RemoveDuplicates(roleGroup), parse.RemoveDuplicates(perms)
}
}
// RouteMenus 前端路由所需要的菜单
func (s *ServiceAccount) RouteMenus(userId string, isAdmin bool) []vo.Router {
var buildMenus []vo.Router
if isAdmin {
menus := s.sysMenuService.SelectMenuTreeByUserId("*")
buildMenus = s.sysMenuService.BuildRouteMenus(menus, "")
} else {
menus := s.sysMenuService.SelectMenuTreeByUserId(userId)
buildMenus = s.sysMenuService.BuildRouteMenus(menus, "")
}
return buildMenus
}