访客落地页、在线读秒与浏览轨迹
记录进线页面与换页路径,心跳刷新活跃时间;工作台展示落地页/当前页/轨迹时间线与在线时长,嵌入脚本同步宿主 SPA 路由。
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
const (
|
||||
maxPageURLLen = 1000
|
||||
maxPageTitleLen = 200
|
||||
// 单会话最多保留轨迹条数(超出删最旧)
|
||||
maxPageViewsPerSession = 50
|
||||
)
|
||||
|
||||
// 常见敏感 query 参数,上报 URL 时剥离
|
||||
var sensitiveQueryKeys = map[string]bool{
|
||||
"token": true, "access_token": true, "refresh_token": true,
|
||||
"password": true, "passwd": true, "pwd": true,
|
||||
"phone": true, "mobile": true, "tel": true,
|
||||
"idcard": true, "id_card": true, "secret": true, "key": true,
|
||||
"authorization": true, "auth": true, "session": true, "sid": true,
|
||||
"code": true, "otp": true, "verify": true,
|
||||
}
|
||||
|
||||
func truncateRunes(s string, max int) string {
|
||||
s = strings.TrimSpace(s)
|
||||
if max <= 0 || s == "" {
|
||||
return s
|
||||
}
|
||||
if utf8.RuneCountInString(s) <= max {
|
||||
return s
|
||||
}
|
||||
r := []rune(s)
|
||||
return string(r[:max])
|
||||
}
|
||||
|
||||
// sanitizePageURL 规范化并脱敏 URL,非法则返回空。
|
||||
func sanitizePageURL(raw string) string {
|
||||
raw = strings.TrimSpace(raw)
|
||||
if raw == "" {
|
||||
return ""
|
||||
}
|
||||
if utf8.RuneCountInString(raw) > maxPageURLLen*2 {
|
||||
raw = string([]rune(raw)[:maxPageURLLen*2])
|
||||
}
|
||||
u, err := url.Parse(raw)
|
||||
if err != nil || u.Scheme == "" || u.Host == "" {
|
||||
// 允许无 scheme 的相对路径拼不成绝对时,若已是 http(s) 失败则截断原文
|
||||
if strings.HasPrefix(raw, "http://") || strings.HasPrefix(raw, "https://") {
|
||||
return truncateRunes(raw, maxPageURLLen)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
if u.Scheme != "http" && u.Scheme != "https" {
|
||||
return ""
|
||||
}
|
||||
q := u.Query()
|
||||
changed := false
|
||||
for k := range q {
|
||||
lk := strings.ToLower(k)
|
||||
if sensitiveQueryKeys[lk] || strings.Contains(lk, "token") || strings.Contains(lk, "password") {
|
||||
q.Del(k)
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
if changed {
|
||||
u.RawQuery = q.Encode()
|
||||
}
|
||||
// 去掉 fragment
|
||||
u.Fragment = ""
|
||||
out := u.String()
|
||||
return truncateRunes(out, maxPageURLLen)
|
||||
}
|
||||
|
||||
func sanitizePageTitle(raw string) string {
|
||||
return truncateRunes(raw, maxPageTitleLen)
|
||||
}
|
||||
Reference in New Issue
Block a user