本次主要分析指纹识别部分,针对开放http的端口的服务信息进行识别。
项目来源:https://github.com/XinRoom/go-portScan/blob/main/util/file.go
此文件的功能,主要是从html页面中提取favicon的url,然后计算favicon的hash值
此文件中主要包含三个方法:
从 HTML 页面的字符串中提取 favicon 的 URL;使用正则表达式 shortcutText
匹配包含 favicon 的 标签,然后再使用
shortcutHref
提取其 href
属性中的 URL
计算给定字节切片的 MurmurHash3 32 位哈希值;使用 MurmurHash3 算法对输入的字节切片进行哈希运算,返回结果作为字符串表示。
对二进制数据进行标准的 Base64 编码,并按行分割。将输入的二进制数据使用标准的 Base64 编码成字符串,然后按照每行字符数为 76 的标准,将编码后的结果分割成多行。
package webfinger
import (
"bytes"
"encoding/base64"
"fmt"
"github.com/twmb/murmur3"
"regexp"
)
var (
shortcutText = regexp.MustCompile(`(?im)`)//提取link标签的正则表达式
shortcutHref = regexp.MustCompile(`(?im)href=['"]+(.*?)['"]+`)//提取link标签里的href值的正则表达式
)
func FindFaviconUrl(body string) string {
a := shortcutText.FindStringSubmatch(body)//首先根据正则提取传入的html页面的link标签,赋值给a
if len(a) > 0 {//如果存在link标签
faviconLink := a[0]
b := shortcutHref.FindStringSubmatch(faviconLink)//提取第一个link标签的href的值
if len(b) > 1 {
return b[1] //如果提取url成功,则返回url
}
}
return ""//不存在link标签,返回空
}
func mmh3Hash32(raw []byte) string {
var h32 = murmur3.New32()
_, err := h32.Write(raw)
if err == nil {
return fmt.Sprintf("%d", int32(h32.Sum32()))//使用 MurmurHash3 算法对输入的字节切片进行哈希运算,返回结果作为字符串表示。
} else {
return ""
}
}
func standBase64(braw []byte) []byte {
bckd := base64.StdEncoding.EncodeToString(braw)
var buffer bytes.Buffer
for i := 0; i < len(bckd); i++ {
ch := bckd[i]
buffer.WriteByte(ch)
if (i+1)%76 == 0 {
buffer.WriteByte('\n')//将输入的二进制数据使用标准的 Base64 编码成字符串,然后按照每行字符数为 76 的标准,将编码后的结果分割成多行。
}
}
buffer.WriteByte('\n')
return buffer.Bytes()
}
这个文件主要为faviconhash的字典,用来通过faviconhash的值比对来获取指纹信息。
此文件代码是一个 Web 系统指纹识别器。它的主要目的是从 HTTP 响应或 Favicon.ico 数据中识别 Web 服务的指纹。下面是一步步的解释:
Date
结构体表示特定指纹识别方法的数据结构;
WebFinger
结构体用于保存不同的指纹集合
type Date struct {
Name string //指纹名称
Location string //在http响应中的位置
Method string //指纹识别方法(如keyword、regular、faviconhash)
Keyword []string //关键字或者正则表达式数组
}
type WebFinger struct {
Name string //指纹集合的名称
Fingers []Date //包含data结构体的切片
}
var WebFingers []WebFinger
//go:embed finger.json
var DefFingerData []byte
解析 Web 指纹数据。
func ParseWebFingerData(data []byte) error {
err := json.Unmarshal(data, &WebFingers) //将json数据保存到WebFingers实例中
if err != nil {
return err
}
return nil
}
从文件中加载 Web 指纹数据。
//LoadWebFingerData 加载web指纹数据
func LoadWebFingerData(file string) error {
data, err := os.ReadFile(file)
if err != nil {
return err
}
err = ParseWebFingerData(data)
if err != nil {
return err
}
return nil
}
此函数解析 HTTP 响应的内容和头部信息,然后遍历 WebFingers
中的指纹集合,检查是否匹配给定的指纹识别方法和关键字或正则表达式;如果匹配,则返回匹配的指纹名称。
- 解析 HTTP 响应的内容和头部信息。
- 遍历
WebFingers
中的指纹集合,检查是否匹配给定的指纹识别方法和关键字或正则表达式。- 如果匹配,则返回匹配的指纹名称。
//WebFingerIdent web系统指纹识别 用指纹库的指纹循环比对response中header部分和body部分是否包含特征
func WebFingerIdent(resp *http.Response) (names []string){
var dataMap = make(map[string]string)
body, _ := io.ReadAll(resp.Body)
dataMap["body"] = string(body)
var b bytes.Buffer
resp.Header.Write(&b)
dataMap["header"] = b.String()
for _,finger := range WebFingers {
for _, finger2 := range finger.Fingers {
var flag bool
if _, ok := dataMap[finger2.Location]; !ok {//取指纹库中Location字段为body和header部分的指纹
continue
}
switch finger2.Method {
case "keyword" :
if iskeyword(dataMap[finger2.Location], finger2.Keyword){
flag = true
}
case "regular" :
if isregular(dataMap[finger2.Location], finger2.Keyword){
flag = true
}
}
if flag {
if finger2.Name != ""{
finger.Name += "," + finger2.Name
}
names = append(names, finger.Name)
break
}
}
}
return
}
此函数通过 Favicon.ico 识别 Web 系统的指纹。首先对对 Favicon.ico 数据进行哈希运算,然后遍历WebFingers
中的指纹集合,检查是否与哈希值匹配指定的指纹识别方法和关键字。如果匹配,则返回匹配的指纹名称。
- 对 Favicon.ico 数据进行哈希运算。
- 遍历
WebFingers
中的指纹集合,检查是否与哈希值匹配指定的指纹识别方法和关键字。- 如果匹配,则返回匹配的指纹名称。
// WebFingerIdentByFavicon web系统指纹识别,通过Favicon.ico用指纹库的指纹循环比对favico hash之判断是否包含特征
func WebFingerIdentByFavicon(body []byte) (names []string) {
var data string
data = mmh3Hash32(standBase64(body))
for _, finger := range WebFingers {
for _, finger2 := range finger.Fingers {
switch finger2.Method {
case "faviconhash":
if data != "" && len(finger2.Keyword) > 0 && data == finger2.Keyword[0] {
if finger2.Name != "" {
finger.Name += "," + finger2.Name
}
names = append(names, finger.Name)
break
}
}
}
}
return
}
此文件包含两个方法,iskeyword和isregular,都是用于检查字符串中是否存在特定的关键字或匹配正则表达式
检查字符串中是否存在指定的关键字。遍历关键字数组,如果字符串中不包含其中任何一个关键字,则立即返回 false
,否则返回 true
。
检查字符串是否匹配指定的正则表达式。遍历正则表达式数组,使用 regexp.MustCompile
创建正则表达式对象,然后检查字符串是否与每个正则表达式匹配。如果字符串不匹配任何一个正则表达式,则立即返回 false
,否则返回 true
。
func iskeyword(str string, keyword []string) bool {
if len(keyword) == 0 || str == "" {//先判断传入关键字切片是否为空和字符串是否为空
return false
}
//遍历关键字数组,如果字符串中不包含其中任何一个关键字,则立即返回
for _, k := range keyword {//循环遍历keyword切片,检查keyword是否都包含在str字符串中
if !strings.Contains(str, k) {
return false
}
}
return true
}
//遍历正则表达式数组,使用 regexp.MustCompile 创建正则表达式对象,然后检查字符串是否与每个正则表达式匹配。如果字符串不匹配任何一个正则表达式,则立即返回 false,否则返回 true。
func isregular(str string, keyword []string) bool {
if len(keyword) == 0 || str == "" {
return false
}
for _, k := range keyword {
re := regexp.MustCompile(k)
if !re.Match([]byte(str)) {
return false
}
}
return true
}
mmh3Hash32(raw []byte) string
:对数据进行哈希并返回哈希值的字符串表示形式。standBase64(braw []byte) []byte
:对数据进行 Base64 编码,并在每行末尾添加换行符。此文件主要是一些用于字符编码转换的函数,以及一个用于从http响应中提取标题并根据响应的内容类型进行相应的解码的函数。
这个函数用于将 GBK 编码的字节序列转换为 UTF-8 编码的字节序列。它使用
simplifiedchinese.GBK.NewDecoder()
进行解码,将输入的 GBK 编码字节流转换为 UTF-8 编码//将GBK编码转化UTF-编码 func Decodegbk(s []byte) ([]byte, error){ I := bytes.NewReader(s) O := transform.NewReader(I, simplifiedchinese.GBK.NewDecoder()) d, e := ioutil.ReadAll(O) if e != nil { return nil, e } return d, nil }
类似于
Decodegbk
,这个函数将 BIG5 编码的字节序列转换为 UTF-8 编码的字节序列,使用traditionalchinese.Big5.NewDecoder()
进行解码。func Decodebig5(s []byte) ([]byte, error){ I := bytes.NewReader(s) O := transform.NewReader(I, traditionalchinese.Big5.NewDecoder()) d, e := ioutil.ReadAll(O) if e != nil { return nil, e } return d, nil }
这个函数是将 UTF-8 编码的字节序列转换为 BIG5 编码的字节序列,使用
traditionalchinese.Big5.NewEncoder()
进行编码。func Encodebig5(s []byte) ([]byte, error){ I := bytes.NewReader(s) O := transform.NewReader(I, traditionalchinese.Big5.NewEncoder()) d, e := ioutil.ReadAll(O) if e != nil { return nil, e } return d,nil }
这个函数是用于将韩文编码的字节序列转换为 UTF-8 编码的字节序列,使用
korean.EUCKR.NewDecoder()
进行解码。func DecodeKorean(s []byte) ([]byte, error) { koreanDecoder := korean.EUCKR.NewDecoder() return koreanDecoder.Bytes(s) }
这个函数从 HTTP 响应中提取数据,并根据响应的内容类型进行相应的解码。它会检查响应头中的 Content-Type
,尝试根据特定的字符集(如 GBK、EUC-KR 等)对数据进行解码。如果检测到响应头中指定了字符集,会调用相应的解码函数进行转换,如果没有匹配到特定的字符集,就会尝试从 HTML 头部的 meta 标签中提取字符集信息,并根据提取的信息进行解码。最终返回解码后的数据或原始数据(如果未指定字符集或解码失败)。
func DecodeData(data []byte, headers http.Header) ([]byte, error) { //Non UTF-8 if contentTypes, ok := headers["Content-Type"]; ok { contentType := strings.ToLower(strings.Join(contentTypes, ";"))//用;连接contenttypes内容,并转化为小写 //根据不同的contentyps值调用不同的解密方法 switch { case stringsutil.ContainsAny(contentType, "charset=gb2312","charset=gbk"): return Decodegbk([]byte(data)) case stringsutil.ContainsAyn(contentType,"euc-kr"): return DecodeDorean(data) } //Content-Type 来自 head tag var match = reContentType.FindSubmatch(data)//reContentType来自 core/port/fingerprint/title.go ,作用为regexp.MustCompile(`(?im)\s*charset="(.*?)"|charset=(.*?)"\s*`),根据正则提取对应字段 var mcontentType = "" if len(match) != 0 { for i, v := range match{ if string(v) != "" && i != 0 { mcontentType = string(v) } } mcontentType = strings.ToLower(mcontentType) } switch { case stringsutil.ContainsAny(mcontentType, "gb2312", "gbk"): return Decodegbk(data) } } return data, nil }
ps:这些函数的作用在于根据不同的字符编码格式,将原始的字符序列转换为 UTF-8 编码的字符序列,使其能够被正确解析和处理。DecodeData
函数则是根据 HTTP 响应的内容类型来判断使用何种字符编码转换函数,以确保正确地解码数据
这个文件主要用于端口服务的指纹识别,主要功能如下:
首先创建常量和类型:
type Action uint8
const (
ActionRecv = Action(iota)
ActionSend
)
const (
refusedStr = "refused"
ioTimeoutStr = "i/o timeout"
)
type ruleData struct {
Action Action // send or recv
Data []byte // send or match data
Regexps []*regexp.Regexp
}
type serviceRule struct {
Tls bool
DataGroup []ruleData
}
var serviceRules = make(map[string]serviceRule)
var readBufPool = &sync.Pool{
New: func() interface{} {
return make([]byte, 4096)
},
}
函数如下:
此函数识别端口对应的服务。它按照指定的顺序尝试从一系列服务规则中匹配出对应的服务。如果连接失败或者无法匹配出服务,则返回"unknown"
// PortIdentify 端口识别 func PortIdentify(network string, ip net.IP, _port uint16, dailTimeout time.Duration) (serviceName string, isDailErr bool) { matchedRule := make(map[string]struct{}) unknown := "unknown" var matchStatus int // 优先判断port可能的服务 if serviceNames, ok := portServiceOrder[_port]; ok {//portServiceOrder为rule.go文件中常见端口服务对应列表 for _, service := range serviceNames { matchedRule[service] = struct{}{} matchStatus = matchRule(network, ip, _port, serviceRules[service], dailTimeout)//获取服务端口开放状态 if matchStatus == 1 { return service, false } else if matchStatus == -1 { return unknown, true } } } // onlyRecv { var conn net.Conn var n int buf := readBufPool.Get().([]byte) defer func() { readBufPool.Put(buf) }() address := fmt.Sprintf("%s:%d", ip, _port) conn, _ = net.DialTimeout(network, address, dailTimeout) if conn == nil { return unknown, true } n, _ = read(conn, buf) conn.Close() if n != 0 { for _, service := range onlyRecv { _, ok := matchedRule[service] //检测是不是已有服务 if ok { continue } matchStatus = matchRuleWhithBuf(buf[:n], ip, _port, serviceRules[service])//判断端口服务开放状态 if matchStatus == 1 { return service, false } } } for _, service := range onlyRecv { matchedRule[service] = struct{}{} } } // 优先判断Top服务 for _, service := range serviceOrder { _, ok := matchedRule[service] if ok { continue } matchedRule[service] = struct{}{} matchStatus = matchRule(network, ip, _port, serviceRules[service], dailTimeout) if matchStatus == 1 { return service, false } else if matchStatus == -1 { return unknown, true } } // other for service, rule := range serviceRules { _, ok := matchedRule[service] if ok { continue } matchStatus = matchRule(network, ip, _port, rule, dailTimeout) if matchStatus == 1 { return service, false } else if matchStatus == -1 { return unknown, true } } return unknown, false }
函数根据传入的数据缓冲区,匹配给定的服务规则。如果规则中的数据在缓冲区中找到了匹配,则返回成功状态。
// 指纹匹配函数 func matchRuleWhithBuf(buf, ip net.IP, _port uint16, serviceRule serviceRule) int { data := []byte("") // 逐个判断 for _, rule := range serviceRule.DataGroup { if rule.Data != nil {//先替换请求中的数据data数据,比如IP和port data = bytes.Replace(rule.Data, []byte("{IP}"), []byte(ip.String()), -1) data = bytes.Replace(data, []byte("{PORT}"), []byte(strconv.Itoa(int(_port))), -1) } // 包含数据就正确,使用正则匹配 if rule.Regexps != nil { for _, _regex := range rule.Regexps { if _regex.MatchString(convert2utf8(string(buf))) { return 1 } } } if bytes.Compare(data, []byte("")) != 0 && bytes.Contains(buf, data) { return 1 } } return 0 }
此函数建立连接并根据连接的情况进行数据的发送和接收,然后匹配给定的服务规则。如果规则中的数据在接收的数据中找到了匹配,则返回成功状态。
// 指纹匹配函数 func matchRule(network string, ip net.IP, _port uint16, serviceRule serviceRule, dailTimeout time.Duration) int { var err error var isTls bool var conn net.Conn var connTls *tls.Conn address := fmt.Sprintf("%s:%d", ip, _port) // 建立连接 if serviceRule.Tls {//先确定是不是https // tls connTls, err = tls.DialWithDialer(&net.Dialer{Timeout: dailTimeout}, network, address, &tls.Config{ InsecureSkipVerify: true, MinVersion: tls.VersionTLS10, }) if err != nil { if strings.HasSuffix(err.Error(), ioTimeoutStr) || strings.Contains(err.Error(), refusedStr) { return -1 } return 0 } defer connTls.Close() isTls = true } else {//如果不是https,使用net.conn连接 conn, err = net.DialTimeout(network, address, dailTimeout) if conn == nil { return -1 } defer conn.Close() } buf := readBufPool.Get().([]byte) defer func() { readBufPool.Put(buf) }() data := []byte("") // 逐个判断 for _, rule := range serviceRule.DataGroup { if rule.Data != nil {//替换所有data数据中的IP和port,针对http和https data = bytes.Replace(rule.Data, []byte("{IP}"), []byte(ip.String()), -1) data = bytes.Replace(data, []byte("{PORT}"), []byte(strconv.Itoa(int(_port))), -1) } if rule.Action == ActionSend {//先判断获取发送数据,进行conn连接 if isTls { connTls.SetWriteDeadline(time.Now().Add(time.Second)) _, err = connTls.Write(data) } else { conn.SetWriteDeadline(time.Now().Add(time.Second)) _, err = conn.Write(data) } if err != nil { // 出错就退出 return 0 } } else {//针对recive进行数据判断 var n int if isTls { n, err = read(connTls, buf) } else { n, err = read(conn, buf) } // 出错就退出 if n == 0 { return 0 } // 包含数据就正确 使用正则筛选返回包数据 if rule.Regexps != nil { for _, _regex := range rule.Regexps { if _regex.MatchString(convert2utf8(string(buf[:n]))) { return 1 } } } //判断数据是否为空,以及buf中是否包含数据 if bytes.Compare(data, []byte("")) != 0 && bytes.Contains(buf[:n], data) { return 1 } } } return 0 }
函数负责从连接中读取数据,并在指定时间内设置读取的截止时间。它会尝试从传入的连接中读取数据到缓冲区中,并返回读取的字节数和可能出现的错误。如果读取的数据长度为 0 或者发生错误,则会返回相应的状态信息。
func read(conn interface{}, buf []byte) (int, error) { switch conn.(type) { case net.Conn: conn.(net.Conn).SetReadDeadline(time.Now().Add(time.Second)) return conn.(net.Conn).Read(buf[:]) case *tls.Conn: conn.(*tls.Conn).SetReadDeadline(time.Now().Add(time.Second)) return conn.(*tls.Conn).Read(buf[:]) } return 0, errors.New("unknown type") }
函数用于修复正则表达式在匹配非UTF-8字符时可能出现的问题。它会将UTF-8以外的字符转换成对应的UTF-8字符,确保正则表达式可以正确匹配。
func convert2utf8(src string) string { var dst string for i, r := range src { var v string if r == utf8.RuneError { // convert, rune => string, intstring() => encoderune() v = string(src[i]) } else { v = string(r) } dst += v } return dst }
ps:代码中利用 sync.Pool
作为缓冲池,用来存储读取数据的缓冲区,提高效率。总体来说,这些函数实现了通过发送和接收数据并根据特定规则匹配服务的功能,用于识别特定端口上可能运行的服务
这个文件主要为一个指纹识别器,它尝试根据不同服务的特征来判断特定端口上的服务。这里的 serviceOrder
是服务的顺序列表,onlyRecv
是仅接收数据的服务列表,portServiceOrder
是特定端口和服务的映射关系。
var serviceOrder = []string{"http", "https", "ssh", "redis", "mysql"}
var onlyRecv []string
var portServiceOrder = map[uint16][]string{
21: {"ftp"},
22: {"ssh"},
80: {"http", "https"},
443: {"https", "http"},
445: {"smb"},
1035: {"oracle"},
1080: {"socks5", "socks4"},
1081: {"socks5", "socks4"},
1082: {"socks5", "socks4"},
1083: {"socks5", "socks4"},
1433: {"sqlserver"},
1521: {"oracle"},
1522: {"oracle"},
1525: {"oracle"},
1526: {"oracle"},
1574: {"oracle"},
1748: {"oracle"},
1754: {"oracle"},
3306: {"mysql"},
3389: {"ms-wbt-server"},
6379: {"redis"},
9001: {"mongodb"},
11211: {"memcached"},
14238: {"oracle"},
27017: {"mongodb"},
20000: {"oracle"},
49153: {"mongodb"},
}
init()
函数中,为了不同的服务(如HTTP、HTTPS、SSH等),定义了对应的识别规则。每个服务都有其特定的数据发送和接收规则,用于匹配返回的数据以确定服务是否存在。例如,对于 HTTP 服务,它尝试发送一个 HTTP 头部,并尝试匹配返回数据中是否包含HTTP/
;对于 SSH 服务,它会尝试匹配返回数据中是否包含 SSH 相关的标识等。这些规则都是通过发送特定数据和匹配接收到的数据来进行识别的func init() { // http serviceRules["http"] = serviceRule{ //pingerprint.go中结构体,包含Tls和DataGroup Tls: false, DataGroup: []ruleData{ {//设置http和https的规则数据,包含请求头和接收包 ActionSend, []byte("HEAD / HTTP/1.1\r\nHost: {IP}\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Firefox/91.0\r\nAccept: */*\r\nAccept-Language: en\r\nAccept-Encoding: deflate\r\n\r\n"), nil, }, { ActionRecv, []byte("HTTP/"), nil, }, }, } // https serviceRules["https"] = serviceRule{ Tls: true, DataGroup: serviceRules["http"].DataGroup, } // ssh serviceRules["ssh"] = serviceRule{ Tls: false, DataGroup: []ruleData{ {//设置ssh的匹配规则 ActionRecv, nil, []*regexp.Regexp{ regexp.MustCompile(`^SSH-([\d.]+)-`), regexp.MustCompile(`^SSH-(\d[\d.]+)-`), regexp.MustCompile(`^SSH-(\d[\d.]*)-`), regexp.MustCompile(`^SSH-2\.0-`), regexp.MustCompile(`^SSH-1\.`), }, }, }, } // ftp serviceRules["ftp"] = serviceRule{ Tls: false, DataGroup: []ruleData{ {//设置ftp的匹配规则 ActionRecv, nil, []*regexp.Regexp{ regexp.MustCompile(`^220 ([-/.+\w]+) FTP server`), regexp.MustCompile(`^220[ |-](.*?)FileZilla`), regexp.MustCompile(`^(?i)220[ |-](.*?)version`), regexp.MustCompile(`^220 3Com `), regexp.MustCompile(`^220-GuildFTPd`), regexp.MustCompile(`^220-.*\r\n220`), regexp.MustCompile(`^220 Internet Rex`), regexp.MustCompile(`^530 Connection refused,`), regexp.MustCompile(`^220 IIS ([\w._-]+) FTP`), regexp.MustCompile(`^220 PizzaSwitch `), regexp.MustCompile(`(?i)^220 ([-.+\w]+) FTP`), regexp.MustCompile(`(?i)^220[ |-](.*?)FTP`), }, }, }, } // socks4 serviceRules["socks4"] = serviceRule{ Tls: false, DataGroup: []ruleData{ {//设置socks4发送包的数据部分 ActionSend, []byte("\x04\x01\x00\x16\x7f\x00\x00\x01rooo\x00"), nil, }, {//设置socks4接收包的匹配规则 ActionRecv, nil, []*regexp.Regexp{ regexp.MustCompile(`^\x00\x5a`), regexp.MustCompile(`^\x00\x5b`), regexp.MustCompile(`^\x00\x5c`), regexp.MustCompile(`^\x00\x5d`), }, }, }, } // socks5 serviceRules["socks5"] = serviceRule{ Tls: false, DataGroup: []ruleData{ {//设置socks5的发送包数据 ActionSend, []byte("\x05\x04\x00\x01\x02\x80\x05\x01\x00\x03\x0dwww.baidu.com\x00\x50GET / HTTP/1.0\r\n\r\n"), nil, }, {//设置socks5的接收包规则 ActionRecv, nil, []*regexp.Regexp{ regexp.MustCompile(`^\x05\x00\x05\x01`), regexp.MustCompile(`^\x05\x00\x05\x00\x00\x01.{6}HTTP`), regexp.MustCompile(`^\x05\x02`), regexp.MustCompile(`^\x05\x00`), }, }, }, } tls //serviceRules["tls"] = serviceRule{ // Tls: false, // DataGroup: []ruleData{ // { // ActionSend, // []byte("\x16\x03\x00\x00S\x01\x00\x00O\x03\x00?G\xd7\xf7\xba,\xee\xea\xb2`~\xf3\x00\xfd\x82{\xb9\xd5\x96\xc8w\x9b\xe6\xc4\xdb<=\xdbo\xef\x10n\x00\x00(\x00\x16\x00\x13\x00\x0a\x00f\x00\x05\x00\x04\x00e\x00d\x00c\x00b\x00a\x00`\x00\x15\x00\x12\x00\x09\x00\x14\x00\x11\x00\x08\x00\x06\x00\x03\x01\x00"), // nil, // }, // { // ActionRecv, // nil, // []*regexp.Regexp{ // regexp.MustCompile(`^[\x16\x15]\x03\x00`), // regexp.MustCompile(`^[\x16\x15]\x03...\x02`), // }, // }, // }, //} // smb serviceRules["smb"] = serviceRule{ Tls: false, DataGroup: []ruleData{ {//设置smb协议的发送包数据 ActionSend, []byte("\x00\x00\x00\xa4\xff\x53\x4d\x42\x72\x00\x00\x00\x00\x08\x01\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x06\x00\x00\x01\x00\x00\x81\x00\x02PC NETWORK PROGRAM 1.0\x00\x02MICROSOFT NETWORKS 1.03\x00\x02MICROSOFT NETWORKS 3.0\x00\x02LANMAN1.0\x00\x02LM1.2X002\x00\x02Samba\x00\x02NT LM 0.12\x00\x02NT LANMAN 1.0\x00"), nil, }, {//设置smb协议的接收包规则 ActionRecv, nil, []*regexp.Regexp{ regexp.MustCompile(`MBr\x00\x00\x00\x00\x88\x01@\x00`), }, }, }, } // ms-wbt-server serviceRules["ms-wbt-server"] = serviceRule{ Tls: false, DataGroup: []ruleData{ { ActionSend, []byte("\x03\x00\x00*%\xe0\x00\x00\x00\x00\x00Cookie: mstshash=pcpc\r\n\x01\x00\x08\x00\x03\x00\x00\x00"), nil, }, { ActionRecv, nil, []*regexp.Regexp{ regexp.MustCompile(`\x03\x00\x00.\x0e\xd0\x00\x00\x124\x00`), }, }, }, } // jdwp serviceRules["jdwp"] = serviceRule{ Tls: false, DataGroup: []ruleData{ { ActionRecv, []byte("JDWP-Handshake"), nil, }, }, } // jdbc serviceRules["jdbc"] = serviceRule{ Tls: false, DataGroup: []ruleData{ { ActionRecv, []byte("HSQLDB JDBC Network Listener"), nil, }, }, } // Db // mysql serviceRules["mysql"] = serviceRule{ Tls: false, DataGroup: []ruleData{ { ActionRecv, nil, []*regexp.Regexp{ regexp.MustCompile(`(?s)^.\x00\x00\x00\xff..Host .* is not allowed to connect to this .* server$`), regexp.MustCompile(`^.\x00\x00\x00\xff..Too many connections`), regexp.MustCompile(`(?s)^.\x00\x00\x00\xff..Host .* is blocked because of many connection errors`), regexp.MustCompile(`(?s)^.\x00\x00\x00\x0a(\d\.[-_~.+:\w]+MariaDB-[-_~.+:\w]+)`), regexp.MustCompile(`(?s)^.\x00\x00\x00\x0a(\d\.[-_~.+\w]+)\x00`), regexp.MustCompile(`(?s)^.\x00\x00\x00\xffj\x04'[\d.]+' .* MySQL`), }, }, }, } // redis serviceRules["redis"] = serviceRule{ Tls: false, DataGroup: []ruleData{ { ActionSend, []byte("GET / HTTP/1.1\r\n"), nil, }, { ActionRecv, nil, []*regexp.Regexp{ regexp.MustCompile(`-ERR operation not permitted\r\n`), regexp.MustCompile(`-ERR wrong number of arguments for 'get' command\r\n`), }, }, }, } // sqlserver serviceRules["sqlserver"] = serviceRule{ Tls: false, DataGroup: []ruleData{ { ActionSend, []byte("\x12\x01\x00\x34\x00\x00\x00\x00\x00\x00\x15\x00\x06\x01\x00\x1b\x00\x01\x02\x00\x1c\x00\x0c\x03\x00\x28\x00\x04\xff\x08\x00\x01\x55\x00\x00\x00\x4d\x53\x53\x51\x4c\x53\x65\x72\x76\x65\x72\x00\x48\x0f\x00\x00"), nil, }, { ActionRecv, []byte("\x04\x01\x00\x25\x00\x00\x01\x00\x00\x00\x15\x00\x06\x01\x00\x1b\x00\x01\x02\x00\x1c\x00\x01\x03\x00\x1d\x00\x00\xff"), nil, }, }, } // oracle serviceRules["oracle"] = serviceRule{ Tls: false, DataGroup: []ruleData{ { ActionSend, []byte("\x00Z\x00\x00\x01\x00\x00\x00\x016\x01,\x00\x00\x08\x00\x7F\xFF\x7F\x08\x00\x00\x00\x01\x00 \x00:\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x004\xE6\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00(CONNECT_DATA=(COMMAND=version))"), nil, }, { ActionRecv, nil, []*regexp.Regexp{ regexp.MustCompile(`(?s)^\x00\x20\x00\x00\x02\x00\x00\x00\x016\x00\x00\x08\x00\x7f\xff\x01\x00\x00\x00\x00\x20`), regexp.MustCompile(`^\+\x00\x00\x00$`), regexp.MustCompile(`^\x00.\x00\x00\x02\x00\x00\x00.*\(IAGENT`), regexp.MustCompile(`^..\x00\x00\x04\x00\x00\x00"\x00..\(DESCRIPTION=`), regexp.MustCompile(`^\x00.\x00\x00[\x02\x04]\x00\x00\x00.*\(`), regexp.MustCompile(`^\x00.\x00\x00[\x02\x04]\x00\x00\x00.*TNSLSNR`), regexp.MustCompile(`^\x00,\x00\x00\x04\x00\x00"`), }, }, }, } // mongodb serviceRules["mongodb"] = serviceRule{ Tls: false, DataGroup: []ruleData{ { ActionSend, []byte("\x41\x00\x00\x00\x3a\x30\x00\x00\xff\xff\xff\xff\xd4\x07\x00\x00\x00\x00\x00\x00test.$cmd\x00\x00\x00\x00\x00\xff\xff\xff\xff\x1b\x00\x00\x00\x01serverStatus\x00\x00\x00\x00\x00\x00\x00\xf0\x3f\x00"), nil, }, { ActionRecv, nil, []*regexp.Regexp{ regexp.MustCompile(`(?s)^.*version([: "]+)([.\d]+)"`), regexp.MustCompile(`(?s)^\xcb\x00\x00\x00....:0\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\xa7\x00\x00\x00\x01uptime\x00\x00\x00\x00\x00\x00 ` + "`" + `@\x03globalLock\x009\x00\x00\x00\x01totalTime\x00\x00\x00\x00\x7c\xf0\x9a\x9eA\x01lockTime\x00\x00\x00\x00\x00\x00\xac\x9e@\x01ratio\x00!\xc6\$G\xeb\x08\xf0>\x00\x03mem\x00<\x00\x00\x00\x10resident\x00\x03\x00\x00\x00\x10virtual\x00\xa2\x00\x00\x00\x08supported\x00\x01\x12mapped\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01ok\x00\x00\x00\x00\x00\x00\x00\xf0\?\x00$`), regexp.MustCompile(`(?s)^.\x00\x00\x00....:0\x00\x00\x01\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\+\x00\x00\x00\x02errmsg\x00\x0e\x00\x00\x00need to login\x00\x01ok\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00`), regexp.MustCompile(`(?s)^.\x00\x00\x00....:0\x00\x00\x01\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00.\x00\x00\x00\x01ok\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02errmsg\x00.\x00\x00\x00not authorized on`), }, }, }, } // memcached serviceRules["memcached"] = serviceRule{ Tls: false, DataGroup: []ruleData{ { ActionSend, []byte("stats\\n"), nil, }, { ActionRecv, nil, []*regexp.Regexp{ regexp.MustCompile(`(?s)^STAT pid \d`), regexp.MustCompile(`(?s)^ERROR\r\n`), }, }, }, } // onlyRecv 仅接收数据的服务列表 for k, m := range serviceRules { if len(m.DataGroup) == 1 { onlyRecv = append(onlyRecv, k) } } }
ps:总体来说,这个代码段通过发送特定数据并检查返回数据中是否包含特定标识来识别不同服务的存在。如果匹配成功,则确定该服务在特定端口上运行。
此文件主要处理从 HTML 文档中提取标题以及获取页面重定向的目标 URL。
首先:定义变量,设置获取title、contenttype、refresh、replace的正则
var (
cutset = "\n\t\v\f\r"
reTitle = regexp.MustCompile(`(?im)<\s*title.*>(.*?)<\s*/\s*title>`)
reContentType = regexp.MustCompile(`(?im)\s*charset="(.*?)"|charset=(.*?)"\s*`)
reRefresh = regexp.MustCompile(`(?im)\s*content=['"]\d;url=['"](.*?)['"]`)
reReplace = regexp.MustCompile(`(?im)window\.location\.replace\(['"](.*?)['"]\)`)
)
这个函数用于从 HTML 页面中提取标题。它首先尝试使用 DOM 解析器解析文档,并提取其中的标题。如果解析出错,就使用正则表达式
reTitle
在 HTML 文档中查找标题标签,提取标题内容。最后对提取到的标题进行处理,去除空白字符和特殊符号func ExtractTitle(body []byte) (title string) { //先从dom中获取 titleDom, err := getTitleWithDom(body)//从dom节点中获取title //如果出错,则回退到用正则匹配 if err != nil { for _, match := range reTitle.FindAllString(string(body), -1) { title = match break } }else { title = renderNode(titleDom)//将html转换为字符串类型 } title = html.UnescapeString(trimTitleTags(title)) //删除多余字符 title = strings.TrimSpace(strings.Trim(title, cutset)) //去掉首尾的特殊字符 title = strings.ReplaceAll(title, "\n", "") title = strings.ReplaceAll(title, "\r", "") return title }
这个函数尝试使用 HTML 解析器来解析 HTML 文档,并找到
</code> 标签对应的节点。它通过递归方式遍历 HTML 文档的节点树,在遍历过程中寻找 <code>title</code> 节点</p> <pre><code class="language-Go">func getTitleWithDom(body []byte) (*html.Node, error){ var title *html.Node var crawler func(*html.Node) crawler = func(node *html.Node) {//用于遍历 HTML 文档的节点树,查找 <title> 节点。 if node.Type == html.ElementNode && node.Data == "title" { title = node return } for child := node.FirstChild; child != nil && title == nil; child = child.NextSibling { crawler(child) } } htmlDoc, err := html.Parse(bytes.NewReader(body))//使用 html.Parse 函数解析传入的 HTML 文档,将其转换为一个 HTML 文档对象(htmlDoc) if err != nil { return nil, err } crawler(htmlDoc) if title != nil { return title, nil } return nil, fmt.Errorf("title not found") }</code></pre> <p></p> </blockquote> <ul> <li>func renderNode(n *html.Node) string</li> </ul> <blockquote> <p>这个函数将 HTML 节点转换为字符串形式,使用 <code>html.Render</code> 将节点内容写入缓冲区,然后将缓冲区的内容以字符串形式返回。</p> <pre><code class="language-Go">func renderNode(n *html.Node) string{ var buf bytes.Buffer w := io.Writer(&buf) html.Render(w, n) //nolint return buf.String() }</code></pre> </blockquote> <ul> <li>func trimTitleTags(title string) string</li> </ul> <blockquote> <p>这个函数用于处理提取到的标题字符串,去除 <code><title></code> 标签的开始和结束标记,只返回标题文本内容</p> <pre><code class="language-Go">func trimTitleTags(title string) string { titleBegin := strings.Index(title, ">") titleEnd := strings.Index(title,"</") if titleEnd < 0 || titleBegin < 0 { return title } return title[titleBegin + 1 : titleEnd] }</code></pre> <p></p> </blockquote> <ul> <li>func GetLocation(body []byte) (location string)</li> </ul> <blockquote> <p>这个函数用于从 HTML 文档中获取重定向的目标 URL。它首先使用正则表达式 <code>reRefresh</code> 在文档中查找重定向的目标 URL,如果未找到,则使用正则表达式 <code>reReplace</code> 进行查找。如果找到,就返回第一个匹配到的重定向 URL。</p> <pre><code class="language-Go">func GetLocation(body []byte) (location string) { for _, match := range reRefresh.FindAllStringSubmatch(string(body),1){ location = match[1] break } if location == "" { for _, match := range reReplace.FindAllStringSubmatch(string(body),1){ location = match[1] break } } return }</code></pre> <p></p> </blockquote> <p><em><u><strong><span style="background-color:#a2e043;"> ps:这些函数主要用于解析 HTML 文档,从中提取标题并检索重定向目标 URL。它们结合使用 DOM 解析器和正则表达式来从文档中提取所需的信息。</span></strong></u></em></p> <h3>5、core-port-fingerprint-http.go</h3> <p>这个文件主要设置HTTP客户端和定义功能来处理响应体,识别其编码并限制最大读取大小。包含函数如下:</p> <p>首先定义:</p> <pre><code class="language-Go">var ErrOverflow = errors.New("OverflowMax") type Options struct { }</code></pre> <ul> <li><span style="color:#956fe7;">func newHttpClient(dialTimeout time.Duration) *http.Client</span></li> </ul> <blockquote> <ul> <li>创建并配置了一个具有特定传输配置的HTTP客户端,包括TLS设置、超时、最大连接数和禁用长连接等。</li> <li>设置了一个自定义的 <code>CheckRedirect</code> 函数来限制最大重定向次数为两次。</li> </ul> <pre><code class="language-Go">func newHttpClient(dialTimeout time.Duration) *http.Client { transport := &http.Transport{ TLSClientConfig: &tls.Config{//设置httpclient配置 InsecureSkipVerify: true, MinVersion: tls.VersionTLS10, }, DialContext: (&net.Dialer{ Timeout: dialTimeout, }).DialContext, MaxIdleConnsPerHost: 1, IdleConnTimeout: 100 * time.Millisecond, TLSHandshakeTimeout: 3 * time.Second, ExpectContinueTimeout: 3 * time.Second, DisableKeepAlives: true, ForceAttemptHttp2: false, Proxy: http.ProxyFromEnvironment, } // proxy //if options.ProxyUrl != "" { // proxyUrl, err := url.Parse(options.ProxyUrl) // if err != nil { // log.Fatalln(err) // } // transport.Proxy = http.ProxyURL(proxyUrl) //} return &http.Client{ Timeout : 3 * time.Second, Transport: transport, CheckRedirect: func(req *http.Request, via []*http.Request) error{ if len(via) >= 2{ return errors.New("stopped after 2 redirects") } return nil }, } }</code></pre> <p></p> </blockquote> <ul> <li><span style="color:#956fe7;">func getBody(resp *http.Response) (body []byte, err error)</span></li> </ul> <blockquote> <ul> <li>根据响应头中的 <code>Content-Encoding</code> 字段确定响应体的编码格式。</li> <li>通过使用适当的读取器(例如 <code>gzip.NewReader</code> 和 <code>flate.NewReader</code>)处理不同的压缩格式,如 gzip 和 deflate。</li> <li>通过调用 <code>readMaxSize</code> 函数,限制响应体的最大大小为300KB。</li> </ul> <pre><code class="language-Go">// getBody 识别响应Body的编码,读取body数据 func getBody(resp *http.Response) (body []byte, err error) { if resp.Body == nil || resp.Body == http.NoBody { return } var reader io.Reader switch resp.Header.Get("Content-Encoding") { case "gzip": reader, err = gzip.NewReader(resp.Body) case "deflate": reader = flate.NewReader(resp.Body) //case "br": // reader = brotli.NewReader(resp.Body) default: reader = resp.Body } if err == nil { body, err = readMaxSize(reader, 300*1024) // Max Size 300kb } return }</code></pre> <p></p> </blockquote> <ul> <li><span style="color:#956fe7;">func readMaxSize(r io.Reader, maxsize int) ([]byte, error)</span></li> </ul> <blockquote> <ul> <li>从 <code>io.Reader</code>(<code>r</code>)读取数据,同时限制最大尺寸(<code>maxsize</code>)。</li> <li>为一个字节切片(<code>b</code>)分配内存,并以块的方式读取数据,将其附加到切片中,直到达到最大尺寸。</li> <li>如果达到了尺寸限制,函数返回到目前为止读取的数据以及一个 <code>ErrOverflow</code> 错误。</li> </ul> <pre><code class="language-Go"> //这个函数的作用是安全地从 io.Reader 中读取数据,并在达到指定的最大尺寸时停止读取,以避免读取过多的数据造成内存溢出或其他问题。 func readMaxSize(r io.Reader, maxsize int) ([]byte, error) { b := make([]byte, 0, 512) for { if len(b) >= maxsize { return b, ErrOverflow } if len(b) == cap(b) { // Add more capacity (let append pick how much). b = append(b, 0)[:len(b)] } n, err := r.Read(b[len(b):cap(b)]) b = b[:len(b)+n] if err != nil { if err == io.EOF { err = nil } return b, err } } }</code></pre> <p></p> </blockquote> <p><em><u><strong><span style="background-color:#a2e043;">ps:这些函数为主要目的是创建一个具有特定配置的HTTP客户端,并提供功能来处理响应体的解码,考虑到不同的压缩格式,并限制读取数据的最大尺寸,以防止潜在的过大响应引发问题。</span></strong></u></em></p> <p></p> <p></p> <h3>6、core-port-fingerprint-httpInfo.go</h3> <p>这个文件主要实现了HTTP服务信息的探测和提取。主要函数如下:</p> <p>先定义变量httpsTopPort,高频的https端口</p> <pre><code class="language-Go">var httpsTopPort = []uint16{443, 4443, 1443, 8443} var httpClient *http.Client</code></pre> <ul> <li><span style="color:#956fe7;">func ProbeHttpInfo(ip net.IP, _port uint16, dialTimeout time.Duration) (httpInfo *port.HttpInfo, isDailErr bool)</span></li> </ul> <blockquote> <ul> <li>接收一个 IP、端口和请求超时时间,用于在给定的 IP 地址和端口上探测 HTTP 服务信息。</li> <li>根据指定的端口列表确定探测的协议(HTTP 或 HTTPS)。</li> <li>使用 <code>httpClient</code> 对象发送 HTTP 请求,并根据响应提取信息。</li> <li>通过循环尝试 HTTP 和 HTTPS 请求,根据响应来获取信息,包括状态码、重定向 URL、服务器信息、标题、TLS 证书相关信息等。</li> <li>通过 <code>webfinger</code> 包来分析响应的指纹信息,还会尝试提取页面的 favicon。</li> </ul> <pre><code class="language-Go">func ProbeHttpInfo(ip net.IP, _port uint16, dialTimeout time.Duration) (httpInfo *port.HttpInfo, isDailErr bool) { if httpClient == nil { httpClient = newHttpClient(dialTimeout) } var err error var rewriteUrl string var body []byte var resp *http.Response var schemes []string if util.IsUint16InList(_port, httpsTopPort) { //检测请求的_port是否在httpsTopPort列表中 schemes = []string{"https", "http"} }else { schemes = []string["http", "https"} } for _, scheme := range schemes { //循环使用https和http去请求 var rewriteNum int url2 := fmt.Sprintf("%s://%s:%d/", scheme, ip.String(), _port) goReq: resp, body, err = getReq(url2) //获取响应包和响应body if err != nil { if strings.HasSuffix(err.Error(), ioTimeoutStr) || strings.Contains(err.Error(), regusedStr) { return nil, true } continue } if resp != nil { if resp.ContentLength == -1 { resp.ContentLength = int64(len(body)) } //先在响应头中获取重定向url rewriteUrl2, _ := resp.Location() //重新获取url,提取响应包头中的location参数值 if rewriteUrl2 != nil { rewriteUrl = rewriteUrl2.String() }else { rewriteUrl = "" } //其次在body中获取重定向url location := GetLocation(body) //在title.go中,用于获取body中的location 的url,即重定向url if rewriteUrl == "" && location != "" { rewriteUrl = location } if location != "" && rewriteNum < 3 { if !strings.HasPrefix(location, "http") { //判断location前缀中是否http开头,如果不是,则进入下一个if if strings.HasPrefix(location, "/") { resp.Request.URL.Path = location }else { resp.Request.URL.Path = resp.Request.URL.Path[:strings.LastIndex(resp.Request.URL.Path, "/")+1] + location//将 HTTP 请求的 URL 路径修改为原路径中最后一个斜杠之前的部分,再添加上 location 变量的值。这样,新的路径将以最后一个斜杠结尾,然后连接上 location 的值。 } location = resp.Request.URL.String() } url2 = location rewriteNum++ goto goReq } httpInfo = new(port.HttpInfo) //在port.go文件中,为HttpInfo结构体 httpInfo.Url = resp.Request.URL.String() httpInfo.StatusCode = resp.StatusCode httpInfo.ContentLen = int(resp.ContentLength) httpInfo.Location = rewriteUrl httpInfo.Server = resp.Header.Get("Server") httpInfo.Title = ExtracTitle(body) //title.go文件中,获取响应包中的title if resp.TLS != nil && len(resp.TLS.PeerCertificates) > 0 { //如果是https,获取tls握手证书的信息 httpInfo.TlsCN = resp.TLS.PeerCertificates[0].Subject.CommonName //证书主题部分的通用名称信息 httpInfo.TlsDNS = resp.TLS.PeerCertificates[0].DNSNames //证书中包含的DNS名称列表 } //指纹信息 err = webfinger.ParseWebFingerData(webfinger.DefFingerData) if err == nil { resp.Body = io.NopCloser(bytes.NewReader(body)) httpInfo.Fingers = webfinger.WebFingerIdent(resp) //j检测识别响应头和响应body识别指纹 //favicon fau := webfinger.FindFaviconUrl(string(body)) //获取响应body中的favionUrl if fau != ""{ if !strings.HasPrefix(fau, "http") { fau = resp.Request.URL.String() + fau } _, body2, err2 := getReq(fau) //获取请求favicon的响应包信息 if err2 == nil && len(body2) != 0{ httpInfo.Fingers = append(httpInfo.Fingers, webfinger.WebFingerIdentByFavicon(body2)...)//检测favicon指纹 } } } if resp.StatusCode != 400 { break } } } return httpInfo, false }</code></pre> <p></p> </blockquote> <ul> <li><span style="color:#956fe7;">func getReq(url2 string) (resp *http.Response, body []byte, err error)</span></li> </ul> <blockquote> <ul> <li>构造一个 HTTP GET 请求,设置请求头,然后使用 <code>httpClient</code> 执行请求。</li> <li>从响应中读取内容,如果是文本类型则进行编码解析(尝试将非 UTF-8 编码的文本解析为 UTF-8)。</li> </ul> <pre><code class="language-Go">func getReq (url2 string) (resp *http.Response, body []byte, err error) { req, err := http.NewRequest(http.MethodGet, url2, http.Nobody) //创建get请求 if err != nil { return } req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36") req.Header.Set("Accept-Encoding", "gzip, deflate") req.Close = true //关闭keepalive resp, err = httpClient.Do(req) if err != nil { return } if resp.Body != http.NoBody && resp.Body != nil { //如果body不为空且不为http.NoBody,则提取body body, _ = getBody(resp) //在http.go文件中,主要为识别body的编码,读取body数据 if contentTypes, _ := resp.Header["Content-Type"]; len(contentTypes) > 0 { if strings.Contains(contentTypes[0], "text") {//如果响应类型是文本类型,则对响应体进行解码,将非utf-8编码的文本解析为utf-8 _body, err2 := DecodeData(body, resp.Header) if err2 == nil { body = _body } resp.Body = io.NopCloser(bytes.NewReader(body)) } } } return }</code></pre> <p></p> </blockquote> <p>ps:这些函数主要是尝试使用 HTTP 和 HTTPS 进行请求,根据响应提取 HTTP 服务的相关信息,包括标题、服务器信息、TLS 证书信息以及其他可能的指纹信息,为后续服务识别和指纹探测提供数据支持。</p> <h2 style="background-color:transparent;">四、小结</h2> <p>这部分的功能主要围绕在对http和https指纹的识别,通过网页titile和icon的hash进行指纹判断;其次是针对端口进行服务识别,主要匹配规则库在“fingerprint-webfinger-finger.json” 文件和“core-port-fingerprint-rules.go”文件中。</p> <p></p> </div> </div> </div> </div> </div> <!--PC和WAP自适应版--> <div id="SOHUCS" sid="1736114035088220160"></div> <script type="text/javascript" src="/views/front/js/chanyan.js"></script> <!-- 文章页-底部 动态广告位 --> <div class="youdao-fixed-ad" id="detail_ad_bottom"></div> </div> <div class="col-md-3"> <div class="row" id="ad"> <!-- 文章页-右侧1 动态广告位 --> <div id="right-1" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad"> <div class="youdao-fixed-ad" id="detail_ad_1"> </div> </div> <!-- 文章页-右侧2 动态广告位 --> <div id="right-2" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad"> <div class="youdao-fixed-ad" id="detail_ad_2"></div> </div> <!-- 文章页-右侧3 动态广告位 --> <div id="right-3" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad"> <div class="youdao-fixed-ad" id="detail_ad_3"></div> </div> </div> </div> </div> </div> </div> <div class="container"> <h4 class="pt20 mb15 mt0 border-top">你可能感兴趣的:(golang,开发语言,后端)</h4> <div id="paradigm-article-related"> <div class="recommend-post mb30"> <ul class="widget-links"> <li><a href="/article/1897601708851261440.htm" title="基于Python深度学习的【害虫识别】系统~卷积神经网络+TensorFlow+图像识别+人工智能" target="_blank">基于Python深度学习的【害虫识别】系统~卷积神经网络+TensorFlow+图像识别+人工智能</a> <span class="text-muted"></span> <a class="tag" taget="_blank" href="/search/%E5%9B%BE%E5%83%8F%E8%AF%86%E5%88%AB%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/1.htm">图像识别人工智能深度学习</a> <div>一、介绍害虫识别系统,本系统使用Python作为主要开发语言,基于TensorFlow搭建卷积神经网络算法,并收集了12种常见的害虫种类数据集【"蚂蚁(ants)","蜜蜂(bees)","甲虫(beetle)","毛虫(catterpillar)","蚯蚓(earthworms)","蜚蠊(earwig)","蚱蜢(grasshopper)","飞蛾(moth)","鼻涕虫(slug)","蜗牛</div> </li> <li><a href="/article/1897577265630343168.htm" title="浏览器发请求不携 cookie (加了跨域失败)" target="_blank">浏览器发请求不携 cookie (加了跨域失败)</a> <span class="text-muted">Wdc_12</span> <a class="tag" taget="_blank" href="/search/%E7%8A%B6%E6%80%81%E6%A8%A1%E5%BC%8F/1.htm">状态模式</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/boot/1.htm">boot</a> <div>1.发送请求后端产生的唯一的sessionid,服务器会通过HTTP响应头中的Set-Cookie字段将SessionID发送到客户端。客户端(浏览器)收到响应后,会将SessionID存储为cookie。但由于跨域了不自动携带cookie2.加withCredentials所以在前端加上了:myAxios.defaults.withCredentials=true3.出现了跨域的错误,CORS请</div> </li> <li><a href="/article/1897576383249444864.htm" title="敏捷开发之自动化流水线" target="_blank">敏捷开发之自动化流水线</a> <span class="text-muted">舒旻</span> <a class="tag" taget="_blank" href="/search/%E6%95%8F%E6%8D%B7%E9%A1%B9%E7%9B%AE%E7%AE%A1%E7%90%86/1.htm">敏捷项目管理</a><a class="tag" taget="_blank" href="/search/devops/1.htm">devops</a><a class="tag" taget="_blank" href="/search/%E6%95%8F%E6%8D%B7%E6%B5%81%E7%A8%8B/1.htm">敏捷流程</a><a class="tag" taget="_blank" href="/search/scrum/1.htm">scrum</a><a class="tag" taget="_blank" href="/search/%E8%BD%AF%E4%BB%B6%E5%B7%A5%E7%A8%8B/1.htm">软件工程</a><a class="tag" taget="_blank" href="/search/%E6%95%8F%E6%8D%B7%E5%BC%80%E5%8F%91/1.htm">敏捷开发</a> <div>自动化流水线就像给软件交付装上了「智能检测仪」,每个环节自动过滤风险,确保最终交付物既安全又高质量。以下是一个在线教育平台支付系统升级的实战案例,完整展示从开发到上线的全流程。以下是「在线教育平台支付系统升级」案例的完整责任矩阵:责任分工框架环节主要责任人协作角色关键交付物协作工具1.代码开发与提交后端开发工程师技术负责人、产品经理功能代码、单元测试GitLab、JIRA2.代码安全审查安全工程师</div> </li> <li><a href="/article/1897531605702995968.htm" title="基于SpringBoot大学生创新创业训练项目管理系统的设计与实现" target="_blank">基于SpringBoot大学生创新创业训练项目管理系统的设计与实现</a> <span class="text-muted">四金学长</span> <a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/boot/1.htm">boot</a><a class="tag" taget="_blank" href="/search/%E5%90%8E%E7%AB%AF/1.htm">后端</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E5%A4%A7%E5%AD%A6%E7%94%9F%E5%88%9B%E6%96%B0%E5%88%9B%E4%B8%9A%E8%AE%AD%E7%BB%83%E9%A1%B9%E7%9B%AE%E7%AE%A1%E7%90%86%E7%B3%BB%E7%BB%9F/1.htm">大学生创新创业训练项目管理系统</a> <div>1.引言在当今的软件开发领域,企业级应用的开发和部署速度直接影响着业务的竞争力。SpringBoot以其轻量级、快速启动和强大的集成能力,成为构建现代企业级应用的首选框架。本文将带您深入了解SpringBoot框架的核心特性,并展示如何利用它构建一个高效、可扩展的系统。2.开发环境开发语言:Java框架:springbootJD版本:JDK1.8服务器:tomcat7数据库:mysql5.7(一定</div> </li> <li><a href="/article/1897524914714374144.htm" title="新闻推荐系统:Spring Boot框架详解" target="_blank">新闻推荐系统:Spring Boot框架详解</a> <span class="text-muted">2402_85758936</span> <a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/boot/1.htm">boot</a><a class="tag" taget="_blank" href="/search/%E5%90%8E%E7%AB%AF/1.htm">后端</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a> <div>2相关技术2.1MYSQL数据库MySQL是一个真正的多用户、多线程SQL数据库服务器。是基于SQL的客户/服务器模式的关系数据库管理系统,它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等,非常适用于Web站点或者其他应用软件的数据库后端的开发工作。此外,用户可利用许多语言编写访问MySQL数据库的程序。作为开放源代码运动的产物之一,MyS</div> </li> <li><a href="/article/1897490109381537792.htm" title="504 Gateway Time-out nginx如何处理" target="_blank">504 Gateway Time-out nginx如何处理</a> <span class="text-muted">web14786210723</span> <a class="tag" taget="_blank" href="/search/%E9%9D%A2%E8%AF%95/1.htm">面试</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0%E8%B7%AF%E7%BA%BF/1.htm">学习路线</a><a class="tag" taget="_blank" href="/search/%E9%98%BF%E9%87%8C%E5%B7%B4%E5%B7%B4/1.htm">阿里巴巴</a><a class="tag" taget="_blank" href="/search/gateway/1.htm">gateway</a><a class="tag" taget="_blank" href="/search/nginx/1.htm">nginx</a><a class="tag" taget="_blank" href="/search/%E8%BF%90%E7%BB%B4/1.htm">运维</a> <div>在遇到504GatewayTime-out错误时,通常表示后端服务器未能在规定时间内响应请求。以下是一些常见的原因和解决方法:一、原因分析后端服务超时:后端服务器处理请求耗时过长,超出了反向代理(如Nginx)的等待时间。网络连接问题:Nginx和后端服务器之间的网络延迟或断开。后端服务不可用:后端服务崩溃、未启动,或服务器资源耗尽(如CPU或内存)。Nginx配置问题:Nginx超时时间设置过短</div> </li> <li><a href="/article/1897489097161764864.htm" title="Spring Boot整合Thymeleaf模板引擎实战——从静态页面到动态表单处理全流程解析" target="_blank">Spring Boot整合Thymeleaf模板引擎实战——从静态页面到动态表单处理全流程解析</a> <span class="text-muted">Sendingab</span> <a class="tag" taget="_blank" href="/search/%E9%9B%B6%E5%9F%BA%E7%A1%807%E5%A4%A9%E7%B2%BE%E9%80%9ASpring/1.htm">零基础7天精通Spring</a><a class="tag" taget="_blank" href="/search/Boot/1.htm">Boot</a><a class="tag" taget="_blank" href="/search/Spring/1.htm">Spring</a><a class="tag" taget="_blank" href="/search/boot/1.htm">boot</a><a class="tag" taget="_blank" href="/search/%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E7%B2%BE%E9%80%9A/1.htm">从入门到精通</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/boot/1.htm">boot</a><a class="tag" taget="_blank" href="/search/%E5%90%8E%E7%AB%AF/1.htm">后端</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/tomcat/1.htm">tomcat</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/cloud/1.htm">cloud</a><a class="tag" taget="_blank" href="/search/xml/1.htm">xml</a> <div>https://example.com/thymeleaf-spring-demo前言在前后端不分离的传统Web项目中,Thymeleaf凭借自然的HTML语法与强大的表达式功能成为SpringBoot官方推荐的模板引擎。本文将带你从零实现用户注册功能,涵盖表单验证、页面碎片化、国际化等核心场景,并分享性能调优实战经验。一、快速整合Thymeleaf1.1添加基础依赖 xmlorg.springf</div> </li> <li><a href="/article/1897487961893695488.htm" title="智能云图库项目实战(4)---空间模块" target="_blank">智能云图库项目实战(4)---空间模块</a> <span class="text-muted">rain雨雨编程</span> <a class="tag" taget="_blank" href="/search/%E9%A1%B9%E7%9B%AE%E5%AE%9E%E6%88%98/1.htm">项目实战</a><a class="tag" taget="_blank" href="/search/%E6%9D%83%E9%99%90%E7%AE%A1%E7%90%86/1.htm">权限管理</a><a class="tag" taget="_blank" href="/search/%E9%94%81%E6%9C%BA%E5%88%B6/1.htm">锁机制</a><a class="tag" taget="_blank" href="/search/%E4%BA%8B%E5%8A%A1/1.htm">事务</a><a class="tag" taget="_blank" href="/search/%E4%BA%91%E5%9B%BE%E5%BA%93/1.htm">云图库</a><a class="tag" taget="_blank" href="/search/Spring/1.htm">Spring</a> <div>♂️个人主页:@rain雨雨编程微信公众号:rain雨雨编程✍作者简介:持续分享机器学习,爬虫,数据分析希望大家多多支持,我们一起进步!如果文章对你有帮助的话,欢迎评论点赞收藏加关注+目录本节重点一、需求分析二、方案设计空间的必要性空间库表设计1.空间表2、图片表公共图库和空间的关系三、后端开发空间管理1、数据模型2、基础服务开发3、接口开发用户创建私有空间1、创建空间流程2、创建空间服务扩展知</div> </li> <li><a href="/article/1897470183363112960.htm" title="Go-Gin Web 框架完整教程" target="_blank">Go-Gin Web 框架完整教程</a> <span class="text-muted">m0_74825656</span> <a class="tag" taget="_blank" href="/search/%E9%9D%A2%E8%AF%95/1.htm">面试</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0%E8%B7%AF%E7%BA%BF/1.htm">学习路线</a><a class="tag" taget="_blank" href="/search/%E9%98%BF%E9%87%8C%E5%B7%B4%E5%B7%B4/1.htm">阿里巴巴</a><a class="tag" taget="_blank" href="/search/golang/1.htm">golang</a><a class="tag" taget="_blank" href="/search/gin/1.htm">gin</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a> <div>1.环境准备1.1Go环境安装Go语言(或称Golang)是一个开源的编程语言,由Google开发。在开始使用Gin框架之前,我们需要先安装Go环境。安装步骤:访问Go官网下载页面:https://golang.org/dl/根据你的操作系统选择相应的安装包Windows:下载.msi安装包,双击运行安装程序Mac:下载.pkg安装包,双击运行安装程序Linux:下载tar.gz包,解压并配置环境</div> </li> <li><a href="/article/1897465767444672512.htm" title="Axios封装处理" target="_blank">Axios封装处理</a> <span class="text-muted">尤雨溪不会vue</span> <a class="tag" taget="_blank" href="/search/ajax/1.htm">ajax</a> <div>目录要搞清楚在封装axios时,都做了哪些事情?1)安装axios依赖包2)新建一个request.js文件3)添加实例化、拦截器代码目标:基于axios封装一个请求模块,调用接口时使用要搞清楚在封装axios时,都做了哪些事情?实例化baseURLbaseURL:'xxx'超时时间timeout:5000大数处理拦截器请求拦截器全局注入token响应拦截器token失效401跳转登录页当后端接口</div> </li> <li><a href="/article/1897461426549747712.htm" title="园区环境数字化管理系统(源码+文档+讲解+演示)" target="_blank">园区环境数字化管理系统(源码+文档+讲解+演示)</a> <span class="text-muted"></span> <a class="tag" taget="_blank" href="/search/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E4%BB%8B%E7%BB%8D/1.htm">开源项目介绍</a> <div>引言随着工业化和城市化的快速发展,园区环境管理面临着越来越多的挑战。园区环境数字化管理系统通过数字化手段,为园区管理者提供了一个高效、透明、智能的管理平台。本文将详细介绍园区环境数字化管理系统的功能、技术架构以及其在提升园区环境管理效率中的优势。系统概述园区环境数字化管理系统采用前后端分离的架构设计,服务端基于MySQL5.7+、JDK1.8+和Redis,前端则采用Vue2.6.14和Eleme</div> </li> <li><a href="/article/1897444460975943680.htm" title="工程化与框架系列(19)--前端安全防护" target="_blank">工程化与框架系列(19)--前端安全防护</a> <span class="text-muted">一进制ᅟᅠ </span> <a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF%E5%B7%A5%E7%A8%8B%E5%8C%96%E4%B8%8E%E6%A1%86%E6%9E%B6/1.htm">前端工程化与框架</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/%E5%AE%89%E5%85%A8/1.htm">安全</a><a class="tag" taget="_blank" href="/search/%E7%8A%B6%E6%80%81%E6%A8%A1%E5%BC%8F/1.htm">状态模式</a> <div>前端安全防护引言随着Web应用的普及,前端安全问题日益突出。本文将深入探讨前端安全的各种威胁及其防护措施,帮助开发者构建更加安全的Web应用。在当今复杂的网络环境中,理解并实施有效的安全策略已经成为前端开发者的必备技能。前端安全概述前端安全是指保护Web应用的客户端部分免受恶意攻击的一系列技术和实践。虽然后端安全同样重要,但前端作为直接面向用户的界面,往往成为攻击者的首要目标。前端安全威胁主要包括</div> </li> <li><a href="/article/1897438283986694144.htm" title="文件上传和下载前后端交互逻辑" target="_blank">文件上传和下载前后端交互逻辑</a> <span class="text-muted">前端_yu小白</span> <a class="tag" taget="_blank" href="/search/%E7%AC%94%E8%AE%B0/1.htm">笔记</a><a class="tag" taget="_blank" href="/search/%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0/1.htm">文件上传</a><a class="tag" taget="_blank" href="/search/%E4%B8%8B%E8%BD%BD/1.htm">下载</a> <div>上传】1、后端给前端一个上传接口:进行文件上传,上传成功后,该接口返回文件的路径,名称,id2、表单提交接口,提交表单时,将文件的id和表单信息一块提交给后台,实现文件和表单的绑定下载】后端给前端提供一个下载接口,前端调用后,返回二进制文件流。具体请求和接收文件流的方式,参考我之前另一篇博客:后端返回字节流,前端接收下载_后端返回字节数组,前端下载-CSDN博客</div> </li> <li><a href="/article/1897432474359754752.htm" title="请谈谈 HTTP 中的请求方法(GET、POST、PUT、DELETE等),它们的区别是什么?" target="_blank">请谈谈 HTTP 中的请求方法(GET、POST、PUT、DELETE等),它们的区别是什么?</a> <span class="text-muted">程序员黄同学</span> <a class="tag" taget="_blank" href="/search/node.js/1.htm">node.js</a><a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91/1.htm">前端开发</a><a class="tag" taget="_blank" href="/search/http/1.htm">http</a><a class="tag" taget="_blank" href="/search/%E7%BD%91%E7%BB%9C%E5%8D%8F%E8%AE%AE/1.htm">网络协议</a><a class="tag" taget="_blank" href="/search/%E7%BD%91%E7%BB%9C/1.htm">网络</a> <div>一、HTTP方法核心概念HTTP请求方法是客户端向服务器表达操作意图的核心标识,遵循RFC7231等规范定义。正确使用方法可提升API语义清晰度,降低前后端协作成本。以下是常见方法特性对比表:方法幂等性安全性请求体支持典型应用场景GET是是否获取资源列表/详情POST否否是创建资源/触发复杂操作PUT是否是全量替换资源DELETE是否否删除指定资源PATCH否否是部分更新资源二、核心方法详解与代码</div> </li> <li><a href="/article/1897425661912477696.htm" title="2024年华为OD机试真题-提取字符串中的最长数学表达式" target="_blank">2024年华为OD机试真题-提取字符串中的最长数学表达式</a> <span class="text-muted">2301_79125642</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a> <div>我想大家看看我的帖子可以获得一些经验#许愿池##牛客在线求职答疑中心(35799)##牛客在线求职答疑中心#德国弗劳恩霍夫物流研究院还有一个叫帝欧的公司,这个实习值得去吗?有人知道吗offer选择(java后端)华子还是杭州银行希望uu们给给意见华子华为云部门,业务关于基础平台开发(偏软),无转正杭州银行#牛客在线求职答疑中心(35799)##牛客在线求职答疑中心#https://www.nowc</div> </li> <li><a href="/article/1897405481333354496.htm" title="Golang依赖注入实战:从容器管理到应用实践" target="_blank">Golang依赖注入实战:从容器管理到应用实践</a> <span class="text-muted">磐基Stack专业服务团队</span> <a class="tag" taget="_blank" href="/search/golang/1.htm">golang</a> <div>#作者:曹付江文章目录1、示例:管理依赖关系的容器1.1.日志记录器设置1.2.数据库连接设置1.3.管理依赖关系的容器2、如何使用容器3、结论依赖注入(DI)是一种在软件应用程序中促进松散耦合和可测试性的设计模式。它允许将依赖关系(如服务、配置或数据库)注入到组件中,而不是让组件直接创建或管理依赖关系。这将使代码更模块化、更易维护、更易测试。在本篇文章中,我们将使用一个实用的Golang示例来探</div> </li> <li><a href="/article/1897388199857156096.htm" title="【java后端学习路线4】SpringBoot+MyBatisPlus+Redis学习指南,985本海硕自学转码" target="_blank">【java后端学习路线4】SpringBoot+MyBatisPlus+Redis学习指南,985本海硕自学转码</a> <span class="text-muted">程序员城南</span> <a class="tag" taget="_blank" href="/search/java%E5%90%8E%E7%AB%AF%E5%AD%A6%E4%B9%A0%E8%B7%AF%E7%BA%BF/1.htm">java后端学习路线</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/boot/1.htm">boot</a><a class="tag" taget="_blank" href="/search/mybatis/1.htm">mybatis</a><a class="tag" taget="_blank" href="/search/redis/1.htm">redis</a> <div>JAVA后端学习路线路线总览javase->Mysql->计算机网络->JavaWeb->Maven(1)->Spring->SpringMVC->Mybatis->Maven(2)->Linux->Git->SpringBoot->MyBatisPlus->Redis->JVM->JUC->Nginx->Docker->RabbitMQ->SpringCloud->项目(谷粒商城/仿牛客网)方法</div> </li> <li><a href="/article/1897385926968012800.htm" title="多租户适配、多租户隔离" target="_blank">多租户适配、多租户隔离</a> <span class="text-muted">Java掌门人</span> <a class="tag" taget="_blank" href="/search/%E6%8A%80%E6%9C%AF%E8%B5%84%E6%96%99/1.htm">技术资料</a><a class="tag" taget="_blank" href="/search/%E5%85%AC%E6%9C%89%E4%BA%91/1.htm">公有云</a><a class="tag" taget="_blank" href="/search/%E5%A4%9A%E7%A7%9F%E6%88%B7/1.htm">多租户</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/websocket/1.htm">websocket</a><a class="tag" taget="_blank" href="/search/%E6%B7%B7%E5%90%88%E4%BA%91/1.htm">混合云</a> <div>多租户适配很多产品只有专属化版本,需要从产品底层进行尽量少的改造,满足上云之后多租户的数据、缓存、定时任务等隔离多租户适配条目条目名称适配方案持久层适配支持schema和字段隔离两种方案quartz定时任务上下文无法获取租户信息,通过JobGroup识别reids缓存缓存key体现租户id即可websocket场景从cookie获取、前端调用diwork的api获取租户信息塞到cookie,后端w</div> </li> <li><a href="/article/1897378241048539136.htm" title="前端怎么实现聊天输入框?怎么实现类似b站评论的输入并发送自定义表情包?输入回显、发送时表情包转义为[emoji]字符串、页面展示回显" target="_blank">前端怎么实现聊天输入框?怎么实现类似b站评论的输入并发送自定义表情包?输入回显、发送时表情包转义为[emoji]字符串、页面展示回显</a> <span class="text-muted">旅行中的伊蕾娜</span> <a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/vue.js/1.htm">vue.js</a><a class="tag" taget="_blank" href="/search/html5/1.htm">html5</a> <div>之前做项目实现聊天功能,有几个功能点我觉得挺复杂的。今天我来说一下,我是如何实现图片小表情在输入框中显示,发送给后端时只发送一个含义字符串如:[emoji],然后正常回显在页面上。此demo使用vue3源码已上传:源码地址实现效果图:输入自定义表情发送并回显声明:这只是个demo,不涉及与后端交互,不过会在该交互的地方标记,如需实际应用于项目,请根据实际情况进行改造完善!父组件定义及逻辑实现父组件</div> </li> <li><a href="/article/1897343694181298176.htm" title="golang实践-目录结构与工具" target="_blank">golang实践-目录结构与工具</a> <span class="text-muted">alex_023</span> <a class="tag" taget="_blank" href="/search/golang/1.htm">golang</a><a class="tag" taget="_blank" href="/search/golang/1.htm">golang</a> <div>这个话题确实是老调重弹,但确异常重要。老实说,用go做正式项目之前,写过scala,但那个SBT太折磨人,偶然就上了go。两者语法的差别就不说了,但入坑之后才发现水深:没有模块部署及官方的版本管理工具,会带来很多麻烦。反复折腾了近一年,基本上形成了一点固有的模式,做一般项目还算能够简单支持。没什么特别的技术点,更多只是一点心得。一、目录结构我们的代码以rpc为主,http为辅(调用rpc,也辅助测</div> </li> <li><a href="/article/1897341677819981824.htm" title="Golang项目结构规范" target="_blank">Golang项目结构规范</a> <span class="text-muted">权衡77</span> <a class="tag" taget="_blank" href="/search/golang/1.htm">golang</a><a class="tag" taget="_blank" href="/search/golang/1.htm">golang</a> <div>基于Google标准的Golang-Project-Layout标准模板https://github.com/JasonkayZK/Go_Learn/tree/golang-project-layout翻译版https://github.com/golang-standards/project-layout这是Go应用程序项目的基本布局。它不是核心Go开发团队定义的官方标准;然而,它是Go生态系统</div> </li> <li><a href="/article/1897330193706577920.htm" title="golang 内存对齐和填充规则" target="_blank">golang 内存对齐和填充规则</a> <span class="text-muted">老赵不会写代码</span> <a class="tag" taget="_blank" href="/search/go%E8%AF%AD%E8%A8%80/1.htm">go语言</a><a class="tag" taget="_blank" href="/search/golang/1.htm">golang</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/%E5%90%8E%E7%AB%AF/1.htm">后端</a> <div>内存对齐和填充规则对齐要求:每个数据类型的起始地址必须是其大小的倍数。int8(1字节):不需要对齐。int16(2字节):起始地址必须是2的倍数。int32(4字节):起始地址必须是4的倍数。int64(8字节):起始地址必须是8的倍数。填充规则:如果当前偏移量不是下一个成员变量对齐要求的倍数,则编译器会在前一个成员后插入“填充字节”,以使下一个成员的起始地址满足对齐要求。结构体总大小:结构体的</div> </li> <li><a href="/article/1897329814642159616.htm" title="第五阶段【MySQL数据库:常用开发语言连接MySQL】01:使用Shell操作MySQL" target="_blank">第五阶段【MySQL数据库:常用开发语言连接MySQL】01:使用Shell操作MySQL</a> <span class="text-muted">做一个有趣的人Zz</span> <a class="tag" taget="_blank" href="/search/DBA%E6%95%B0%E6%8D%AE%E5%B7%A5%E7%A8%8B%E5%B8%88%E6%88%90%E9%95%BF%E4%B9%8B%E8%B7%AF/1.htm">DBA数据工程师成长之路</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E5%BA%93/1.htm">数据库</a><a class="tag" taget="_blank" href="/search/mysql/1.htm">mysql</a><a class="tag" taget="_blank" href="/search/dba/1.htm">dba</a> <div>一、创建测试用户和测试表CREATEUSER'shell_rw'@'%'IDENTIFIEDBY'admin';GRANTcreate,alter,insert,delete,select</div> </li> <li><a href="/article/1897314306542858240.htm" title="132java ssm springboot基于大数据的吉林省农村产权交易数据分析可视化平台系统(源码+文档+运行视频+讲解视频)" target="_blank">132java ssm springboot基于大数据的吉林省农村产权交易数据分析可视化平台系统(源码+文档+运行视频+讲解视频)</a> <span class="text-muted">QQ2279239102</span> <a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/boot/1.htm">boot</a><a class="tag" taget="_blank" href="/search/%E5%A4%A7%E6%95%B0%E6%8D%AE/1.htm">大数据</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90/1.htm">数据分析</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/maven/1.htm">maven</a><a class="tag" taget="_blank" href="/search/vue.js/1.htm">vue.js</a> <div>文章目录系列文章目录目的前言一、详细视频演示二、项目部分实现截图三、技术栈后端框架springboot前端框架vue持久层框架MyBaitsPlus系统测试四、代码参考源码获取目的摘要:本文介绍了基于JavaSSM和SpringBoot开发的吉林省农村产权交易数据分析可视化平台系统,为农村产权交易市场提供决策支持。系统前端利用HTML、CSS和JavaScript构建直观的可视化界面,后端运用Ja</div> </li> <li><a href="/article/1897287063032623104.htm" title="CORS 跨域资源共享" target="_blank">CORS 跨域资源共享</a> <span class="text-muted">disgare</span> <a class="tag" taget="_blank" href="/search/%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9F%BA%E7%A1%80/1.htm">计算机基础</a><a class="tag" taget="_blank" href="/search/csrf/1.htm">csrf</a><a class="tag" taget="_blank" href="/search/%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">服务器</a><a class="tag" taget="_blank" href="/search/ajax/1.htm">ajax</a> <div>CORS跨域资源共享跨域同源策略为什么有跨域限制发生跨域时,允许进行的操作跨域限制的资源处理跨域常用的方法CORS请求简单请求复杂请求后端支持跨域代码支持CORS支持JSONP支持ng浏览器一般使用CORS(跨域资源共享)来处理跨域问题。同源导致了不同源数据不能互相访问,而在开发中我们很多时候需要用第一个页面的脚本访问第二个页面里的数据,所以制定了一些允许跨域的策略跨域同源策略在了解真正的网络攻击</div> </li> <li><a href="/article/1897282523700981760.htm" title="从 ISO 到 GMT+8:Vue 前端时间格式的奇妙之旅!" target="_blank">从 ISO 到 GMT+8:Vue 前端时间格式的奇妙之旅!</a> <span class="text-muted">小丁学Java</span> <a class="tag" taget="_blank" href="/search/%E4%BA%A7%E5%93%81%E8%B5%84%E8%B4%A8%E7%AE%A1%E7%90%86%E7%B3%BB%E7%BB%9F/1.htm">产品资质管理系统</a><a class="tag" taget="_blank" href="/search/vue.js/1.htm">vue.js</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a> <div>“从ISO到GMT+8:Vue前端时间格式的奇妙之旅!”作者:小丁|日期:2025-03-04嘿,各位前端探险家!今天我要带你们走进一个时间格式的“变形记”:从前端的ISO格式("2025-03-04T07:28:17.058Z")到后端期待的GMT+8"yyyy-MM-ddHH:mm:ss"("2025-03-0415:28:17"),我如何在Vue项目中搞定这个“时间旅行”的小挑战?别急,</div> </li> <li><a href="/article/1897282396806508544.htm" title="Getter 和 Setter 的秘密:为什么 Location 不翼而飞?" target="_blank">Getter 和 Setter 的秘密:为什么 Location 不翼而飞?</a> <span class="text-muted">小丁学Java</span> <a class="tag" taget="_blank" href="/search/%E4%BA%A7%E5%93%81%E8%B5%84%E8%B4%A8%E7%AE%A1%E7%90%86%E7%B3%BB%E7%BB%9F/1.htm">产品资质管理系统</a><a class="tag" taget="_blank" href="/search/%E7%8A%B6%E6%80%81%E6%A8%A1%E5%BC%8F/1.htm">状态模式</a> <div>标题:Getter和Setter的秘密:为什么Location不翼而飞?️♂️嘿,各位技术探险家!欢迎来到今天的博客,今天我们要聊一个既基础又充满戏剧性的问题:为什么我的location字段没有getter方法就没法返回给前端?这是一个发生在Java和JSON序列化世界中的小小“悬案”,让我们戴上侦探帽,一起揭开它的神秘面纱吧!故事的开端:Location的失踪案想象一下,你是一个勤奋的后端开发</div> </li> <li><a href="/article/1897250128981454848.htm" title="Github 2024-08-16Java开源项目日报 Top10" target="_blank">Github 2024-08-16Java开源项目日报 Top10</a> <span class="text-muted">老孙正经胡说</span> <a class="tag" taget="_blank" href="/search/github/1.htm">github</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E6%BA%90/1.htm">开源</a><a class="tag" taget="_blank" href="/search/Github%E8%B6%8B%E5%8A%BF%E5%88%86%E6%9E%90/1.htm">Github趋势分析</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE/1.htm">开源项目</a><a class="tag" taget="_blank" href="/search/Python/1.htm">Python</a><a class="tag" taget="_blank" href="/search/Golang/1.htm">Golang</a> <div>根据GithubTrendings的统计,今日(2024-08-16统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下:开发语言项目数量Java项目10TypeScript项目1Ruby项目1ApacheDubbo:高性能的Java开源RPC框架创建周期:4441天开发语言:Java协议类型:ApacheLicense2.0Star数量:40303个Fork数量:26383次关注人数</div> </li> <li><a href="/article/1897250129711263744.htm" title="Github 2024-09-30 开源项目周报 Top15" target="_blank">Github 2024-09-30 开源项目周报 Top15</a> <span class="text-muted">老孙正经胡说</span> <a class="tag" taget="_blank" href="/search/github/1.htm">github</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E6%BA%90/1.htm">开源</a><a class="tag" taget="_blank" href="/search/Github%E8%B6%8B%E5%8A%BF%E5%88%86%E6%9E%90/1.htm">Github趋势分析</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE/1.htm">开源项目</a><a class="tag" taget="_blank" href="/search/Python/1.htm">Python</a><a class="tag" taget="_blank" href="/search/Golang/1.htm">Golang</a> <div>根据GithubTrendings的统计,本周(2024-09-30统计)共有15个项目上榜。根据开发语言中项目的数量,汇总情况如下:开发语言项目数量Python项目7JupyterNotebook项目2Dart项目1Ruby项目1HTML项目1C#项目1TypeScript项目1Rust项目1非开发语言项目1AutoGPT:人工智能革命的先锋创建周期:486天开发语言:Python协议类型:MI</div> </li> <li><a href="/article/1897248995550490624.htm" title="vue+element ui多表单统一验证" target="_blank">vue+element ui多表单统一验证</a> <span class="text-muted">as-鲨鱼辣椒</span> <a class="tag" taget="_blank" href="/search/js/1.htm">js</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/html/1.htm">html</a><a class="tag" taget="_blank" href="/search/vue.js/1.htm">vue.js</a><a class="tag" taget="_blank" href="/search/elementui/1.htm">elementui</a> <div>场景每个产品对应多个应用,当对产品进行修改的时,可同时修改应用相关参数,从后端接受应用参数集合,每个应用生成动态表单,点击确认按钮,对所有表单进行统一认证,验证成功再将数据保存至数据库;HTML代码//特别注意ref这里的index,填在$refs['xx']--xx就是这个ref<el-form:ref="'app</div> </li> <li><a href="/article/110.htm" title="强大的销售团队背后 竟然是大数据分析的身影" target="_blank">强大的销售团队背后 竟然是大数据分析的身影</a> <span class="text-muted">蓝儿唯美</span> <a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90/1.htm">数据分析</a> <div>Mark Roberge是HubSpot的首席财务官,在招聘销售职位时使用了大量数据分析。但是科技并没有挤走直觉。 大家都知道数理学家实际上已经渗透到了各行各业。这些热衷数据的人们通过处理数据理解商业流程的各个方面,以重组弱点,增强优势。 Mark Roberge是美国HubSpot公司的首席财务官,HubSpot公司在构架集客营销现象方面出过一份力——因此他也是一位数理学家。他使用数据分析 </div> </li> <li><a href="/article/237.htm" title="Haproxy+Keepalived高可用双机单活" target="_blank">Haproxy+Keepalived高可用双机单活</a> <span class="text-muted">bylijinnan</span> <a class="tag" taget="_blank" href="/search/%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/1.htm">负载均衡</a><a class="tag" taget="_blank" href="/search/keepalived/1.htm">keepalived</a><a class="tag" taget="_blank" href="/search/haproxy/1.htm">haproxy</a><a class="tag" taget="_blank" href="/search/%E9%AB%98%E5%8F%AF%E7%94%A8/1.htm">高可用</a> <div>我们的应用MyApp不支持集群,但要求双机单活(两台机器:master和slave): 1.正常情况下,只有master启动MyApp并提供服务 2.当master发生故障时,slave自动启动本机的MyApp,同时虚拟IP漂移至slave,保持对外提供服务的IP和端口不变 F5据说也能满足上面的需求,但F5的通常用法都是双机双活,单活的话还没研究过 服务器资源 10.7</div> </li> <li><a href="/article/364.htm" title="eclipse编辑器中文乱码问题解决" target="_blank">eclipse编辑器中文乱码问题解决</a> <span class="text-muted">0624chenhong</span> <a class="tag" taget="_blank" href="/search/eclipse%E4%B9%B1%E7%A0%81/1.htm">eclipse乱码</a> <div>使用Eclipse编辑文件经常出现中文乱码或者文件中有中文不能保存的问题,Eclipse提供了灵活的设置文件编码格式的选项,我们可以通过设置编码 格式解决乱码问题。在Eclipse可以从几个层面设置编码格式:Workspace、Project、Content Type、File 本文以Eclipse 3.3(英文)为例加以说明: 1. 设置Workspace的编码格式: Windows-&g</div> </li> <li><a href="/article/491.htm" title="基础篇--resources资源" target="_blank">基础篇--resources资源</a> <span class="text-muted">不懂事的小屁孩</span> <a class="tag" taget="_blank" href="/search/android/1.htm">android</a> <div>最近一直在做java开发,偶尔敲点android代码,突然发现有些基础给忘记了,今天用半天时间温顾一下resources的资源。 String.xml 字符串资源 涉及国际化问题 http://www.2cto.com/kf/201302/190394.html string-array</div> </li> <li><a href="/article/618.htm" title="接上篇补上window平台自动上传证书文件的批处理问卷" target="_blank">接上篇补上window平台自动上传证书文件的批处理问卷</a> <span class="text-muted">酷的飞上天空</span> <a class="tag" taget="_blank" href="/search/window/1.htm">window</a> <div> @echo off : host=服务器证书域名或ip,需要和部署时服务器的域名或ip一致 ou=公司名称, o=公司名称 set host=localhost set ou=localhost set o=localhost set password=123456 set validity=3650 set salias=s</div> </li> <li><a href="/article/745.htm" title="企业物联网大潮涌动:如何做好准备?" target="_blank">企业物联网大潮涌动:如何做好准备?</a> <span class="text-muted">蓝儿唯美</span> <a class="tag" taget="_blank" href="/search/%E4%BC%81%E4%B8%9A/1.htm">企业</a> <div>物联网的可能性也许是无限的。要找出架构师可以做好准备的领域然后利用日益连接的世界。 尽管物联网(IoT)还很新,企业架构师现在也应该为一个连接更加紧密的未来做好计划,而不是跟上闸门被打开后的集成挑战。“问题不在于物联网正在进入哪些领域,而是哪些地方物联网没有在企业推进,” Gartner研究总监Mike Walker说。 Gartner预测到2020年物联网设备安装量将达260亿,这些设备在全</div> </li> <li><a href="/article/872.htm" title="spring学习——数据库(mybatis持久化框架配置)" target="_blank">spring学习——数据库(mybatis持久化框架配置)</a> <span class="text-muted">a-john</span> <a class="tag" taget="_blank" href="/search/mybatis/1.htm">mybatis</a> <div>Spring提供了一组数据访问框架,集成了多种数据访问技术。无论是JDBC,iBATIS(mybatis)还是Hibernate,Spring都能够帮助消除持久化代码中单调枯燥的数据访问逻辑。可以依赖Spring来处理底层的数据访问。 mybatis是一种Spring持久化框架,要使用mybatis,就要做好相应的配置: 1,配置数据源。有很多数据源可以选择,如:DBCP,JDBC,aliba</div> </li> <li><a href="/article/999.htm" title="Java静态代理、动态代理实例" target="_blank">Java静态代理、动态代理实例</a> <span class="text-muted">aijuans</span> <a class="tag" taget="_blank" href="/search/Java%E9%9D%99%E6%80%81%E4%BB%A3%E7%90%86/1.htm">Java静态代理</a> <div> 采用Java代理模式,代理类通过调用委托类对象的方法,来提供特定的服务。委托类需要实现一个业务接口,代理类返回委托类的实例接口对象。 按照代理类的创建时期,可以分为:静态代理和动态代理。 所谓静态代理: 指程序员创建好代理类,编译时直接生成代理类的字节码文件。 所谓动态代理: 在程序运行时,通过反射机制动态生成代理类。 一、静态代理类实例: 1、Serivce.ja</div> </li> <li><a href="/article/1126.htm" title="Struts1与Struts2的12点区别" target="_blank">Struts1与Struts2的12点区别</a> <span class="text-muted">asia007</span> <a class="tag" taget="_blank" href="/search/Struts1%E4%B8%8EStruts2/1.htm">Struts1与Struts2</a> <div>1) 在Action实现类方面的对比:Struts 1要求Action类继承一个抽象基类;Struts 1的一个具体问题是使用抽象类编程而不是接口。Struts 2 Action类可以实现一个Action接口,也可以实现其他接口,使可选和定制的服务成为可能。Struts 2提供一个ActionSupport基类去实现常用的接口。即使Action接口不是必须实现的,只有一个包含execute方法的P</div> </li> <li><a href="/article/1253.htm" title="初学者要多看看帮助文档 不要用js来写Jquery的代码" target="_blank">初学者要多看看帮助文档 不要用js来写Jquery的代码</a> <span class="text-muted">百合不是茶</span> <a class="tag" taget="_blank" href="/search/jquery/1.htm">jquery</a><a class="tag" taget="_blank" href="/search/js/1.htm">js</a> <div>解析json数据的时候需要将解析的数据写到文本框中, 出现了用js来写Jquery代码的问题; 1, JQuery的赋值 有问题 代码如下: data.username 表示的是: 网易 $("#use</div> </li> <li><a href="/article/1380.htm" title="经理怎么和员工搞好关系和信任" target="_blank">经理怎么和员工搞好关系和信任</a> <span class="text-muted">bijian1013</span> <a class="tag" taget="_blank" href="/search/%E5%9B%A2%E9%98%9F/1.htm">团队</a><a class="tag" taget="_blank" href="/search/%E9%A1%B9%E7%9B%AE%E7%AE%A1%E7%90%86/1.htm">项目管理</a><a class="tag" taget="_blank" href="/search/%E7%AE%A1%E7%90%86/1.htm">管理</a> <div> 产品经理应该有坚实的专业基础,这里的基础包括产品方向和产品策略的把握,包括设计,也包括对技术的理解和见识,对运营和市场的敏感,以及良好的沟通和协作能力。换言之,既然是产品经理,整个产品的方方面面都应该能摸得出门道。这也不懂那也不懂,如何让人信服?如何让自己懂?就是不断学习,不仅仅从书本中,更从平时和各种角色的沟通</div> </li> <li><a href="/article/1507.htm" title="如何为rich:tree不同类型节点设置右键菜单" target="_blank">如何为rich:tree不同类型节点设置右键菜单</a> <span class="text-muted">sunjing</span> <a class="tag" taget="_blank" href="/search/contextMenu/1.htm">contextMenu</a><a class="tag" taget="_blank" href="/search/tree/1.htm">tree</a><a class="tag" taget="_blank" href="/search/Richfaces/1.htm">Richfaces</a> <div>组合使用target和targetSelector就可以啦,如下: <rich:tree id="ruleTree" value="#{treeAction.ruleTree}" var="node" nodeType="#{node.type}" selectionChangeListener=&qu</div> </li> <li><a href="/article/1634.htm" title="【Redis二】Redis2.8.17搭建主从复制环境" target="_blank">【Redis二】Redis2.8.17搭建主从复制环境</a> <span class="text-muted">bit1129</span> <a class="tag" taget="_blank" href="/search/redis/1.htm">redis</a> <div>开始使用Redis2.8.17 Redis第一篇在Redis2.4.5上搭建主从复制环境,对它的主从复制的工作机制,真正的惊呆了。不知道Redis2.8.17的主从复制机制是怎样的,Redis到了2.4.5这个版本,主从复制还做成那样,Impossible is nothing! 本篇把主从复制环境再搭一遍看看效果,这次在Unbuntu上用官方支持的版本。 Ubuntu上安装Red</div> </li> <li><a href="/article/1761.htm" title="JSONObject转换JSON--将Date转换为指定格式" target="_blank">JSONObject转换JSON--将Date转换为指定格式</a> <span class="text-muted">白糖_</span> <a class="tag" taget="_blank" href="/search/JSONObject/1.htm">JSONObject</a> <div>项目中,经常会用JSONObject插件将JavaBean或List<JavaBean>转换为JSON格式的字符串,而JavaBean的属性有时候会有java.util.Date这个类型的时间对象,这时JSONObject默认会将Date属性转换成这样的格式: {"nanos":0,"time":-27076233600000,</div> </li> <li><a href="/article/1888.htm" title="JavaScript语言精粹读书笔记" target="_blank">JavaScript语言精粹读书笔记</a> <span class="text-muted">braveCS</span> <a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a> <div>【经典用法】: //①定义新方法 Function .prototype.method=function(name, func){ this.prototype[name]=func; return this; } //②给Object增加一个create方法,这个方法创建一个使用原对</div> </li> <li><a href="/article/2015.htm" title="编程之美-找符合条件的整数 用字符串来表示大整数避免溢出" target="_blank">编程之美-找符合条件的整数 用字符串来表示大整数避免溢出</a> <span class="text-muted">bylijinnan</span> <a class="tag" taget="_blank" href="/search/%E7%BC%96%E7%A8%8B%E4%B9%8B%E7%BE%8E/1.htm">编程之美</a> <div> import java.util.LinkedList; public class FindInteger { /** * 编程之美 找符合条件的整数 用字符串来表示大整数避免溢出 * 题目:任意给定一个正整数N,求一个最小的正整数M(M>1),使得N*M的十进制表示形式里只含有1和0 * * 假设当前正在搜索由0,1组成的K位十进制数</div> </li> <li><a href="/article/2142.htm" title="读书笔记" target="_blank">读书笔记</a> <span class="text-muted">chengxuyuancsdn</span> <a class="tag" taget="_blank" href="/search/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/1.htm">读书笔记</a> <div>1、Struts访问资源 2、把静态参数传递给一个动作 3、<result>type属性 4、s:iterator、s:if c:forEach 5、StringBuilder和StringBuffer 6、spring配置拦截器 1、访问资源 (1)通过ServletActionContext对象和实现ServletContextAware,ServletReque</div> </li> <li><a href="/article/2269.htm" title="[通讯与电力]光网城市建设的一些问题" target="_blank">[通讯与电力]光网城市建设的一些问题</a> <span class="text-muted">comsci</span> <a class="tag" taget="_blank" href="/search/%E9%97%AE%E9%A2%98/1.htm">问题</a> <div> 信号防护的问题,前面已经说过了,这里要说光网交换机与市电保障的关系 我们过去用的ADSL线路,因为是电话线,在小区和街道电力中断的情况下,只要在家里用笔记本电脑+蓄电池,连接ADSL,同样可以上网........ </div> </li> <li><a href="/article/2396.htm" title="oracle 空间RESUMABLE" target="_blank">oracle 空间RESUMABLE</a> <span class="text-muted">daizj</span> <a class="tag" taget="_blank" href="/search/oracle/1.htm">oracle</a><a class="tag" taget="_blank" href="/search/%E7%A9%BA%E9%97%B4%E4%B8%8D%E8%B6%B3/1.htm">空间不足</a><a class="tag" taget="_blank" href="/search/RESUMABLE/1.htm">RESUMABLE</a><a class="tag" taget="_blank" href="/search/%E9%94%99%E8%AF%AF%E6%8C%82%E8%B5%B7/1.htm">错误挂起</a> <div>空间RESUMABLE操作 转 Oracle从9i开始引入这个功能,当出现空间不足等相关的错误时,Oracle可以不是马上返回错误信息,并回滚当前的操作,而是将操作挂起,直到挂起时间超过RESUMABLE TIMEOUT,或者空间不足的错误被解决。 这一篇简单介绍空间RESUMABLE的例子。 第一次碰到这个特性是在一次安装9i数据库的过程中,在利用D</div> </li> <li><a href="/article/2523.htm" title="重构第一次写的线程池" target="_blank">重构第一次写的线程池</a> <span class="text-muted">dieslrae</span> <a class="tag" taget="_blank" href="/search/%E7%BA%BF%E7%A8%8B%E6%B1%A0+python/1.htm">线程池 python</a> <div>最近没有什么学习欲望,修改之前的线程池的计划一直搁置,这几天比较闲,还是做了一次重构,由之前的2个类拆分为现在的4个类. 1、首先是工作线程类:TaskThread,此类为一个工作线程,用于完成一个工作任务,提供等待(wait),继续(proceed),绑定任务(bindTask)等方法 #!/usr/bin/env python # -*- coding:utf8 -*- </div> </li> <li><a href="/article/2650.htm" title="C语言学习六指针" target="_blank">C语言学习六指针</a> <span class="text-muted">dcj3sjt126com</span> <a class="tag" taget="_blank" href="/search/c/1.htm">c</a> <div>初识指针,简单示例程序: /* 指针就是地址,地址就是指针 地址就是内存单元的编号 指针变量是存放地址的变量 指针和指针变量是两个不同的概念 但是要注意: 通常我们叙述时会把指针变量简称为指针,实际它们含义并不一样 */ # include <stdio.h> int main(void) { int * p; // p是变量的名字, int * </div> </li> <li><a href="/article/2777.htm" title="yii2 beforeSave afterSave beforeDelete" target="_blank">yii2 beforeSave afterSave beforeDelete</a> <span class="text-muted">dcj3sjt126com</span> <a class="tag" taget="_blank" href="/search/delete/1.htm">delete</a> <div>public function afterSave($insert, $changedAttributes) { parent::afterSave($insert, $changedAttributes); if($insert) { //这里是新增数据 } else { //这里是更新数据 } } </div> </li> <li><a href="/article/2904.htm" title="timertask" target="_blank">timertask</a> <span class="text-muted">shuizhaosi888</span> <a class="tag" taget="_blank" href="/search/timertask/1.htm">timertask</a> <div>java.util.Timer timer = new java.util.Timer(true); // true 说明这个timer以daemon方式运行(优先级低, // 程序结束timer也自动结束),注意,javax.swing // 包中也有一个Timer类,如果import中用到swing包, // 要注意名字的冲突。 TimerTask task = new</div> </li> <li><a href="/article/3031.htm" title="Spring Security(13)——session管理" target="_blank">Spring Security(13)——session管理</a> <span class="text-muted">234390216</span> <a class="tag" taget="_blank" href="/search/session/1.htm">session</a><a class="tag" taget="_blank" href="/search/Spring+Security/1.htm">Spring Security</a><a class="tag" taget="_blank" href="/search/%E6%94%BB%E5%87%BB%E4%BF%9D%E6%8A%A4/1.htm">攻击保护</a><a class="tag" taget="_blank" href="/search/%E8%B6%85%E6%97%B6/1.htm">超时</a> <div>session管理 目录 1.1 检测session超时 1.2 concurrency-control 1.3 session 固定攻击保护 </div> </li> <li><a href="/article/3158.htm" title="公司项目NODEJS实践0.3[ mongo / session ...]" target="_blank">公司项目NODEJS实践0.3[ mongo / session ...]</a> <span class="text-muted">逐行分析JS源代码</span> <a class="tag" taget="_blank" href="/search/mongodb/1.htm">mongodb</a><a class="tag" taget="_blank" href="/search/session/1.htm">session</a><a class="tag" taget="_blank" href="/search/nodejs/1.htm">nodejs</a> <div> http://www.upopen.cn 一、前言 书接上回,我们搭建了WEB服务端路由、模板等功能,完成了register 通过ajax与后端的通信,今天主要完成数据与mongodb的存取,实现注册 / 登录 /</div> </li> <li><a href="/article/3285.htm" title="pojo.vo.po.domain区别" target="_blank">pojo.vo.po.domain区别</a> <span class="text-muted">LiaoJuncai</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/VO/1.htm">VO</a><a class="tag" taget="_blank" href="/search/POJO/1.htm">POJO</a><a class="tag" taget="_blank" href="/search/javabean/1.htm">javabean</a><a class="tag" taget="_blank" href="/search/domain/1.htm">domain</a> <div> POJO = "Plain Old Java Object",是MartinFowler等发明的一个术语,用来表示普通的Java对象,不是JavaBean, EntityBean 或者 SessionBean。POJO不但当任何特殊的角色,也不实现任何特殊的Java框架的接口如,EJB, JDBC等等。 即POJO是一个简单的普通的Java对象,它包含业务逻辑</div> </li> <li><a href="/article/3412.htm" title="Windows Error Code" target="_blank">Windows Error Code</a> <span class="text-muted">OhMyCC</span> <a class="tag" taget="_blank" href="/search/windows/1.htm">windows</a> <div>0 操作成功完成. 1 功能错误. 2 系统找不到指定的文件. 3 系统找不到指定的路径. 4 系统无法打开文件. 5 拒绝访问. 6 句柄无效. 7 存储控制块被损坏. 8 存储空间不足, 无法处理此命令. 9 存储控制块地址无效. 10 环境错误. 11 试图加载格式错误的程序. 12 访问码无效. 13 数据无效. 14 存储器不足, 无法完成此操作. 15 系</div> </li> <li><a href="/article/3539.htm" title="在storm集群环境下发布Topology" target="_blank">在storm集群环境下发布Topology</a> <span class="text-muted">roadrunners</span> <a class="tag" taget="_blank" href="/search/%E9%9B%86%E7%BE%A4/1.htm">集群</a><a class="tag" taget="_blank" href="/search/storm/1.htm">storm</a><a class="tag" taget="_blank" href="/search/topology/1.htm">topology</a><a class="tag" taget="_blank" href="/search/spout/1.htm">spout</a><a class="tag" taget="_blank" href="/search/bolt/1.htm">bolt</a> <div>storm的topology设计和开发就略过了。本章主要来说说如何在storm的集群环境中,通过storm的管理命令来发布和管理集群中的topology。 1、打包 打包插件是使用maven提供的maven-shade-plugin,详细见maven-shade-plugin。 <plugin> <groupId>org.apache.maven.</div> </li> <li><a href="/article/3666.htm" title="为什么不允许代码里出现“魔数”" target="_blank">为什么不允许代码里出现“魔数”</a> <span class="text-muted">tomcat_oracle</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a> <div> 在一个新项目中,我最先做的事情之一,就是建立使用诸如Checkstyle和Findbugs之类工具的准则。目的是制定一些代码规范,以及避免通过静态代码分析就能够检测到的bug。 迟早会有人给出案例说这样太离谱了。其中的一个案例是Checkstyle的魔数检查。它会对任何没有定义常量就使用的数字字面量给出警告,除了-1、0、1和2。 很多开发者在这个检查方面都有问题,这可以从结果</div> </li> <li><a href="/article/3793.htm" title="zoj 3511 Cake Robbery(线段树)" target="_blank">zoj 3511 Cake Robbery(线段树)</a> <span class="text-muted">阿尔萨斯</span> <a class="tag" taget="_blank" href="/search/%E7%BA%BF%E6%AE%B5%E6%A0%91/1.htm">线段树</a> <div> 题目链接:zoj 3511 Cake Robbery 题目大意:就是有一个N边形的蛋糕,切M刀,从中挑选一块边数最多的,保证没有两条边重叠。 解题思路:有多少个顶点即为有多少条边,所以直接按照切刀切掉点的个数排序,然后用线段树维护剩下的还有哪些点。 #include <cstdio> #include <cstring> #include <vector&</div> </li> </ul> </div> </div> </div> <div> <div class="container"> <div class="indexes"> <strong>按字母分类:</strong> <a href="/tags/A/1.htm" target="_blank">A</a><a href="/tags/B/1.htm" target="_blank">B</a><a href="/tags/C/1.htm" target="_blank">C</a><a href="/tags/D/1.htm" target="_blank">D</a><a href="/tags/E/1.htm" target="_blank">E</a><a href="/tags/F/1.htm" target="_blank">F</a><a href="/tags/G/1.htm" target="_blank">G</a><a href="/tags/H/1.htm" target="_blank">H</a><a href="/tags/I/1.htm" target="_blank">I</a><a href="/tags/J/1.htm" target="_blank">J</a><a href="/tags/K/1.htm" target="_blank">K</a><a href="/tags/L/1.htm" target="_blank">L</a><a href="/tags/M/1.htm" target="_blank">M</a><a href="/tags/N/1.htm" target="_blank">N</a><a href="/tags/O/1.htm" target="_blank">O</a><a href="/tags/P/1.htm" target="_blank">P</a><a href="/tags/Q/1.htm" target="_blank">Q</a><a href="/tags/R/1.htm" target="_blank">R</a><a href="/tags/S/1.htm" target="_blank">S</a><a href="/tags/T/1.htm" target="_blank">T</a><a href="/tags/U/1.htm" target="_blank">U</a><a href="/tags/V/1.htm" target="_blank">V</a><a href="/tags/W/1.htm" target="_blank">W</a><a href="/tags/X/1.htm" target="_blank">X</a><a href="/tags/Y/1.htm" target="_blank">Y</a><a href="/tags/Z/1.htm" target="_blank">Z</a><a href="/tags/0/1.htm" target="_blank">其他</a> </div> </div> </div> <footer id="footer" class="mb30 mt30"> <div class="container"> <div class="footBglm"> <a target="_blank" href="/">首页</a> - <a target="_blank" href="/custom/about.htm">关于我们</a> - <a target="_blank" href="/search/Java/1.htm">站内搜索</a> - <a target="_blank" href="/sitemap.txt">Sitemap</a> - <a target="_blank" href="/custom/delete.htm">侵权投诉</a> </div> <div class="copyright">版权所有 IT知识库 CopyRight © 2000-2050 E-COM-NET.COM , All Rights Reserved. <!-- <a href="https://beian.miit.gov.cn/" rel="nofollow" target="_blank">京ICP备09083238号</a><br>--> </div> </div> </footer> <!-- 代码高亮 --> <script type="text/javascript" src="/static/syntaxhighlighter/scripts/shCore.js"></script> <script type="text/javascript" src="/static/syntaxhighlighter/scripts/shLegacy.js"></script> <script type="text/javascript" src="/static/syntaxhighlighter/scripts/shAutoloader.js"></script> <link type="text/css" rel="stylesheet" href="/static/syntaxhighlighter/styles/shCoreDefault.css"/> <script type="text/javascript" src="/static/syntaxhighlighter/src/my_start_1.js"></script> </body> </html>