mirror of
https://github.com/bjdgyc/anylink.git
synced 2025-09-17 08:57:16 +08:00
增加证书认证开关,优化前端下载证书的错误处理
This commit is contained in:
@@ -47,6 +47,7 @@ type ServerConfig struct {
|
|||||||
DbSource string `json:"db_source"`
|
DbSource string `json:"db_source"`
|
||||||
CertFile string `json:"cert_file"`
|
CertFile string `json:"cert_file"`
|
||||||
CertKey string `json:"cert_key"`
|
CertKey string `json:"cert_key"`
|
||||||
|
AuthAloneCert bool `json:"auth_alone_cert"`
|
||||||
ClientCertCAFile string `json:"client_ca_file"`
|
ClientCertCAFile string `json:"client_ca_file"`
|
||||||
ClientCertCAKeyFile string `json:"client_ca_key_file"`
|
ClientCertCAKeyFile string `json:"client_ca_key_file"`
|
||||||
FilesPath string `json:"files_path"`
|
FilesPath string `json:"files_path"`
|
||||||
|
@@ -33,6 +33,7 @@ var configs = []config{
|
|||||||
{Typ: cfgStr, Name: "db_source", Usage: "数据库source", ValStr: "./conf/anylink.db"},
|
{Typ: cfgStr, Name: "db_source", Usage: "数据库source", ValStr: "./conf/anylink.db"},
|
||||||
{Typ: cfgStr, Name: "cert_file", Usage: "证书文件", ValStr: "./conf/vpn_cert.pem"},
|
{Typ: cfgStr, Name: "cert_file", Usage: "证书文件", ValStr: "./conf/vpn_cert.pem"},
|
||||||
{Typ: cfgStr, Name: "cert_key", Usage: "证书密钥", ValStr: "./conf/vpn_cert.key"},
|
{Typ: cfgStr, Name: "cert_key", Usage: "证书密钥", ValStr: "./conf/vpn_cert.key"},
|
||||||
|
{Typ: cfgBool, Name: "auth_alone_cert", Usage: "启用独立证书验证", ValBool: false},
|
||||||
{Typ: cfgStr, Name: "client_ca_file", Usage: "客户端证书CA证书", ValStr: "./conf/client_ca.pem"},
|
{Typ: cfgStr, Name: "client_ca_file", Usage: "客户端证书CA证书", ValStr: "./conf/client_ca.pem"},
|
||||||
{Typ: cfgStr, Name: "client_ca_key_file", Usage: "客户端证书CA密钥", ValStr: "./conf/client_ca.key"},
|
{Typ: cfgStr, Name: "client_ca_key_file", Usage: "客户端证书CA密钥", ValStr: "./conf/client_ca.key"},
|
||||||
{Typ: cfgStr, Name: "files_path", Usage: "外部下载文件路径", ValStr: "./conf/files"},
|
{Typ: cfgStr, Name: "files_path", Usage: "外部下载文件路径", ValStr: "./conf/files"},
|
||||||
|
@@ -9,6 +9,16 @@ db_source = "./conf/anylink.db"
|
|||||||
#证书文件 使用跟nginx一样的证书即可
|
#证书文件 使用跟nginx一样的证书即可
|
||||||
cert_file = "./conf/vpn_cert.pem"
|
cert_file = "./conf/vpn_cert.pem"
|
||||||
cert_key = "./conf/vpn_cert.key"
|
cert_key = "./conf/vpn_cert.key"
|
||||||
|
|
||||||
|
#是否启用独立证书验证,开启后客户端连接需要携带证书
|
||||||
|
#如果不开启则使用用户名密码验证
|
||||||
|
auth_alone_cert = false
|
||||||
|
|
||||||
|
#客户端证书CA证书
|
||||||
|
client_cert_ca_file = "./conf/client_ca.pem"
|
||||||
|
#客户端证书CA密钥
|
||||||
|
client_cert_ca_key_file = "./conf/client_ca.key"
|
||||||
|
|
||||||
files_path = "./conf/files"
|
files_path = "./conf/files"
|
||||||
profile = "./conf/profile.xml"
|
profile = "./conf/profile.xml"
|
||||||
#profile name(用于区分不同服务端的配置)
|
#profile name(用于区分不同服务端的配置)
|
||||||
@@ -52,7 +62,7 @@ admin_addr = ":8800"
|
|||||||
proxy_protocol = false
|
proxy_protocol = false
|
||||||
|
|
||||||
#开启go标准库http.Server的日志
|
#开启go标准库http.Server的日志
|
||||||
http_server_log=false
|
http_server_log = false
|
||||||
|
|
||||||
#虚拟网络类型[tun macvtap tap]
|
#虚拟网络类型[tun macvtap tap]
|
||||||
link_mode = "tun"
|
link_mode = "tun"
|
||||||
|
@@ -11,11 +11,6 @@ cert_file = "./conf/vpn_cert.pem"
|
|||||||
cert_key = "./conf/vpn_cert.key"
|
cert_key = "./conf/vpn_cert.key"
|
||||||
files_path = "./conf/files"
|
files_path = "./conf/files"
|
||||||
|
|
||||||
#客户端证书CA证书
|
|
||||||
client_cert_ca_file = "./conf/client_ca.pem"
|
|
||||||
#客户端证书CA密钥
|
|
||||||
client_cert_ca_key_file = "./conf/client_ca.key"
|
|
||||||
|
|
||||||
#日志目录,默认为空写入标准输出
|
#日志目录,默认为空写入标准输出
|
||||||
#log_path = "./log"
|
#log_path = "./log"
|
||||||
log_level = "debug"
|
log_level = "debug"
|
||||||
|
@@ -78,6 +78,7 @@ func LinkAuth(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 检查客户端证书认证
|
// 检查客户端证书认证
|
||||||
|
if base.Cfg.AuthAloneCert {
|
||||||
if r.TLS != nil && len(r.TLS.PeerCertificates) > 0 {
|
if r.TLS != nil && len(r.TLS.PeerCertificates) > 0 {
|
||||||
clientCert := r.TLS.PeerCertificates[0]
|
clientCert := r.TLS.PeerCertificates[0]
|
||||||
username := clientCert.Subject.CommonName
|
username := clientCert.Subject.CommonName
|
||||||
@@ -87,7 +88,8 @@ func LinkAuth(w http.ResponseWriter, r *http.Request) {
|
|||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
ua.Username = username
|
||||||
|
ua.GroupName = groupname
|
||||||
// 验证证书有效性和用户状态
|
// 验证证书有效性和用户状态
|
||||||
if dbdata.ValidateClientCert(clientCert, userAgent) {
|
if dbdata.ValidateClientCert(clientCert, userAgent) {
|
||||||
// 证书认证成功,创建会话
|
// 证书认证成功,创建会话
|
||||||
@@ -95,16 +97,25 @@ func LinkAuth(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
sessionData.ClientRequest.GroupSelect = groupname
|
sessionData.ClientRequest.GroupSelect = groupname
|
||||||
sessionData.ClientRequest.Auth.Username = username
|
sessionData.ClientRequest.Auth.Username = username
|
||||||
ua.Username = username
|
|
||||||
ua.GroupName = groupname
|
|
||||||
ua.Info = "用户通过证书认证登录"
|
ua.Info = "用户通过证书认证登录"
|
||||||
ua.Status = dbdata.UserConnected
|
ua.Status = dbdata.UserConnected
|
||||||
dbdata.UserActLogIns.Add(*ua, userAgent)
|
dbdata.UserActLogIns.Add(*ua, userAgent)
|
||||||
|
|
||||||
CreateSession(w, r, sessionData)
|
CreateSession(w, r, sessionData)
|
||||||
return
|
return
|
||||||
|
} else {
|
||||||
|
ua.Info = "客户端证书验证失败"
|
||||||
|
ua.Status = dbdata.UserAuthFail
|
||||||
|
dbdata.UserActLogIns.Add(*ua, userAgent)
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusForbidden)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
base.Warn("启用了独立证书验证,但用户未提供有效证书")
|
||||||
|
w.WriteHeader(http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if cr.Type == "init" {
|
if cr.Type == "init" {
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
|
@@ -66,13 +66,16 @@ func startTls() {
|
|||||||
NextProtos: []string{"http/1.1"},
|
NextProtos: []string{"http/1.1"},
|
||||||
MinVersion: tls.VersionTLS12,
|
MinVersion: tls.VersionTLS12,
|
||||||
CipherSuites: selectedCipherSuites,
|
CipherSuites: selectedCipherSuites,
|
||||||
ClientAuth: tls.VerifyClientCertIfGiven, // 验证客户端证书
|
|
||||||
ClientCAs: dbdata.LoadClientCAPool(), // 加载客户端CA证书
|
|
||||||
GetCertificate: func(chi *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
GetCertificate: func(chi *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||||
base.Trace("GetCertificate ServerName", chi.ServerName)
|
base.Trace("GetCertificate ServerName", chi.ServerName)
|
||||||
return dbdata.GetCertificateBySNI(chi.ServerName)
|
return dbdata.GetCertificateBySNI(chi.ServerName)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
// 开启证书认证
|
||||||
|
if base.Cfg.AuthAloneCert {
|
||||||
|
tlsConfig.ClientAuth = tls.VerifyClientCertIfGiven // 验证客户端证书
|
||||||
|
tlsConfig.ClientCAs = dbdata.LoadClientCAPool() // 加载客户端CA证书
|
||||||
|
}
|
||||||
srv := &http.Server{
|
srv := &http.Server{
|
||||||
Addr: addr,
|
Addr: addr,
|
||||||
Handler: initRoute(),
|
Handler: initRoute(),
|
||||||
|
@@ -561,6 +561,20 @@ export default {
|
|||||||
url: '/set/client_cert/download?' + params.toString(),
|
url: '/set/client_cert/download?' + params.toString(),
|
||||||
responseType: 'blob'
|
responseType: 'blob'
|
||||||
}).then(response => {
|
}).then(response => {
|
||||||
|
const contentType = response.headers['content-type'];
|
||||||
|
if (contentType && contentType.includes('application/json')) {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = () => {
|
||||||
|
try {
|
||||||
|
const errorData = JSON.parse(reader.result);
|
||||||
|
this.$message.error(errorData.msg || '证书下载失败');
|
||||||
|
} catch (e) {
|
||||||
|
this.$message.error('证书下载失败');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
reader.readAsText(response.data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
const blob = new Blob([response.data], { type: 'application/x-pkcs12' });
|
const blob = new Blob([response.data], { type: 'application/x-pkcs12' });
|
||||||
const url = window.URL.createObjectURL(blob);
|
const url = window.URL.createObjectURL(blob);
|
||||||
const link = document.createElement('a');
|
const link = document.createElement('a');
|
||||||
@@ -572,8 +586,8 @@ export default {
|
|||||||
window.URL.revokeObjectURL(url);
|
window.URL.revokeObjectURL(url);
|
||||||
this.$message.success('证书下载成功');
|
this.$message.success('证书下载成功');
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
if (error.response && error.response.data && error.response.data.msg) {
|
if (error.response && error.response.data) {
|
||||||
this.$message.error(error.response.data.msg);
|
this.$message.error(error.response.data.msg || '证书下载失败');
|
||||||
} else {
|
} else {
|
||||||
this.$message.error('证书下载失败');
|
this.$message.error('证书下载失败');
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user