1 Star 0 Fork 0

tomatomeatman/GolangRepository

加入 Gitee
与超过 1400万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
Interceptor.go 13.16 KB
一键复制 编辑 原始数据 按行查看 历史
tomatomeatman 提交于 2024-10-15 13:51 +08:00 . 修改统一返回对象名称
package interceptor
import (
"encoding/json"
"net/http"
"os"
"path/filepath"
"strings"
"sync"
"gitee.com/tomatomeatman/golang-repository/bricks/model/msgentity"
"gitee.com/tomatomeatman/golang-repository/bricks/system/bridge"
"gitee.com/tomatomeatman/golang-repository/bricks/utils/function/urlutil"
"gitee.com/tomatomeatman/golang-repository/bricks/utils/ginutil"
Log "github.com/cihub/seelog"
"gopkg.in/ini.v1"
)
var (
interceptorOnce sync.Once //初始化锁
sysIgnoreUrlByNotLogin = "" //当前系统设置的忽略拦截路径-未登录前 配置app.InterceptorIgnore
sysIgnoreUrlByLogined = "" //当前系统设置的忽略拦截路径-登录后 配置app.InterceptorIgnoreLogined
sysIgnoreUrlByNotLoginList = map[string]string{} //当前系统设置的忽略拦截路径(精确)-未登录前
sysIgnoreUrlByLoginedList = map[string]string{} //当前系统设置的忽略拦截路径(精确)-登录后
sysIgnoreUrlByNotLoginListLike = map[string]string{} //当前系统设置的忽略拦截路径(模糊)-未登录前
sysIgnoreUrlByLoginedListLike = map[string]string{} //当前系统设置的忽略拦截路径(模糊)-登录后
appKey = ""
)
// --拦截器--//
type Interceptor struct{}
// 引入操作
func (ic Interceptor) Import() {
//fmt.Println("无意义,只为了能将模块引入程序")
}
// 初始化
func init() {
go interceptorOnce.Do(interceptor_initVar)
}
// 初始化参数
func interceptor_initVar() {
root := ""
exePath, err := os.Executable()
if err != nil {
root = "."
}
root, _ = filepath.EvalSymlinks(filepath.Dir(exePath))
configFilePath := strings.Replace(root+"/config/app.ini", "\\", "/", -1)
_, err = os.Stat(configFilePath) //os.Stat获取文件信息
if err != nil {
if !os.IsExist(err) {
Log.Error("配置文件不存在", err)
return
}
}
appCfg, err := ini.Load(configFilePath)
if err != nil {
Log.Error("配置文件读取错误", err)
return
}
appSec := appCfg.Section("App")
value, _ := appSec.GetKey("InterceptorIgnore")
if value != nil {
sysIgnoreUrlByNotLogin = value.String()
}
value, _ = appSec.GetKey("InterceptorIgnoreLogined")
if value != nil {
sysIgnoreUrlByLogined = value.String()
}
value, _ = appSec.GetKey("InsideKey")
if value != nil {
appKey = value.String()
}
}
/**
* 拦截操作
* @param ctx
* @return
*/
func (inter Interceptor) Check(ctx ginutil.Context) bool {
interceptorOnce.Do(interceptor_initVar) //防止未初始化结束
if inter.isIgnoresByNotLogin(ctx) { //属于配置文件中的未登录时免拦截路径,因程序可能属于无数据库的情况,所以需要配置文件检查
return true
}
i := inter.checkIgnoreUrl(ctx, ctx.Request.URL.Path, false, "") //验证是否可忽略路径模块,只能验证不用登录也能忽略的路径
if i == 2 { //1:没有使用'忽略路径模块'2:路径为'可忽略路径'3:不是可忽略的路径
return true //属于免登录的免拦截路径则通过
}
switch inter.isInside(ctx) { //判断是否属于内部请求的路径 0:不是内部请求, 1:是内部请求并通过, 2:是内部请求但不通过
case 1: //1:是内部请求并通过
return true
case 2: //2:是内部请求但不通过
return false
default: //0:不是内部请求
break
}
if !inter.validLogin(ctx) { // 用户登录验证
return false
}
sUserType := urlutil.GetParam(ctx.Request, "sLoginUserType", "").(string)
i = inter.checkIgnoreUrl(ctx, ctx.Request.URL.Path, true, sUserType) //验证是否可忽略路径模块,一并验证登录后才能忽略的路径
if i == 2 { //1:没有使用'忽略路径模块'2:路径为'可忽略路径'3:不是可忽略的路径
return true //属于登录后的免拦截路径则通过
}
//没有使用'忽略路径模块'或'不是可忽略的路径',必须交由权限进行控制访问
if !inter.validRight(ctx) { //权限验证
return false
}
return true
}
/**
* 判断是否属于内部请求的路径; 0:不是内部请求, 1:是内部请求并通过, 2:是内部请求但不通过
* 判断内部请求的依据:有内部请求密钥或请求地址为内部请求地址
* @param ctx
* @return
*/
func (inter Interceptor) isInside(ctx ginutil.Context) int {
servletPath := ctx.Request.URL.Path
key := urlutil.GetParam(ctx.Request, "sInsideKey", "").(string) //如果是内部请求,则内部密钥必须存在,从请求头获取内部密钥
//--检查是否属于内部请求路径,如果是内部请求路径则检查访问key是否与当前系统一致,只要一致则可以通过--//
if !strings.Contains(servletPath, "/inside/") { //包含'/inside/'的请求属于内部请求
if key == "" { //不是内部请求路径,又不提供内部请求密钥,则返回"不是内部请求"
return 0 //不是内部请求
}
// appKey := app.ReadConfigKey("App", "InsideKey", "12345").(string)
if (appKey == "") || (key != appKey) { //有提供内部密钥,但是密钥错误,则返回"不是内部请求"
return 0 //不是内部请求
}
//--对非内部请求的路径进行了内部请求操作,使用超管账号--//
urlutil.AddAttrib(ctx.Request, "sLoginUserId", "00000000") //将对应的id赋予请求属性,用于后续请求操作
urlutil.AddAttrib(ctx.Request, "sLoginUserName", "superAdmin") //将对应的sName赋予请求属性,用于后续请求操作
urlutil.AddAttrib(ctx.Request, "sLoginUserNo", "0000") //将对应的sNo赋予请求属性,用于后续请求操作
urlutil.AddAttrib(ctx.Request, "sLoginUserType", "admin") //将对应的sType赋予请求属性,用于后续请求操作
return 1 //内部请求操作
}
// appKey := app.ReadConfigKey("App", "InsideKey", "12345").(string)
if (key == "") || (key != appKey) {
ctx.JSONP(http.StatusOK, msgentity.Err(1000001, "内部请求密钥错误"))
return 2 //密钥不符
}
//--使用内部请求时,一旦没有输入登录密钥则视为超管操作--//
sCookie := urlutil.GetParam(ctx.Request, "sCookie", "").(string) //获取request对象中的参数,优先: 头信息->参数-->属性
if sCookie == "" { //确实没有登录
urlutil.AddAttrib(ctx.Request, "sLoginUserId", "00000000") //将对应的id赋予请求属性,用于后续请求操作
urlutil.AddAttrib(ctx.Request, "sLoginUserName", "superAdmin") //将对应的sName赋予请求属性,用于后续请求操作
urlutil.AddAttrib(ctx.Request, "sLoginUserNo", "0000") //将对应的sNo赋予请求属性,用于后续请求操作
urlutil.AddAttrib(ctx.Request, "sLoginUserType", "admin") //将对应的sType赋予请求属性,用于后续请求操作
} else {
urlutil.AddAttrib(ctx.Request, "sCookie", sCookie) //放入传递的参数
}
return 1
}
/**
* 判断是否属于未登录下免拦截的路径
* @param ctx
* @return
*/
func (inter Interceptor) isIgnoresByNotLogin(ctx ginutil.Context) bool {
servletPath := ctx.Request.URL.Path
if strings.Contains(sysIgnoreUrlByNotLogin, servletPath) { //访问'忽略路径的验证'方法必须跳过
return true
}
if strings.Contains(sysIgnoreUrlByNotLogin, "*") && (len(sysIgnoreUrlByNotLoginList) < 1) {
array := strings.Split(sysIgnoreUrlByNotLogin, ",")
for _, val := range array {
if !strings.Contains(val, "*") {
sysIgnoreUrlByNotLoginList[val] = ""
}
}
}
if strings.Contains(sysIgnoreUrlByNotLogin, "*") && (len(sysIgnoreUrlByNotLoginListLike) < 1) {
array := strings.Split(sysIgnoreUrlByNotLogin, ",")
for _, val := range array {
if !strings.Contains(val, "*") {
continue
}
iEd := strings.Index(val, "*")
val := val[:iEd-1]
sysIgnoreUrlByNotLoginListLike[val] = ""
}
}
if len(sysIgnoreUrlByNotLoginListLike) > 0 {
for key := range sysIgnoreUrlByNotLoginListLike {
if strings.HasPrefix(servletPath, key) {
return true
}
}
}
if len(sysIgnoreUrlByNotLoginList) > 0 {
for key := range sysIgnoreUrlByNotLoginList {
if servletPath == key { //必须完全匹配,注意:ctx.Request.URL.Path并不会带有url后面的参数
return true
}
}
}
return false
}
/**
* 用户登录验证
* @param ctx
* @return
*/
func (inter Interceptor) validLogin(ctx ginutil.Context) bool {
sCookie := urlutil.GetParam(ctx.Request, "sCookie", "").(string) //获取request对象中的参数,优先: 头信息->参数-->属性
if (sCookie == "") || (strings.ToUpper(sCookie) == "NULL") { //如果没有登录密钥,则返回错误
ctx.JSONP(http.StatusOK, msgentity.Err(1000002, "您还没有登录或登录已超时,请重新登录!"))
return false
}
//--检查登录密钥--//
me := bridge.LoginController{}.Check(ctx).(*msgentity.MsgEntity)
// me := ginutil.CallFuncByUrl("/login/check", ginutil.POST, ctx).(*msgentity.MsgEntity)
if !me.Gsuccess {
ctx.JSONP(http.StatusOK, msgentity.Err(me.IncData(1000000), me.Gmsg))
return false
}
me = bridge.LoginController{}.GetLogin(ctx).(*msgentity.MsgEntity)
// me = ginutil.CallFuncByUrl("/login/info", ginutil.POST, ctx).(*msgentity.MsgEntity)
if !me.Gsuccess {
ctx.JSONP(http.StatusOK, msgentity.Err(me.IncData(1100000), me.Gmsg))
return false
}
//loginUser := me.Gdata.(LoginUser) //注意:这样会异常,必须进行转换
temp, err := json.Marshal(me.Gdata)
if err != nil {
Log.Error("Map数据转字符串格式发生异常:", err)
return false
}
loginUser := LoginUser{}
err = json.Unmarshal(temp, &loginUser)
if err != nil {
Log.Error("字符串转结构体格式发生异常:", err)
return false
}
urlutil.AddAttrib(ctx.Request, "sLoginUserId", loginUser.GsId) //将对应的id赋予请求属性,用于后续请求操作
urlutil.AddAttrib(ctx.Request, "sLoginUserName", loginUser.GsName) //将对应的sName赋予请求属性,用于后续请求操作
urlutil.AddAttrib(ctx.Request, "sLoginUserNo", loginUser.GsNo) //将对应的sNo赋予请求属性,用于后续请求操作
urlutil.AddAttrib(ctx.Request, "sLoginUserType", loginUser.GsType) //将对应的sType赋予请求属性,用于后续请求操作
urlutil.AddAttrib(ctx.Request, "sCookie", sCookie) //放入传递的参数
//-- 含有附属信息则一并加入 --//
if len(loginUser.Gattached) > 0 {
for key, val := range loginUser.Gattached {
urlutil.AddAttrib(ctx.Request, key, val)
}
}
return true
}
/**
* 用户权限验证
* @param ctx
* @return
*/
func (inter Interceptor) validRight(ctx ginutil.Context) bool {
servletPath := ctx.Request.URL.Path
sUserId := urlutil.GetAttrib(ctx.Request, "sLoginUserId") //验证'是否登录'时已经添加
me := bridge.UserAndRightService{}.CheckRight(ctx, sUserId, servletPath)
// me := ginutil.CallFuncByUrl("/user/and/right/check", ginutil.POST, ctx).(*msgentity.MsgEntity)
if me.Gsuccess {
return true //有权限
}
ctx.JSONP(http.StatusOK, msgentity.Err(1000006, "当前用户没有访问'", servletPath, "'的权限",
"请检查1:是否在权限中添加了权限信息2:是否分配权限到具体角色或用户;3:若权限分配到了角色则是否已经将角色分配到用户"))
return false //没有权限,也不是可忽略的路径,禁止
}
/**
* 验证是否可忽略路径模块
* @param ctx gin请求
* @param url 要验证的路径
* @param isMustLogin 是否必须登录
* @param sUserType 待检验的用户类型
* @return 1:没有使用'忽略路径模块';2:路径为'可忽略路径';3:不是可忽略的路径
*/
func (inter Interceptor) checkIgnoreUrl(ctx ginutil.Context, url string, isMustLogin bool, sUserType string) int {
if isMustLogin { //判断是否是在配置文件中要求登录后就能免拦截的路径
if strings.Contains(sysIgnoreUrlByLogined, "*") && (len(sysIgnoreUrlByLoginedListLike) < 1) {
array := strings.Split(sysIgnoreUrlByLogined, ",")
for _, val := range array {
if !strings.Contains(val, "*") {
continue
}
iEd := strings.Index(val, "*")
val := val[:iEd-1]
sysIgnoreUrlByLoginedListLike[val] = ""
}
}
if len(sysIgnoreUrlByLoginedList) < 1 {
array := strings.Split(sysIgnoreUrlByLogined, ",")
for _, val := range array {
if strings.Contains(val, "*") {
continue
}
sysIgnoreUrlByLoginedList[val] = ""
}
}
if len(sysIgnoreUrlByLoginedListLike) > 0 {
for key := range sysIgnoreUrlByLoginedListLike {
if strings.HasPrefix(url, key) {
return 2
}
}
}
_, ok := sysIgnoreUrlByLoginedList[url]
if ok {
return 2
}
}
iMustLogin := 2
if isMustLogin {
iMustLogin = 1
}
bl := bridge.IgnoreURLService{}.CheckIgnoreUrl(ctx, url, iMustLogin, sUserType)
// ctx.AddParam("iMustLogin", strconv.Itoa(iMustLogin))
// ctx.AddParam("sUserType", sUserType)
// bl := ginutil.CallFuncByUrl("/ignore/url/check", ginutil.POST, ctx).(bool)
if bl {
return 2 //路径是可忽略路径
}
return 3 //不是可忽略的路径,禁止
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/tomatomeatman/golang-repository.git
git@gitee.com:tomatomeatman/golang-repository.git
tomatomeatman
golang-repository
GolangRepository
307ac0f25175

搜索帮助