mirror of
https://github.com/bjdgyc/anylink.git
synced 2025-09-09 01:21:23 +08:00
db.go文件冲突已解决
This commit is contained in:
@@ -3,6 +3,7 @@ package admin
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"runtime/debug"
|
||||
"time"
|
||||
|
||||
"github.com/bjdgyc/anylink/base"
|
||||
@@ -93,7 +94,7 @@ func authMiddleware(next http.Handler) http.Handler {
|
||||
route := mux.CurrentRoute(r)
|
||||
name := route.GetName()
|
||||
// fmt.Println("bb", r.URL.Path, name)
|
||||
if utils.InArrStr([]string{"login", "index", "static"}, name) {
|
||||
if utils.InArrStr([]string{"login", "index", "static", "reset_password", "forgot_password"}, name) {
|
||||
// 不进行鉴权
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
@@ -120,3 +121,17 @@ func authMiddleware(next http.Handler) http.Handler {
|
||||
|
||||
return http.HandlerFunc(fn)
|
||||
}
|
||||
|
||||
func recoverHttp(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
stack := debug.Stack()
|
||||
base.Error(err, string(stack))
|
||||
// http.Error(w, "Internal Server Error", 500)
|
||||
RespError(w, 500, "Internal Server Error")
|
||||
}
|
||||
}()
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
295
server/admin/api_reset_password.go
Normal file
295
server/admin/api_reset_password.go
Normal file
@@ -0,0 +1,295 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/bjdgyc/anylink/base"
|
||||
"github.com/bjdgyc/anylink/dbdata"
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/smtp"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var forgot_interval_time = 1 * 60 // 1分钟的间隔时间(单位:秒)
|
||||
var reset_interval_time = 30 // 密码重置有效期(单位: 分钟)
|
||||
|
||||
func GenerateResetToken(userID int) (string, error) {
|
||||
fmt.Println(base.Cfg.JwtSecret)
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
||||
"user_id": userID,
|
||||
"exp": time.Now().Add(time.Duration(reset_interval_time) * time.Minute).Unix(),
|
||||
})
|
||||
return token.SignedString([]byte(base.Cfg.JwtSecret))
|
||||
}
|
||||
|
||||
type CustomClaims struct {
|
||||
UserID int `json:"user_id"`
|
||||
jwt.StandardClaims
|
||||
}
|
||||
|
||||
func ValidateResetToken(tokenString string) (*CustomClaims, error) {
|
||||
token, err := jwt.ParseWithClaims(
|
||||
tokenString,
|
||||
&CustomClaims{},
|
||||
func(token *jwt.Token) (interface{}, error) {
|
||||
return []byte(base.Cfg.JwtSecret), nil
|
||||
},
|
||||
)
|
||||
|
||||
if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
|
||||
return claims, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 重置密码函数
|
||||
func ResetPassword(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
Token string `json:"token" binding:"required"`
|
||||
Password string `json:"password" binding:"required"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
RespError(w, RespInternalErr, "json 解析失败")
|
||||
return
|
||||
} else {
|
||||
// 验证token,并获取UserID
|
||||
claims, valid_err := ValidateResetToken(req.Token)
|
||||
if valid_err != nil {
|
||||
msg := fmt.Sprintf("验证失败, 重置链接已过期, 请重新申请。错误信息: %v", valid_err)
|
||||
RespError(w, RespInternalErr, msg)
|
||||
return
|
||||
}
|
||||
// 根据验证后的UserId 来更新用户表的密码
|
||||
s := &dbdata.User{PinCode: req.Password}
|
||||
update_err := dbdata.Update("Id", claims.UserID, s)
|
||||
if update_err != nil {
|
||||
RespError(w, RespInternalErr, "更新密码失败")
|
||||
return
|
||||
}
|
||||
fmt.Println("更新密码成功")
|
||||
// 删除重置记录表的数据
|
||||
reset := dbdata.PasswordReset{UserId: claims.UserID}
|
||||
del_err := dbdata.Del(&reset)
|
||||
if del_err != nil {
|
||||
fmt.Println("删除记录失败", del_err)
|
||||
} else {
|
||||
fmt.Println("删除验证记录成功,UserId", claims.UserID)
|
||||
}
|
||||
RespSucess(w, "密码重置成功")
|
||||
}
|
||||
}
|
||||
|
||||
func ForgotPassword(w http.ResponseWriter, r *http.Request) {
|
||||
// 校验 Content-Type
|
||||
if r.Header.Get("Content-Type") != "application/json" {
|
||||
RespError(w, RespInternalErr, "仅支持 JSON 格式")
|
||||
return
|
||||
}
|
||||
// 解析json数据
|
||||
var req struct {
|
||||
Email string `json:"email"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
RespError(w, RespInternalErr, "Json 解析失败")
|
||||
return
|
||||
}
|
||||
// 获取用户数据
|
||||
user := &dbdata.User{}
|
||||
err := dbdata.One("Email", req.Email, user)
|
||||
if err != nil {
|
||||
RespError(w, RespInternalErr, "用户不存在 输入的地址:"+req.Email)
|
||||
return
|
||||
}
|
||||
// 检查上一次请求的时间
|
||||
reset := &dbdata.PasswordReset{}
|
||||
err = dbdata.One("user_id", user.Id, reset)
|
||||
if err != nil {
|
||||
if err == dbdata.ErrNotFound {
|
||||
base.Info("此账号没有重置记录")
|
||||
} else {
|
||||
RespError(w, RespInternalErr, "查询重置记录失败", err.Error())
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// 检查时间间隔是否足够
|
||||
currentTime := int(time.Now().Unix())
|
||||
lastRequestTime := reset.LastRequestTime
|
||||
if currentTime-lastRequestTime < forgot_interval_time {
|
||||
msg := fmt.Sprintf("重复的重置操作,请等待%d秒后进行重置申请", forgot_interval_time)
|
||||
RespError(w, RespInternalErr, msg)
|
||||
return
|
||||
}
|
||||
}
|
||||
// 生成新的重置令牌
|
||||
token, err := GenerateResetToken(user.Id)
|
||||
if err != nil {
|
||||
RespError(w, RespInternalErr, "生成token失败")
|
||||
return
|
||||
}
|
||||
// 开始更新或插入重置记录
|
||||
reset.ExpiresAt = int(time.Now().Add(time.Duration(reset_interval_time) * time.Minute).Unix())
|
||||
reset.LastRequestTime = int(time.Now().Unix())
|
||||
reset.UserId = user.Id
|
||||
|
||||
if reset.Token == "" {
|
||||
// 如果 Token 为空,说明是第一次请求,插入新记录
|
||||
reset.Token = token
|
||||
err = dbdata.Add(reset)
|
||||
} else {
|
||||
// 如果 Token 不为空,说明记录已存在,更新记录
|
||||
reset.Token = token
|
||||
err = dbdata.Update("user_id", reset.UserId, reset)
|
||||
}
|
||||
if err != nil {
|
||||
RespError(w, RespInternalErr, "更新重置记录失败")
|
||||
return
|
||||
}
|
||||
|
||||
// 获取邮箱服务器的配置
|
||||
dataSmtp := &dbdata.SettingSmtp{}
|
||||
serverConf := &dbdata.SettingOther{}
|
||||
|
||||
mail_err := dbdata.SettingGet(dataSmtp)
|
||||
server_err := dbdata.SettingGet(serverConf)
|
||||
if server_err != nil {
|
||||
RespError(w, RespInternalErr, "获取服务器配置失败,请检查后台对外地址的配置")
|
||||
return
|
||||
}
|
||||
if mail_err != nil {
|
||||
RespError(w, RespInternalErr, "获取邮箱配置失败,请检查邮箱配置")
|
||||
return
|
||||
}
|
||||
|
||||
// 构建重置链接
|
||||
parsedURL, err := url.Parse(serverConf.LinkAddr)
|
||||
if err != nil {
|
||||
RespError(w, RespInternalErr, "解析URL失败,可能是后台配置的对外地址不符合要求")
|
||||
return
|
||||
}
|
||||
|
||||
scheme := parsedURL.Scheme
|
||||
hosts := parsedURL.Hostname()
|
||||
fullURL := fmt.Sprintf("%s://%s%s", scheme, hosts, base.Cfg.AdminAddr)
|
||||
resetLink := fmt.Sprintf("%s/ui/#resetPassword?token=%s", fullURL, reset.Token)
|
||||
|
||||
// 发送邮件
|
||||
mail_user := dataSmtp.Username
|
||||
password := dataSmtp.Password
|
||||
host := dataSmtp.Host
|
||||
port := strconv.Itoa(dataSmtp.Port)
|
||||
toUser := req.Email
|
||||
|
||||
message := fmt.Sprintf("这个是vpn账号的重置链接:%s \n密码有效期%d分钟,超时请重新提交", resetLink, reset_interval_time)
|
||||
if err := SendResetMail(mail_user, password, toUser, "vpn密码重置", message, host, port, false); err != nil {
|
||||
RespError(w, RespInternalErr, "邮箱发送失败")
|
||||
return
|
||||
}
|
||||
RespSucess(w, "邮箱发送成功")
|
||||
}
|
||||
|
||||
func SendResetMail(email, password, toEmail, subject, body, host, port string, isHtml bool) (err error) {
|
||||
header := make(map[string]string)
|
||||
header["From"] = "<" + email + ">"
|
||||
header["To"] = toEmail
|
||||
header["Subject"] = subject
|
||||
if isHtml {
|
||||
header["Content-Type"] = "text/html; charset=UTF-8"
|
||||
} else {
|
||||
header["Content-Type"] = "text/plain; charset=UTF-8"
|
||||
}
|
||||
|
||||
message := ""
|
||||
for k, v := range header {
|
||||
message += fmt.Sprintf("%s: %s\r\n", k, v)
|
||||
}
|
||||
message += "\r\n" + body
|
||||
|
||||
auth := smtp.PlainAuth(
|
||||
"",
|
||||
email,
|
||||
password,
|
||||
host,
|
||||
)
|
||||
|
||||
toEmails := strings.Split(toEmail, ";")
|
||||
fmt.Println(email, password, toEmails, host, port)
|
||||
err = sendMailUsingTLS(
|
||||
fmt.Sprintf("%s:%s", host, port),
|
||||
auth,
|
||||
email,
|
||||
toEmails,
|
||||
[]byte(message),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("send_mail_error: %v, %v", toEmails, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// return a smtp client
|
||||
func dial(addr string) (*smtp.Client, error) {
|
||||
conn, err := tls.Dial("tcp", addr, nil)
|
||||
if err != nil {
|
||||
fmt.Println("Dialing Error:", err)
|
||||
return nil, err
|
||||
}
|
||||
//分解主机端口字符串
|
||||
host, _, _ := net.SplitHostPort(addr)
|
||||
return smtp.NewClient(conn, host)
|
||||
}
|
||||
|
||||
func sendMailUsingTLS(addr string, auth smtp.Auth, from string,
|
||||
to []string, msg []byte) (err error) {
|
||||
|
||||
//create smtp client
|
||||
c, err := dial(addr)
|
||||
if err != nil {
|
||||
fmt.Println("Create smpt client error:", err)
|
||||
return err
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
if auth != nil {
|
||||
if ok, _ := c.Extension("AUTH"); ok {
|
||||
if err = c.Auth(auth); err != nil {
|
||||
fmt.Println("Error during AUTH", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err = c.Mail(from); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, addr := range to {
|
||||
if err = c.Rcpt(addr); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
w, err := c.Data()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = w.Write(msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = w.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.Quit()
|
||||
}
|
||||
|
@@ -21,6 +21,7 @@ var UiData embed.FS
|
||||
func StartAdmin() {
|
||||
|
||||
r := mux.NewRouter()
|
||||
r.Use(recoverHttp, authMiddleware, handlers.CompressHandler)
|
||||
// 所有路由添加安全头
|
||||
r.Use(func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
@@ -29,8 +30,6 @@ func StartAdmin() {
|
||||
next.ServeHTTP(w, req)
|
||||
})
|
||||
})
|
||||
r.Use(authMiddleware)
|
||||
r.Use(handlers.CompressHandler)
|
||||
|
||||
// 监控检测
|
||||
r.HandleFunc("/status.html", func(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -86,6 +85,8 @@ func StartAdmin() {
|
||||
r.HandleFunc("/user/policy/detail", PolicyDetail)
|
||||
r.HandleFunc("/user/policy/set", PolicySet)
|
||||
r.HandleFunc("/user/policy/del", PolicyDel)
|
||||
r.HandleFunc("/user/reset/forgotPassword", ForgotPassword).Name("forgot_password")
|
||||
r.HandleFunc("/user/reset/resetPassword", ResetPassword).Name("reset_password")
|
||||
|
||||
r.HandleFunc("/group/list", GroupList)
|
||||
r.HandleFunc("/group/names", GroupNames)
|
||||
|
@@ -51,6 +51,9 @@ admin_addr = ":8800"
|
||||
#开启tcp proxy protocol协议
|
||||
proxy_protocol = false
|
||||
|
||||
#开启go标准库http.Server的日志
|
||||
http_server_log=false
|
||||
|
||||
#虚拟网络类型[tun macvtap tap]
|
||||
link_mode = "tun"
|
||||
|
||||
|
@@ -118,3 +118,9 @@ type StatsMem struct {
|
||||
Percent float64 `json:"percent" xorm:"Float"`
|
||||
CreatedAt time.Time `json:"created_at" xorm:"DateTime created index"`
|
||||
}
|
||||
|
||||
type PasswordReset struct {
|
||||
Token string `json:"token" xorm:"varchar(60) not null unique"`
|
||||
UserId int `json:"id" xorm:"not null"`
|
||||
ExpiresAt int `json:"expires_at" xorm:"not null"`
|
||||
}
|
||||
|
@@ -75,8 +75,11 @@ func (auth AuthRadius) checkUser(name, pwd string, g *Group, ext map[string]inte
|
||||
return fmt.Errorf("%s %s", name, "Radius set nasip 出现错误")
|
||||
}
|
||||
}
|
||||
macAddr := ext["mac_addr"].(string)
|
||||
base.Trace("AuthRadius", ext, macAddr)
|
||||
macAddr := ""
|
||||
if ext["mac_addr"] != nil {
|
||||
macAddr = ext["mac_addr"].(string)
|
||||
base.Trace("AuthRadius", ext, macAddr)
|
||||
}
|
||||
if macAddr != "" {
|
||||
err = rfc2865.CallingStationID_AddString(packet, macAddr)
|
||||
if err != nil {
|
||||
@@ -94,5 +97,4 @@ func (auth AuthRadius) checkUser(name, pwd string, g *Group, ext map[string]inte
|
||||
return fmt.Errorf("%s %s", name, "Radius:用户名或密码错误")
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user