Browse Source

dao login

master
tangmingyou 4 years ago
parent
commit
38953c0db6
  1. 2
      .gitignore
  2. 28
      cmd/main.go
  3. 19
      etc/config.toml
  4. 0
      etc/config.yaml
  5. 5
      go.mod
  6. 14
      go.sum
  7. 50
      internal/conf/conf.go
  8. 29
      internal/dao/dao.go
  9. 11
      internal/model/entity/entity.go
  10. 14
      internal/model/entity/user.go
  11. 12
      internal/model/message.go
  12. 5
      internal/model/message/alert.go
  13. 9
      internal/model/message/auth.go
  14. 25
      internal/model/message/message.go
  15. 96
      internal/server/auth.go
  16. 12
      internal/server/server.go
  17. 42
      internal/server/ws.go
  18. 147
      internal/service/auth.go
  19. 48
      internal/service/user.go
  20. 2
      internal/session/net_account.go
  21. 2
      internal/session/net_client.go
  22. 2
      internal/session/player.go
  23. 8
      internal/session/subject.go
  24. 10
      tool/collect/validator.go
  25. 8
      tool/security/aes.go
  26. 43
      ws.html

2
.gitignore vendored

@ -14,6 +14,7 @@
*.patch
*.tmp
*.swp
*.db
# system ignore
.DS_Store
@ -26,3 +27,4 @@ Thumbs.db
/target
/configs
/dist
/etc/texas-poker.db

28
cmd/main.go

@ -5,25 +5,25 @@ import (
"crypto/rsa"
"crypto/sha256"
"encoding/json"
"flag"
"fmt"
"texas-poker-bk/internal/model"
"texas-poker-bk/internal/conf"
"texas-poker-bk/internal/model/message"
"texas-poker-bk/internal/server"
"time"
)
var confPath string
func init() {
flag.StringVar(&confPath, "conf", "comet-example.toml", "default config path.")
func startServer() {
httpServer := server.NewServer()
err := httpServer.Run(conf.Conf.Http.Addr)
if err != nil {
panic(err)
}
}
func main() {
// testRsa()
}
func testToml() {
startServer()
// testRsa()
}
func testRsa() {
@ -44,11 +44,11 @@ func testRsa() {
}
func testMsgCodec() {
identity := &model.Identity{Token: "12312312asahjasd"}
identity := &message.ReqIdentity{Token: "12312312asahjasd"}
bytes, _ := json.Marshal(identity)
// msg: int32:route:msg
msg := &model.Message{T: "identity", Ms: time.Now().UnixMilli(), D: bytes}
// message: int32:route:message
msg := &message.Message{T: "identity", Ms: time.Now().UnixMilli(), D: bytes}
b, _ := json.Marshal(msg)
fmt.Println(msg)

19
etc/config.toml

@ -1,7 +1,18 @@
[http]
addr = ":9999"
[auth]
ignoreUrl = [
aesTokenKey = "9Nz3Y6DES3msAFndz4QsJAECUIFkf+KKaRa+jRnNALk="
ignoreUrl = [
"/ws",
"/auth/captcha"
]
"/auth/captcha",
"/auth/authorize",
"/user/findByName",
]
# 需要输入图形验证码的url
captchaUrl = [
"/auth/authorize"
]
[sqlite]
dbPath = "etc/texas-poker.db"

0
etc/config.yaml

5
go.mod

@ -16,9 +16,12 @@ require (
github.com/gorilla/securecookie v1.1.1 // indirect
github.com/gorilla/sessions v1.2.1 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
@ -29,4 +32,6 @@ require (
golang.org/x/text v0.5.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gorm.io/driver/sqlite v1.4.4 // indirect
gorm.io/gorm v1.24.3 // indirect
)

14
go.sum

@ -31,6 +31,12 @@ github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7Fsg
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas=
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@ -43,6 +49,9 @@ github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
@ -96,3 +105,8 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/sqlite v1.4.4 h1:gIufGoR0dQzjkyqDyYSCvsYR6fba1Gw5YKDqKeChxFc=
gorm.io/driver/sqlite v1.4.4/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI=
gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
gorm.io/gorm v1.24.3 h1:WL2ifUmzR/SLp85CSURAfybcHnGZ+yLSGSxgYXlFBHg=
gorm.io/gorm v1.24.3/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=

50
internal/conf/conf.go

@ -0,0 +1,50 @@
package conf
import (
"flag"
"github.com/BurntSushi/toml"
)
var (
confPath string
Conf *Config
)
func init() {
// go run xx -conf=xx.toml
flag.StringVar(&confPath, "conf", "etc/config.toml", "default config path.")
Conf = Default()
_, err := toml.DecodeFile("etc/config.toml", Conf)
if err != nil {
panic(err)
}
// fmt.Printf("%v", Conf)
}
func Default() *Config {
return &Config{
Auth: &Auth{
IgnoreUrl: []string{},
},
}
}
type Config struct {
Auth *Auth
Http *Http
Sqlite *Sqlite
}
type Auth struct {
AesTokenKey string
IgnoreUrl []string
}
type Http struct {
Addr string
}
type Sqlite struct {
DbPath string
}

29
internal/dao/dao.go

@ -0,0 +1,29 @@
package dao
import (
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"texas-poker-bk/internal/conf"
)
var Dao *Persistent
type Persistent struct {
Sqlite *gorm.DB
}
func init() {
Dao = &Persistent{
Sqlite: newSqlite(),
}
}
func newSqlite() (db *gorm.DB) {
db, err := gorm.Open(sqlite.Open(conf.Conf.Sqlite.DbPath), &gorm.Config{
QueryFields: true,
})
if err != nil {
panic(err)
}
return db
}

11
internal/model/entity/entity.go

@ -0,0 +1,11 @@
package entity
import (
"time"
)
// Id int64 `gorm:"column:id;primaryKey;autoIncrement:true" json:"id"`
type Model struct {
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
}

14
internal/model/entity/user.go

@ -0,0 +1,14 @@
package entity
type User struct {
Model
Id int64 `gorm:"column:id;primaryKey;autoIncrement:true" json:"id"`
Username string `gorm:"column:username" json:"username"` // comment:用户名
Password string `gorm:"column:password" json:"password"` // comment:密码
Nickname string `gorm:"column:nickname" json:"nickname"` // comment:昵称
Version int64 `gorm:"column:version" json:"version"` // comment:更新版本锁
}
func (u *User) TableName() string {
return "t_user"
}

12
internal/model/message.go

@ -1,12 +0,0 @@
package model
type Message struct {
T string `json:"t"` // action 时间主题
Ms int64 `json:"ms"` // 毫秒时间戳
D []byte `json:"d"` // 消息数据
}
// Identity 身份认证消息
type Identity struct {
Token string `json:"t"`
}

5
internal/model/message/alert.go

@ -0,0 +1,5 @@
package message
// Alert 弹窗消息
type Alert struct {
}

9
internal/model/message/auth.go

@ -0,0 +1,9 @@
package message
// ReqIdentity 身份认证消息
type ReqIdentity struct {
Token string `json:"t"`
}
type ResIdentity struct {
}

25
internal/model/message/message.go

@ -0,0 +1,25 @@
package message
type Message struct {
T string `json:"t"` // action 时间主题
Ms int64 `json:"ms"` // 毫秒时间戳
D []byte `json:"d"` // 消息数据
}
type Res struct {
Code int32 `json:"code"`
Msg string `json:"msg"`
Data any `json:"data"`
}
func NewRes(code int32, msg string, data any) *Res {
return &Res{Code: code, Msg: msg, Data: data}
}
func NewResSuccess(data any) *Res {
return &Res{Code: 0, Data: data}
}
func NewResFail(msg string) *Res {
return &Res{Code: 0, Msg: msg}
}

96
internal/server/auth.go

@ -1,96 +0,0 @@
package server
import (
"encoding/base64"
"encoding/json"
"github.com/gin-gonic/gin"
"net/http"
"texas-poker-bk/tool/security"
)
var (
SubjectKey = "auth:subject"
sc = &scEtc{} // 加密配置
)
// Subject 认证对象
type Subject struct {
Id int64 `json:"id,omitempty"` // 用户id
Name string `json:"name,omitempty"` // 用户名
Time int64 `json:"time,omitempty"` // 生成时间戳
}
type scEtc struct {
securityKey string
keyBytes []byte
}
func init() {
sc.securityKey = "9Nz3Y6DES3msAFndz4QsJAECUIFkf+KKaRa+jRnNALk="
keyBytes, err := base64.URLEncoding.DecodeString(sc.securityKey)
if err != nil {
panic(err)
}
sc.keyBytes = keyBytes
}
// SubjectAuthFilter 认证过滤器
func SubjectAuthFilter(ctx *gin.Context) {
auth := ctx.GetHeader("Authorization")
if auth == "" {
// TODO config ignore urls
//if isIgnoreUrl(ctx.Request.URL.Path) {
// return
//}
ctx.JSON(http.StatusUnauthorized, gin.H{"msg": "请登录后进行操作"})
ctx.Abort()
return
}
defer func() {
// 捕获aes解析错误
if r := recover(); r != nil {
ctx.JSON(http.StatusUnauthorized, gin.H{"msg": "认证失败,请重新登录"})
ctx.Abort()
}
}()
// TODO 这里返回 error,不然后捕获后续执行 handler 的 panic
subject := DecodeSubject(auth)
ctx.Set(SubjectKey, subject)
}
// EncodeSubject 对subject对象aesCBC加密并返回base64Std编码的 token
// subject 客户端对象
func EncodeSubject(subject *Subject) string {
bytes, err := json.Marshal(subject)
if err != nil {
panic(err)
}
encode := security.EncryptAesCBC(bytes, sc.keyBytes)
return base64.URLEncoding.EncodeToString(encode)
}
// DecodeSubject 解码并解密token返回subject对象
// auth base64Std 编码的 token
func DecodeSubject(auth string) *Subject {
bytes, err := base64.URLEncoding.DecodeString(auth)
if err != nil {
panic(err)
}
decode := security.DecryptAesCBC(bytes, sc.keyBytes)
subject := &Subject{}
err = json.Unmarshal(decode, subject)
if err != nil {
panic(err)
}
return subject
}
// GetSubject 从请求上下文中获取客户端对象, 请求经过了认证过滤器
func GetSubject(ctx *gin.Context) *Subject {
subject, exists := ctx.Get(SubjectKey)
if !exists {
return nil
}
return subject.(*Subject)
}

12
internal/server/server.go

@ -5,17 +5,23 @@ import (
"texas-poker-bk/internal/service"
)
func Start() *gin.Engine {
func NewServer() *gin.Engine {
server := gin.Default()
// token认证过滤器
server.Use(service.SubjectAuthFilter)
// session storage
server.Use(SessionStore("golang-tech-stack"))
// websocket
server.GET("/ws", Upgrade)
auth := server.Group("/auth")
auth.GET("/captcha", service.Captcha) // 验证码
auth.POST("/authorize", service.Authorize) // 登录或注册认证
// session storage
server.Use(SessionStore("golang-tech-stack"))
user := server.Group("/user")
user.GET("/findByName", service.FindUserByName)
return server
}

42
internal/server/ws.go

@ -7,21 +7,33 @@ import (
"github.com/gorilla/websocket"
"log"
"net/http"
"texas-poker-bk/internal/model"
"texas-poker-bk/internal/subject"
"texas-poker-bk/internal/model/message"
"texas-poker-bk/internal/service"
"texas-poker-bk/internal/session"
"time"
)
func Upgrade(ctx *gin.Context) {
token, exists := ctx.GetQuery("t")
if !exists {
ctx.JSON(http.StatusUnauthorized, gin.H{"message": "unauth"})
return
}
subject, err := service.DecodeSubject(token)
if err != nil {
ctx.JSON(http.StatusUnauthorized, gin.H{"message": "token failed"})
return
}
conn, err := upgradeWs(ctx.Writer, ctx.Request)
if err != nil {
fmt.Println(err)
http.Error(ctx.Writer, "Not a websocket handshake", 400)
return
}
client := subject.NewNetClient(conn)
client := session.NewNetClient(conn)
go handleNetClient(client)
go handleNetClient(client, subject)
}
func upgradeWs(resWriter http.ResponseWriter, req *http.Request) (*websocket.Conn, error) {
@ -37,7 +49,7 @@ func upgradeWs(resWriter http.ResponseWriter, req *http.Request) (*websocket.Con
}
// 处理新建的websocket
func handleNetClient(client *subject.NetClient) {
func handleNetClient(client *session.NetClient, subject *session.Subject) {
//defer (func() {
// client.Close("conn finish")
//})()
@ -52,7 +64,7 @@ func handleNetClient(client *subject.NetClient) {
client.Close("read conn err:" + err.Error())
return
}
msg := &model.Message{}
msg := &message.Message{}
err = json.Unmarshal(bytes, msg)
if err != nil {
@ -60,11 +72,23 @@ func handleNetClient(client *subject.NetClient) {
continue
}
// TODO 认证消息(return -> account msg route)
// 认证消息(return -> account message route)
switch msg.T {
case "identify":
param := &model.Identity{}
param := &message.ReqIdentity{}
_ = json.Unmarshal(msg.D, param)
subject, err := service.DecodeSubject(param.Token)
if err != nil {
res := message.NewResFail(err.Error())
resBytes, _ := json.Marshal(res)
_ = client.Conn.WriteMessage(websocket.TextMessage, resBytes)
// 认证失败关闭连接
client.Close("authorization fail!")
return
}
fmt.Printf("subject: %v", subject)
default:
client.Close("wrong message")
}
@ -75,6 +99,6 @@ func handleNetClient(client *subject.NetClient) {
}
// handleIdentity TODO 通过 token 认证连接的身份
func handleIdentity(identity *model.Identity, client *subject.NetClient) {
func handleIdentity(identity *message.ReqIdentity, client *session.NetClient) {
}

147
internal/service/auth.go

@ -1,12 +1,151 @@
package service
import "github.com/gin-gonic/gin"
import (
"encoding/base64"
"encoding/json"
"github.com/gin-gonic/gin"
"net/http"
"texas-poker-bk/internal/conf"
"texas-poker-bk/internal/model/entity"
"texas-poker-bk/internal/session"
"texas-poker-bk/tool/collect"
"texas-poker-bk/tool/security"
"time"
)
// Login 登录或注册
func Login(ctx *gin.Context) {
// http: 登录注册、创建房间
// ws: 大厅、房间、房间状态
var (
SubjectKey = "auth:session"
aesTokenKeyBytes []byte // token aes 加密 key
)
// Authorize 登录或注册
func Authorize(ctx *gin.Context) {
username := ctx.PostForm("username")
password := ctx.PostForm("password")
//username := ctx.Param("username")
//password := ctx.Param("password")
if username == "" || password == "" {
ctx.JSON(http.StatusBadRequest, gin.H{"message": "用户名或密码不能为空"})
return
}
user := userDao.FindUserByName(username)
if user == nil {
// 注册
user = registerUser(username, password)
} else {
// 校验密码
if user.Password != password {
ctx.JSON(http.StatusUnauthorized, gin.H{"message": "用户名或密码有误"})
return
}
}
sub := &session.Subject{Id: user.Id, Name: user.Username, Time: time.Now().UnixMilli()}
token, err := EncodeSubject(sub)
if err != nil {
ctx.JSON(http.StatusUnauthorized, gin.H{"message": err.Error()})
return
}
ctx.JSON(http.StatusOK, gin.H{"data": gin.H{"token": token}})
}
// 注册用户到DB
func registerUser(username string, password string) *entity.User {
user := &entity.User{
Username: username,
Password: password,
Nickname: username,
}
// TODO error
userDao.dao.Save(user)
return user
}
func init() {
keyBytes, err := base64.StdEncoding.DecodeString(conf.Conf.Auth.AesTokenKey)
if err != nil {
panic(err)
}
aesTokenKeyBytes = keyBytes
}
// SubjectAuthFilter 认证过滤器
func SubjectAuthFilter(ctx *gin.Context) {
// 检查是不需要登录的 url
// ignore auth urls
if collect.IsNotEmptySlice(conf.Conf.Auth.IgnoreUrl) {
for _, ignoreUrl := range conf.Conf.Auth.IgnoreUrl {
if ignoreUrl == ctx.Request.URL.Path {
return
}
}
}
auth := ctx.GetHeader("Authorization")
if auth == "" {
ctx.JSON(http.StatusUnauthorized, gin.H{"message": "请登录后进行操作"})
ctx.Abort()
}
defer func() {
// 捕获aes解析错误
if r := recover(); r != nil {
ctx.JSON(http.StatusUnauthorized, gin.H{"message": "认证失败,请重新登录"})
ctx.Abort()
}
}()
// 这里返回 error,不然后捕获后续执行 handler 的 panic
subject, err := DecodeSubject(auth)
if err != nil {
ctx.JSON(http.StatusUnauthorized, gin.H{"message": "认证失败,请重新登录"})
ctx.Abort()
} else {
// 设置token用户到请求上下文
ctx.Set(SubjectKey, subject)
}
}
// EncodeSubject 对subject对象aesCBC加密并返回base64Std编码的 token
// session 客户端对象
func EncodeSubject(subject *session.Subject) (string, error) {
bytes, err := json.Marshal(subject)
if err != nil {
return "", err
}
encode, err := security.EncryptAesCBC(bytes, aesTokenKeyBytes)
if err != nil {
return "", err
}
return base64.URLEncoding.EncodeToString(encode), nil
}
func Register() {
// DecodeSubject 解码并解密token返回subject对象
// auth base64Std 编码的 token
func DecodeSubject(auth string) (*session.Subject, error) {
bytes, err := base64.URLEncoding.DecodeString(auth)
if err != nil {
return nil, err
}
decode := security.DecryptAesCBC(bytes, aesTokenKeyBytes)
subject := &session.Subject{}
err = json.Unmarshal(decode, subject)
if err != nil {
return nil, err
}
return subject, nil
}
// GetSubject 从请求上下文中获取客户端对象, 请求经过了认证过滤器
func GetSubject(ctx *gin.Context) *session.Subject {
subject, exists := ctx.Get(SubjectKey)
if !exists {
panic("request subject not exists!")
}
return subject.(*session.Subject)
}

48
internal/service/user.go

@ -0,0 +1,48 @@
package service
import (
"fmt"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"net/http"
"texas-poker-bk/internal/dao"
"texas-poker-bk/internal/model/entity"
)
var userDao *User = &User{dao: dao.Dao.Sqlite}
func init() {
err := dao.Dao.Sqlite.AutoMigrate(&entity.User{})
if err != nil {
fmt.Println("user table", err)
}
}
type User struct {
dao *gorm.DB
}
func FindUserByName(ctx *gin.Context) {
username := ctx.Param("username")
user := userDao.FindUserByName(username)
if user == nil {
ctx.JSON(http.StatusOK, gin.H{
"message": "not found",
})
return
}
ctx.JSON(http.StatusOK, gin.H{
"message": "ok",
"data": user,
})
}
func (u *User) FindUserByName(username string) *entity.User {
user := &entity.User{}
tx := u.dao.Model(user).Where("username=?", username).Limit(1).Scan(user)
// 查询出结果时 tx.RowsAffected 固定=1
if tx.RowsAffected == 0 {
return nil
}
return user
}

2
internal/subject/net_account.go → internal/session/net_account.go

@ -1,4 +1,4 @@
package subject
package session
// NetAccount 已认证的长连接用户
type NetAccount struct {

2
internal/subject/net_client.go → internal/session/net_client.go

@ -1,4 +1,4 @@
package subject
package session
import (
"github.com/gorilla/websocket"

2
internal/subject/player.go → internal/session/player.go

@ -1,4 +1,4 @@
package subject
package session
// Player 玩家: table,balance,(hand card)
type Player struct {

8
internal/session/subject.go

@ -0,0 +1,8 @@
package session
// Subject 认证对象
type Subject struct {
Id int64 `json:"id,omitempty"` // 用户id
Name string `json:"name,omitempty"` // 用户名
Time int64 `json:"time,omitempty"` // 生成时间戳
}

10
tool/collect/validator.go

@ -0,0 +1,10 @@
package collect
// IsEmptySlice 切片是否为空
func IsEmptySlice[T any](slice []T) bool {
return slice == nil || len(slice) == 0
}
func IsNotEmptySlice[T any](slice []T) bool {
return !IsEmptySlice(slice)
}

8
tool/security/aes.go

@ -27,7 +27,7 @@ func TestAes() {
fmt.Println(key)
text := []byte("you are my sunshine!")
encrypt := EncryptAesCBC(text, key)
encrypt, _ := EncryptAesCBC(text, key)
fmt.Println(encrypt)
fmt.Println(base64.URLEncoding.EncodeToString(text))
@ -52,10 +52,10 @@ func getAesKey(keySize int) []byte {
// EncryptAesCBC
// src -> 要加密的原文
// key -> 秘钥, 和加密秘钥相同, 大小为: 8byte
func EncryptAesCBC(src, key []byte) []byte {
func EncryptAesCBC(src, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
return nil, err
}
blockSize := block.BlockSize()
// 对最后一个明文分组进行数据填充
@ -68,7 +68,7 @@ func EncryptAesCBC(src, key []byte) []byte {
// 5.加密连续的数据块
dst := make([]byte, len(src))
blackMode.CryptBlocks(dst, src)
return dst
return dst, nil
}
// DecryptAesCBC

43
ws.html

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>websocket</title>
</head>
<body>
<input type="text" id="uname">
<button id="btn">连接</button>
<script type="text/javascript">
document.getElementById("btn").onclick = function() {
const uname = document.getElementById("uname").value
console.log(uname)
connect(uname)
}
function connect(uname) {
const ws = new WebSocket("ws://localhost:9999/ws")
ws.onopen = (e) => {
console.log('open', e)
document.getElementById("uname").value = ""
document.getElementById("btn").innerText = '发送'
document.getElementById("btn").onclick = function () {
const msg = document.getElementById("uname").value
const token = "AgOjcdf3goeYDX3lwWWwkXtVpcrL-l2rX8csrRKgs3_-BC3JOx0l6nZU0MV25eIn"
ws.send(JSON.stringify({t: token}))
document.getElementById("uname").value = ''
}
}
ws.onmessage = (e) => {
console.log('msg:', e.data)
}
ws.onerror = (a,b,c) => {
console.log('error:', a, b, c)
}
}
</script>
</body>
</html>
Loading…
Cancel
Save