修复加密bug

master
joefalmko 1 month ago
parent 157a7f99d2
commit 76e8dab193

@ -0,0 +1,14 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Launch Package",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "~/WorkSpace/GinSkeleto",
"env": {},
"args": []
}
]
}

@ -0,0 +1,14 @@
## 获取公钥
/admin/users/publickey
参数字段|参数属性|类型|选项|
user_name|form-data|string|必填
> 返回示例:
```json
{
"code": 200,
"data": {
"PublicKey": "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFrZUx2RWNpS3o5UytUQ3E1VnE5MQpTU2RaNm55d3VzbHNPRzAzZnY1VXMxVzJTR0ZDVnpTY3N6aWlLYlIrQk9FR3JsSVRQN29Yb2w3enhQUm55eTczCkZjTkRDOHQwQlhxcGR0U3pkL3V1N1JndXpDYW5BYXVaRXh4RERLUmZEV0MrR0p4TUlBaUV0VHJwT1d6dWp1azgKbDdDMWprTlRhQUpBMmx6ODA2ZWNHZ1NIcFg4MHhCZUpwV3lERnF2N3J3eS9EWjhaekQvRTNXa2ZLREUvRzFFTApPNWRBWUg0QXoxcVQ3SHFEY0hpVVlrNGFDWUswb1pJSC9hSXlKRjhnMDVIbER6NUN2eXNXZVZCTWljT0VRaXQxCnJMaDRaWVhqSVAyVEZmYU5hTlpuVVdhR1BCc05VRThjRU92MlA1d3ZHazFGL29yQ0NyNlFQRytBVE00SU1EME4KR3dJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg=="
},
"msg": "Success"
}
```

@ -1,6 +1,7 @@
package web
import (
// "fmt"
"goskeleton/app/global/consts"
"goskeleton/app/global/variable"
"goskeleton/app/model"
@ -9,6 +10,7 @@ import (
"goskeleton/app/utils/response"
rsa "goskeleton/app/utils/rsa"
"time"
"net/http"
"github.com/gin-gonic/gin"
)
@ -199,64 +201,14 @@ func (u *Users) Logout(c *gin.Context) {
func (u *Users)PublicKey(c *gin.Context){
userName:=c.GetString(consts.ValidatorPrefix+"user_name")
key := model.CreateUserFactory("").PublicKey(userName)
// fmt.Println("public key: ",string(key))
if key!=nil{
response.Success(c,consts.CurdStatusOkMsg,gin.H{"PublicKey":key})
// response.Success(c,consts.CurdStatusOkMsg,gin.H{"PublicKey":key})
c.Data(http.StatusOK, "application/x-pem-file", key)
}else{
response.Fail(c,consts.CurdPublicKeyFailCode,consts.CurdPublicKeyFailMsg,"")
}
}
// // 解密密码
// func DecryptPassword(userName,pass string)[]byte{
// if pass==""{
// return nil
// }
// key:=model.CreateUserFactory("").PrivateKey(userName)
// if key!=nil{
// privateKey,err:=parsePrivateKeyFromPEM(key)
// if err!=nil{
// return nil
// }
// // Base64 decode the encrypted password
// encryptedPassword, err := base64.StdEncoding.DecodeString(pass)
// if err != nil {
// return nil
// }
// // Decrypt the password using the private key
// decryptedBytes, err := rsa.DecryptPKCS1v15(rand.Reader, privateKey, encryptedPassword)
// if err != nil {
// return nil
// }
// return decryptedBytes
// }
// return nil
// }
// // 解析PEM格式的私钥
// func parsePrivateKeyFromPEM(pemKey []byte)(*rsa.PrivateKey,error){
// block, _ := pem.Decode(pemKey)
// if block == nil {
// return nil, fmt.Errorf("failed to parse PEM block")
// }
// // 解析私钥
// privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
// if err != nil {
// // 如果不是PKCS#1格式尝试使用PKCS#8格式解析
// privateKeyInterface, err := x509.ParsePKCS8PrivateKey(block.Bytes)
// if err != nil {
// return nil, fmt.Errorf("failed to parse PKCS8 private key: %v", err)
// }
// privateKey, ok := privateKeyInterface.(*rsa.PrivateKey)
// if !ok {
// return nil, fmt.Errorf("parsed key is not an RSA private key")
// }
// return privateKey,nil
// }
// return privateKey, nil
// }
// 密码解密
func (u *Users)DecryptPassword(userName, pass string) []byte {
@ -274,7 +226,7 @@ func (u *Users)DecryptPassword(userName, pass string) []byte {
// 解析私钥
privateKey, err := rsa.ParsePrivateKeyFromPEM(key)
if err != nil {
variable.ZapLog.Error("私钥解析失败")
variable.ZapLog.Error("私钥解析失败,"+err.Error())
return nil
}

@ -2,7 +2,7 @@ package model
import (
"encoding/pem"
// "encoding/pem"
// "goskeleton/app/global/consts"
"goskeleton/app/global/variable"
"goskeleton/app/service/users/token_cache_redis"
@ -73,8 +73,8 @@ func (u *UsersModel) Login(userName string, pass string) *UsersModel {
//记录用户登陆login生成的token每次登陆记录一次token
func (u *UsersModel) OauthLoginToken(userId int64, token string, expiresAt int64) bool {
sql := `
INSERT INTO tb_oauth_access_tokens(fr_user_id,action_name,token,expires_at,client_ip)
SELECT ?,'login',? ,?,? FROM DUAL WHERE NOT EXISTS(SELECT 1 FROM tb_oauth_access_tokens a WHERE a.fr_user_id=? AND a.action_name='login' AND a.token=? )
INSERT INTO tb_oauth_access_tokens(fr_user_id,action_name,token,expires_at)
SELECT ?,'login',? ,? FROM DUAL WHERE NOT EXISTS(SELECT 1 FROM tb_oauth_access_tokens a WHERE a.fr_user_id=? AND a.action_name='login' AND a.token=? )
`
//注意token的精确度为秒如果在一秒之内一个账号多次调用接口生成的token其实是相同的这样写入数据库第二次的影响行数为0知己实际上操作仍然是有效的。
//所以这里只判断无错误即可,判断影响行数的话,>=0 都是ok的
@ -277,13 +277,20 @@ func (u *UsersModel) Destroy(id int,userName,pass string) bool {
// 账号密码验证成功
if len(u.Pass) > 0 && (u.Pass == md5_encrypt.Base64Md5(pass)) {
// 删除用户密钥
u.deleteKeyFromDB(userName)
err:=u.deleteKeyFromDB(userName)
if err!=nil{
variable.ZapLog.Error(err.Error())
}
// 删除用户
if u.Delete(u, id).Error == nil {
// 删除token
if u.OauthDestroyToken(id) {
return true
}else{
variable.ZapLog.Error("删除用户时token删除失败")
}
}else{
variable.ZapLog.Error("删除用户失败")
}
}
}
@ -302,7 +309,9 @@ func (u *UsersModel)Logout(id int,userName string)bool{
variable.ZapLog.Error("登出时删除token失败")
}
// 删除用户密钥
u.deleteKeyFromDB(userName)
if err:=u.deleteKeyFromDB(userName);err!=nil{
variable.ZapLog.Error("登出时删除密钥失败")
}
return true
}
// 返回用户密码加密所需的公钥
@ -321,10 +330,12 @@ func (u *UsersModel)PublicKey(userName string)[]byte{
// 不存在密钥,重新生成密钥
pubPEM, priPEM, err := rsa.GenerateRSAKeyPair()
if err != nil {
variable.ZapLog.Error("不存在密钥且密钥生成失败")
return nil
}
if err := u.storeKeyPair(userName, pubPEM, priPEM); err != nil {
return nil
variable.ZapLog.Error("密钥存储失败")
return nil
}
return pubPEM
}
@ -342,12 +353,14 @@ func (u *UsersModel)PrivateKey(userName string)[]byte{
// return nil
privateKey, err := u.getKeyFromDB(userName, "private_key")
if err == nil {
priPEM := pem.EncodeToMemory(&pem.Block{
Type: "PRIVATE KEY",
Bytes: []byte(privateKey),
})
return priPEM
// priPEM := pem.EncodeToMemory(&pem.Block{
// Type: "PRIVATE KEY",
// Bytes: []byte(privateKey),
// })
// return priPEM
return []byte(privateKey)
}
variable.ZapLog.Error("返回私钥失败")
return nil
}
// func (u *UsersModel)GenerateKeyPair(userName string)[]byte{

@ -58,15 +58,11 @@ func parsePKCS1PrivateKey(block *pem.Block) (*rsa.PrivateKey, error) {
}
func parsePKCS8PrivateKey(block *pem.Block) (*rsa.PrivateKey, error) {
privateKeyInterface, err := x509.ParsePKCS8PrivateKey(block.Bytes)
privateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return nil, fmt.Errorf("failed to parse PKCS8 private key: %v", err)
}
privateKey, ok := privateKeyInterface.(*rsa.PrivateKey)
if !ok {
return nil, fmt.Errorf("parsed key is not an RSA private key")
}
return privateKey, nil
return privateKey.(*rsa.PrivateKey), nil
}
func ParsePrivateKeyFromPEM(pemKey []byte) (*rsa.PrivateKey, error) {

@ -61,8 +61,8 @@ UNIQUE KEY `unique_index` (`ptype`,`v0`,`v1`,`v2`,`v3`,`v4`,`v5`)
CREATE TABLE `tb_rsa_keypair` (
`user_name` VARCHAR(30) DEFAULT '' COMMENT '账号',
`public_key` VARCHAR(400) DEFAULT '' COMMENT '公钥',
`private_key` VARCHAR(400) DEFAULT '' COMMENT '私钥',
`public_key` VARCHAR(512) DEFAULT '' COMMENT '公钥',
`private_key` VARCHAR(2048) DEFAULT '' COMMENT '私钥',
/* `pass` VARCHAR(128) DEFAULT '' COMMENT '密码', */
PRIMARY KEY (`user_name`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

@ -3,6 +3,7 @@ package routers
import (
"github.com/gin-contrib/pprof"
"github.com/gin-gonic/gin"
// "go.uber.org/zap"
"goskeleton/app/global/consts"
"goskeleton/app/global/variable"
@ -14,117 +15,6 @@ import (
// "net/http"
)
// 该路由主要设置 后台管理系统等后端应用路由
// func InitWebRouter() *gin.Engine {
// var router *gin.Engine
// // 非调试模式(生产模式) 日志写到日志文件
// if variable.ConfigYml.GetBool("AppDebug") == false {
// //1.gin自行记录接口访问日志不需要nginx如果开启以下3行那么请屏蔽第 34 行代码
// //gin.DisableConsoleColor()
// //f, _ := os.Create(variable.BasePath + variable.ConfigYml.GetString("Logs.GinLogName"))
// //gin.DefaultWriter = io.MultiWriter(f)
// //【生产模式】
// // 根据 gin 官方的说明:[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
// // 如果部署到生产环境,请使用以下模式:
// // 1.生产模式(release) 和开发模式的变化主要是禁用 gin 记录接口访问日志,
// // 2.go服务就必须使用nginx作为前置代理服务这样也方便实现负载均衡
// // 3.如果程序发生 panic 等异常使用自定义的 panic 恢复中间件拦截、记录到日志
// router = gin_release.ReleaseRouter()
// } else {
// // 调试模式,开启 pprof 包,便于开发阶段分析程序性能
// router = gin.Default()
// pprof.Register(router)
// }
// // 设置可信任的代理服务器列表,gin (2021-11-24发布的v1.7.7版本之后出的新功能)
// if variable.ConfigYml.GetInt("HttpServer.TrustProxies.IsOpen") == 1 {
// if err := router.SetTrustedProxies(variable.ConfigYml.GetStringSlice("HttpServer.TrustProxies.ProxyServerList")); err != nil {
// variable.ZapLog.Error(consts.GinSetTrustProxyError, zap.Error(err))
// }
// } else {
// _ = router.SetTrustedProxies(nil)
// }
// //根据配置进行设置跨域
// if variable.ConfigYml.GetBool("HttpServer.AllowCrossDomain") {
// router.Use(cors.Next())
// }
// router.GET("/", func(context *gin.Context) {
// context.String(http.StatusOK, "HelloWorld,这是后端模块")
// })
// //处理静态资源不建议gin框架处理静态资源参见 public/readme.md 说明
// router.Static("/public", "./public") // 定义静态资源路由与实际目录映射关系
// router.StaticFS("/dir", http.Dir("./public")) // 将public目录内的文件列举展示
// router.StaticFile("/abcd", "./public/readme.md") // 可以根据文件名绑定需要返回的文件名
// // 创建一个验证码路由
// verifyCode := router.Group("captcha")
// {
// // 验证码业务,该业务无需专门校验参数,所以可以直接调用控制器
// verifyCode.GET("/", (&captcha.Captcha{}).GenerateId) // 获取验证码ID
// verifyCode.GET("/:captcha_id", (&captcha.Captcha{}).GetImg) // 获取图像地址
// verifyCode.GET("/:captcha_id/:captcha_value", (&captcha.Captcha{}).CheckCode) // 校验验证码
// }
// // 创建一个后端接口路由组
// backend := router.Group("/admin/")
// {
// // 创建一个websocket,如果ws需要账号密码登录才能使用就写在需要鉴权的分组这里暂定是开放式的不需要严格鉴权我们简单验证一下token值
// backend.GET("ws", validatorFactory.Create(consts.ValidatorPrefix+"WebsocketConnect"))
// // 【不需要token】中间件验证的路由 用户注册、登录
// noAuth := backend.Group("users/")
// {
// // 关于路由的第二个参数用法说明
// // 1.编写一个表单参数验证器结构体,参见代码: app/http/validator/web/users/register.go
// // 2.将以上表单参数验证器注册,遵守 键 =》值 格式注册即可 app/http/validator/common/register_validator/web_register_validator.go 20行就是注册时候的键 consts.ValidatorPrefix+"UsersRegister"
// // 3.按照注册时的键,直接从容器调用即可 validatorFactory.Create(consts.ValidatorPrefix+"UsersRegister")
// noAuth.POST("register", validatorFactory.Create(consts.ValidatorPrefix+"UsersRegister"))
// // 不需要验证码即可登陆
// noAuth.POST("login", validatorFactory.Create(consts.ValidatorPrefix+"UsersLogin"))
// // 如果加载了验证码中间件那么就需要提交验证码才可以登陆本质上就是给登陆接口增加了2个参数验证码id提交时的键captcha_id 和 验证码值提交时的键 captcha_value具体参见配置文件
// //noAuth.Use(authorization.CheckCaptchaAuth()).POST("login", validatorFactory.Create(consts.ValidatorPrefix+"UsersLogin"))
// }
// // 刷新token
// refreshToken := backend.Group("users/")
// {
// // 刷新token当过期的token在允许失效的延长时间范围内用旧token换取新token
// refreshToken.Use(authorization.RefreshTokenConditionCheck()).POST("refreshtoken", validatorFactory.Create(consts.ValidatorPrefix+"RefreshToken"))
// }
// // 【需要token】中间件验证的路由
// backend.Use(authorization.CheckTokenAuth())
// {
// // 用户组路由
// users := backend.Group("users/")
// {
// // 查询 这里的验证器直接从容器获取是因为程序启动时将验证器注册在了容器具体代码位置App\Http\Validator\Web\Users\xxx
// users.GET("index", validatorFactory.Create(consts.ValidatorPrefix+"UsersShow"))
// // 新增
// users.POST("create", validatorFactory.Create(consts.ValidatorPrefix+"UsersStore"))
// // 更新
// users.POST("edit", validatorFactory.Create(consts.ValidatorPrefix+"UsersUpdate"))
// // 删除
// users.POST("delete", validatorFactory.Create(consts.ValidatorPrefix+"UsersDestroy"))
// }
// //文件上传公共路由
// uploadFiles := backend.Group("upload/")
// {
// uploadFiles.POST("files", validatorFactory.Create(consts.ValidatorPrefix+"UploadFiles"))
// }
// }
// }
// return router
// }
func InitWebRouter_Co() *gin.Engine {
var router *gin.Engine = gin.Default()
// 日志显示在控制台
@ -160,9 +50,9 @@ func InitWebRouter_Co() *gin.Engine {
// 如果加载了验证码中间件那么就需要提交验证码才可以登陆本质上就是给登陆接口增加了2个参数验证码id提交时的键captcha_id 和 验证码值提交时的键 captcha_value具体参见配置文件
//noAuth.Use(authorization.CheckCaptchaAuth()).POST("login", validatorFactory.Create(consts.ValidatorPrefix+"UsersLogin"))
// 获取公钥,用于密码等敏感信息的加密
noAuth.POST("publickey",validatorFactory.Create(consts.ValidatorPrefix+"PublicKey"))
noAuth.POST("publickey", validatorFactory.Create(consts.ValidatorPrefix+"PublicKey"))
}
// 刷新token
@ -185,7 +75,7 @@ func InitWebRouter_Co() *gin.Engine {
// users.POST("create", validatorFactory.Create(consts.ValidatorPrefix+"UsersStore")) // not need
// 更新用户名或密码
users.POST("username", validatorFactory.Create(consts.ValidatorPrefix+"UsersNameUpdate"))
users.POST("password",validatorFactory.Create(consts.ValidatorPrefix+"UsersPasswordUpdate"))
users.POST("password", validatorFactory.Create(consts.ValidatorPrefix+"UsersPasswordUpdate"))
// 注销用户
users.POST("delete", validatorFactory.Create(consts.ValidatorPrefix+"UsersDestroy"))
// 退出登录
@ -194,4 +84,4 @@ func InitWebRouter_Co() *gin.Engine {
}
}
return router
}
}

@ -0,0 +1,73 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Login</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jsencrypt/3.2.1/jsencrypt.min.js"></script>
</head>
<body>
<form id="loginForm">
<input type="text" id="username" placeholder="Username" required>
<input type="password" id="password" placeholder="Password" required>
<button type="submit">Login</button>
</form>
<script>
document.getElementById('loginForm').addEventListener('submit', async function (event) {
event.preventDefault();
const username = document.getElementById('username').value;
const password = document.getElementById('password').value;
// Fetch the public key from the server
const response = await fetch('http://localhost:14514/admin/users/publickey?user_name=joefalmko',{
method: 'POST',
});
const publicKey = await response.text();
console.log("public key:\n",publicKey);
// Encrypt the password using the public key
const encrypt = new JSEncrypt();
encrypt.setPublicKey(publicKey);
const encryptedPassword = encrypt.encrypt(password);
console.log("encrypted password: ",encryptedPassword);
// Send the login request
const loginResponse = await fetch('http://localhost:14514/admin/users/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ user_name:username, pass: encryptedPassword })
});
const result = await loginResponse.json();
console.log(result);
// if (result.success == "true") {
// localStorage.setItem('token', result.token);
// alert('Login successful!');
// alert("token: " + result.token);
// console.log(fetchWithAuth('auth/index'));
// } else {
// alert('Login failed!');
// }
});
// function fetchWithAuth(url, options = {}) {
// const token = localStorage.getItem('token'); // 从本地存储获取token
// if (token) {
// options.headers = {
// ...options.headers,
// 'Authorization': `Bearer ${token}`
// };
// }
// return fetch(url, options).then(response => {
// if (response.status === 401) {
// window.location.href = '/'; // 重定向到登录页面
// throw new Error('Unauthorized');
// }
// return response;
// });
// }
</script>
</body>
</html>
Loading…
Cancel
Save