更新权限和用户模型

master
veypi 3 years ago
parent 3d194e935d
commit cd7029c298

@ -1,3 +1,7 @@
# OneAuth # OneAuth
统一验证服务 统一验证服务
## 用户验证思路
![](https://public.veypi.com/img/screenshot/20211012194238.png)

@ -15,7 +15,7 @@ func Router(r OneBD.Router) {
r.SetInternalErrorFunc(func(m core.Meta) { r.SetInternalErrorFunc(func(m core.Meta) {
m.Write([]byte("{\"status\": 0}")) m.Write([]byte("{\"status\": 0}"))
}) })
user.Router(r.SubRouter("/auth/user")) user.Router(r.SubRouter("/user"))
wx.Router(r.SubRouter("wx")) wx.Router(r.SubRouter("wx"))
app.Router(r.SubRouter("app")) app.Router(r.SubRouter("app"))
//message.Router(r.SubRouter("/message")) //message.Router(r.SubRouter("/message"))

@ -31,7 +31,7 @@ func (h *appHandler) Get() (interface{}, error) {
} }
h.query = &models.App{} h.query = &models.App{}
h.query.UUID = id h.query.UUID = id
err := cfg.DB().Where(h.query).Preload("Wx").First(h.query).Error err := cfg.DB().Where(h.query).First(h.query).Error
if err != nil { if err != nil {
return nil, err return nil, err
} }

@ -20,7 +20,7 @@ import (
func Router(r OneBD.Router) { func Router(r OneBD.Router) {
pool := OneBD.NewHandlerPool(func() OneBD.Handler { pool := OneBD.NewHandlerPool(func() OneBD.Handler {
h := &handler{} h := &handler{}
h.Ignore(rfc.MethodHead) h.Ignore(rfc.MethodHead, rfc.MethodPost)
return h return h
}) })
r.Set("/", pool, rfc.MethodGet, rfc.MethodPost) // list r.Set("/", pool, rfc.MethodGet, rfc.MethodPost) // list
@ -68,8 +68,8 @@ func (h *handler) Get() (interface{}, error) {
// Post register user // Post register user
func (h *handler) Post() (interface{}, error) { func (h *handler) Post() (interface{}, error) {
if !h.CheckAuth("user").CanCreate() { if !cfg.CFG.EnableRegister {
return nil, oerr.NoAuth return nil, oerr.NoAuth.AttachStr("register disabled.")
} }
var userdata = struct { var userdata = struct {
Username string `json:"username"` Username string `json:"username"`
@ -138,7 +138,7 @@ func (h *handler) Patch() (interface{}, error) {
if err := cfg.DB().Where(&target).First(&target).Error; err != nil { if err := cfg.DB().Where(&target).First(&target).Error; err != nil {
return nil, err return nil, err
} }
if target.ID != h.Payload.ID && !h.CheckAuth("admin").CanDoAny() { if target.ID != h.Payload.ID {
return nil, oerr.NoAuth return nil, oerr.NoAuth
} }
if len(opts.Password) >= 6 { if len(opts.Password) >= 6 {
@ -187,6 +187,10 @@ func (h *handler) Head() (interface{}, error) {
if len(uid) == 0 || len(password) == 0 { if len(uid) == 0 || len(password) == 0 {
return nil, oerr.ApiArgsError return nil, oerr.ApiArgsError
} }
appID, err := strconv.Atoi(h.Meta().Query("app_id"))
if err != nil || appID <= 0 {
return nil, oerr.ApiArgsMissing
}
h.User = new(models.User) h.User = new(models.User)
uidType := h.Meta().Query("uid_type") uidType := h.Meta().Query("uid_type")
switch uidType { switch uidType {
@ -199,6 +203,12 @@ func (h *handler) Head() (interface{}, error) {
default: default:
h.User.Username = uid h.User.Username = uid
} }
app := &models.App{}
app.ID = uint(appID)
err = cfg.DB().Where(app).Find(app).Error
if err != nil {
return nil, oerr.DBErr.Attach(err)
}
if err := cfg.DB().Preload("Roles").Where(h.User).First(h.User).Error; err != nil { if err := cfg.DB().Preload("Roles").Where(h.User).First(h.User).Error; err != nil {
if err.Error() == gorm.ErrRecordNotFound.Error() { if err.Error() == gorm.ErrRecordNotFound.Error() {
// admin 登录自动注册 // admin 登录自动注册
@ -221,7 +231,7 @@ func (h *handler) Head() (interface{}, error) {
} }
} else { } else {
log.HandlerErrs(err) log.HandlerErrs(err)
return nil, err return nil, oerr.DBErr.Attach(err)
} }
} }
isAuth, err := h.User.CheckLogin(password) isAuth, err := h.User.CheckLogin(password)
@ -231,7 +241,7 @@ func (h *handler) Head() (interface{}, error) {
if h.User.Status == "disabled" { if h.User.Status == "disabled" {
return nil, oerr.DisableLogin return nil, oerr.DisableLogin
} }
token, err := h.User.GetToken(cfg.CFG.Key) token, err := h.User.GetToken(app.Key, app.ID)
if err != nil { if err != nil {
log.HandlerErrs(err) log.HandlerErrs(err)
return nil, oerr.Unknown.Attach(err) return nil, oerr.Unknown.Attach(err)

@ -19,7 +19,7 @@ type userRoleHandler struct {
} }
func (h *userRoleHandler) Post() (interface{}, error) { func (h *userRoleHandler) Post() (interface{}, error) {
if !h.CheckAuth("role").CanCreate() { if !h.GetAuth("role").CanCreate() {
return nil, oerr.NoAuth return nil, oerr.NoAuth
} }
uid := h.Meta().ParamsInt("user_id") uid := h.Meta().ParamsInt("user_id")
@ -67,7 +67,7 @@ func (h *userRoleHandler) Post() (interface{}, error) {
} }
func (h *userRoleHandler) Delete() (interface{}, error) { func (h *userRoleHandler) Delete() (interface{}, error) {
if !h.CheckAuth("role").CanDelete() { if !h.GetAuth("role").CanDelete() {
return nil, oerr.NoAuth return nil, oerr.NoAuth
} }
uid := h.Meta().ParamsInt("user_id") uid := h.Meta().ParamsInt("user_id")

@ -11,15 +11,16 @@ import (
var Path = cmd.GetCfgPath("OneAuth", "oa") var Path = cmd.GetCfgPath("OneAuth", "oa")
var CFG = &struct { var CFG = &struct {
AdminUser string AdminUser string
Host string Host string
LoggerPath string LoggerPath string
LoggerLevel string LoggerLevel string
Key string Key string
TimeFormat string TimeFormat string
Debug bool Debug bool
EXEDir string EXEDir string
DB struct { EnableRegister bool
DB struct {
Type string Type string
Addr string Addr string
User string User string
@ -27,13 +28,14 @@ var CFG = &struct {
DB string DB string
} }
}{ }{
AdminUser: "admin", AdminUser: "admin",
Host: "0.0.0.0:19528", Host: "0.0.0.0:4001",
LoggerPath: "", LoggerPath: "",
LoggerLevel: "debug", LoggerLevel: "debug",
TimeFormat: "2006/01/02 15:04:05", TimeFormat: "2006/01/02 15:04:05",
Debug: true, Debug: true,
EXEDir: "./", EXEDir: "./",
EnableRegister: true,
DB: struct { DB: struct {
Type string Type string
Addr string Addr string
@ -41,9 +43,9 @@ var CFG = &struct {
Pass string Pass string
DB string DB string
}{ }{
Type: "sqlite", //Type: "sqlite",
//Addr: "127.0.0.1:3306", Addr: "127.0.0.1:3306",
Addr: "oa.db", //Addr: "oa.db",
User: "root", User: "root",
Pass: "123456", Pass: "123456",
DB: "one_auth", DB: "one_auth",

@ -8,12 +8,12 @@ import (
"github.com/veypi/OneBD/rfc" "github.com/veypi/OneBD/rfc"
) )
type Auth struct { type UserHandler struct {
Payload *models.PayLoad Payload *models.PayLoad
ignoreMethod map[rfc.Method]bool ignoreMethod map[rfc.Method]bool
} }
func (a *Auth) Init(m OneBD.Meta) error { func (a *UserHandler) Init(m OneBD.Meta) error {
if a.ignoreMethod != nil && a.ignoreMethod[m.Method()] { if a.ignoreMethod != nil && a.ignoreMethod[m.Method()] {
return nil return nil
} }
@ -29,7 +29,7 @@ func (a *Auth) Init(m OneBD.Meta) error {
return oerr.NotLogin.Attach(err) return oerr.NotLogin.Attach(err)
} }
func (a *Auth) Ignore(methods ...rfc.Method) { func (a *UserHandler) Ignore(methods ...rfc.Method) {
if a.ignoreMethod == nil { if a.ignoreMethod == nil {
a.ignoreMethod = make(map[rfc.Method]bool) a.ignoreMethod = make(map[rfc.Method]bool)
} }
@ -38,6 +38,6 @@ func (a *Auth) Ignore(methods ...rfc.Method) {
} }
} }
func (a *Auth) CheckAuth(name string, tags ...string) models.AuthLevel { func (a *UserHandler) GetAuth(ResourceID string, ResourceUUID ...string) models.AuthLevel {
return a.Payload.CheckAuth(name, tags...) return a.Payload.GetAuth(ResourceID, ResourceUUID...)
} }

@ -0,0 +1,27 @@
package auth
import (
"OneAuth/models"
"github.com/veypi/utils"
"sync"
)
var keyCache = sync.Map{}
func GetUserKey(uid uint, app *models.App) string {
if app.ID == 1 {
key, _ := keyCache.LoadOrStore(uid, utils.RandSeq(16))
return key.(string)
}
// TODO: 获取其他应用user_key
return ""
}
func RefreshUserKey(uid uint, app *models.App) string {
if app.ID == 1 {
key := utils.RandSeq(16)
keyCache.Store(uid, key)
return key
}
return ""
}

@ -17,11 +17,11 @@ var json = jsoniter.ConfigFastest
type ApiHandler struct { type ApiHandler struct {
OneBD.BaseHandler OneBD.BaseHandler
auth.Auth auth.UserHandler
} }
func (h *ApiHandler) Init(m OneBD.Meta) error { func (h *ApiHandler) Init(m OneBD.Meta) error {
return tools.MultiIniter(m, &h.BaseHandler, &h.Auth) return tools.MultiIniter(m, &h.BaseHandler, &h.UserHandler)
} }
func (h *ApiHandler) OnResponse(data interface{}) { func (h *ApiHandler) OnResponse(data interface{}) {

@ -1,16 +1,34 @@
package models package models
var AppKeys = map[string]string{}
type App struct { type App struct {
BaseModel BaseModel
Name string `json:"name"` Name string `json:"name"`
UUID string `json:"uuid"` Icon string `json:"icon"`
Host string `json:"host"` UUID string `json:"uuid"`
WxID string `json:"wx_id" gorm:""` // 认证成功跳转链接
Wx *Wechat `json:"wx" gorm:"association_foreignkey:ID"` Host string `json:"host"`
// 加解密用户token (key+key2)
// 两个key都是请求获取时刷新
// key oa发放给app 双方保存 针对app生成 每个应用有一个
// key2 app发放给oa app保存 oa使用一次销毁 针对当个用户生成 每个用户有一个
// 获取app用户加密秘钥key2
UserRefreshUrl string `json:"user_refresh_url"`
// app 校验用户token时使用
Key string `json:"key"`
// 是否允许用户注册
EnableRegister string `json:"enable_register"`
EnableUser bool `json:"enable_user"`
EnableWx bool `json:"enable_wx"`
EnablePhone bool `json:"enable_phone"`
EnableEmail bool `json:"enable_email"`
Wx *Wechat `json:"wx" gorm:"foreignkey:AppID;references:ID"`
} }
type Wechat struct { type Wechat struct {
BaseModel BaseModel
AppID uint `json:"app_id"`
// 网页授权登录用 // 网页授权登录用
WxID string `json:"wx_id"` WxID string `json:"wx_id"`
AgentID string `json:"agent_id"` AgentID string `json:"agent_id"`

@ -1,74 +1,44 @@
package models package models
import (
"OneAuth/cfg"
"github.com/veypi/utils/log"
)
var GlobalRoles = make(map[uint]*Role)
func SyncGlobalRoles() {
roles := make([]*Role, 0, 10)
err := cfg.DB().Preload("Auths").Find(&roles).Error
if err != nil {
log.Warn().Msgf("sync global roles error: %s", err.Error())
return
}
for _, r := range roles {
GlobalRoles[r.ID] = r
}
}
type UserRole struct { type UserRole struct {
BaseModel BaseModel
UserID uint `json:"user_id"` UserID uint `json:"user_id"`
RoleID uint `json:"role_id"` RoleID uint `json:"role_id"`
} }
type RoleAuth struct {
BaseModel
RoleID uint `json:"role_id"`
AuthID uint `json:"auth_id"`
}
type Role struct { type Role struct {
BaseModel BaseModel
Name string `json:"name"` Name string `json:"name"`
// 角色类型 // 角色类型
// 0: 系统角色 1: 用户角色 // 1: 系统定义角色 2: 用户自定义角色
Category uint `json:"category" gorm:"default:0"` Category uint `json:"category" gorm:"default:1"`
// 角色标签 // 角色标签
Tag string `json:"tag" gorm:"default:''"` Tag string `json:"tag" gorm:"default:''"`
Users []*User `json:"users" gorm:"many2many:user_role;"` Users []*User `json:"users" gorm:"many2many:user_role;"`
// 具体权限 // 具体权限
Auths []*Auth `json:"auths" gorm:"many2many:role_auth;"` Auths []*Auth `json:"auths" gorm:"foreignkey:RoleID;references:ID"`
IsUnique bool `json:"is_unique" gorm:"default:false"` IsUnique bool `json:"is_unique" gorm:"default:false"`
} }
func (r Role) CheckAuth(name string, tags ...string) AuthLevel { // AuthLevel 权限等级
res := AuthNone // 0 相当于没有
tag := "" // 1 有限读权限
if len(tags) > 0 { // 2 读权限
tag = tags[0] // 3 创建权限
} // 4 修改权限
for _, a := range r.Auths { // 5 删除权限
if a.Name == "admin" && a.Tag == "" || (a.Name == "admin" && a.Tag == tag) || (a.Name == name && a.Tag == tag) { // 6 赋予其余人权限
if a.Level > res {
res = a.Level
}
}
}
return res
}
type AuthLevel uint type AuthLevel uint
const ( const (
AuthNone AuthLevel = 0 AuthNone AuthLevel = 0
AuthRead AuthLevel = 1 // AuthPart TODO: 临时权限
AuthCreate AuthLevel = 2 AuthPart AuthLevel = 1
AuthUpdate AuthLevel = 3 AuthRead AuthLevel = 2
AuthDelete AuthLevel = 4 AuthCreate AuthLevel = 3
AuthUpdate AuthLevel = 4
AuthDelete AuthLevel = 5
AuthAll AuthLevel = 6
) )
func (a AuthLevel) CanRead() bool { func (a AuthLevel) CanRead() bool {
@ -88,17 +58,25 @@ func (a AuthLevel) CanDelete() bool {
} }
func (a AuthLevel) CanDoAny() bool { func (a AuthLevel) CanDoAny() bool {
return a >= AuthDelete return a >= AuthAll
} }
// 资源权限 // 资源权限
type Auth struct { type Auth struct {
BaseModel BaseModel
Name string `json:"name"` Name string `json:"name"`
AppID uint `json:"app_id"` // 该权限作用的应用
AppID uint `json:"app_id"`
// 权限绑定只能绑定一个
RoleID uint `json:"role_id"`
UserID uint `json:"user_id"`
// 资源id
RID string `json:"rid" gorm:""`
// 具体某个资源的id
RUID string `json:"ruid"`
// 权限标签 // 权限标签
Tag string `json:"tag"` Tag string `json:"tag"`
// 权限等级 0 相当于没有 1 读权限 2 创建权限 3 修改权限 4 删除权限
Level AuthLevel `json:"level"` Level AuthLevel `json:"level"`
Des string `json:"des"`
} }

@ -7,7 +7,6 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"github.com/veypi/utils" "github.com/veypi/utils"
"github.com/veypi/utils/log"
"strings" "strings"
"time" "time"
) )
@ -27,33 +26,47 @@ type User struct {
Icon string `json:"icon"` Icon string `json:"icon"`
Roles []*Role `json:"roles" gorm:"many2many:user_role;"` Roles []*Role `json:"roles" gorm:"many2many:user_role;"`
Auths []*Auth `json:"auths" gorm:"foreignkey:UserID;references:ID"`
}
type simpleAuth struct {
RID string `json:"rid"`
// 具体某个资源的id
RUID string `json:"ruid"`
Level AuthLevel `json:"level"`
} }
// TODO:: roles 是否会造成token过大 ? // TODO:: roles 是否会造成token过大 ?
type PayLoad struct { type PayLoad struct {
ID uint `json:"id"` ID uint `json:"id"`
Username string `json:"username"` Iat int64 `json:"iat"` //token time
Nickname string `json:"nickname"` Exp int64 `json:"exp"`
Icon string `json:"icon"` Auth map[uint]*simpleAuth `json:"auth"`
Iat int64 `json:"iat"` //token time
Exp int64 `json:"exp"`
Roles []uint `json:"roles"`
} }
func (p *PayLoad) CheckAuth(name string, tags ...string) AuthLevel { // GetAuth resource_uuid 缺省或仅第一个有效 权限会被更高权限覆盖
func (p *PayLoad) GetAuth(ResourceID string, ResourceUUID ...string) AuthLevel {
res := AuthNone res := AuthNone
if p == nil || p.Roles == nil { if p == nil || p.Auth == nil {
return res return res
} }
for _, id := range p.Roles { ruid := ""
r := GlobalRoles[id] if len(ResourceUUID) > 0 {
if r == nil { ruid = ResourceUUID[0]
log.Warn().Msgf("not found role id: %d", id) }
continue for _, a := range p.Auth {
} if a.RID == ResourceID {
t := r.CheckAuth(name, tags...) if a.RUID != "" {
if t > res { if a.RUID == ruid {
res = t if a.Level > res {
res = a.Level
}
} else {
continue
}
} else if a.Level > res {
res = a.Level
}
} }
} }
return res return res
@ -63,26 +76,7 @@ func (u *User) String() string {
return u.Username + ":" + u.Nickname return u.Username + ":" + u.Nickname
} }
func (u *User) CheckAuth(name string, tags ...string) AuthLevel { func (u *User) GetToken(key string, appID uint) (string, error) {
res := AuthNone
if u == nil || u.Roles == nil {
return res
}
for _, t := range u.Roles {
r := GlobalRoles[t.ID]
if r == nil {
log.Warn().Msgf("not found role id: %d", t.ID)
continue
}
t := r.CheckAuth(name, tags...)
if t > res {
res = t
}
}
return res
}
func (u *User) GetToken(key string) (string, error) {
header := map[string]string{ header := map[string]string{
"typ": "JWT", "typ": "JWT",
"alg": "HS256", "alg": "HS256",
@ -90,15 +84,30 @@ func (u *User) GetToken(key string) (string, error) {
//header := "{\"typ\": \"JWT\", \"alg\": \"HS256\"}" //header := "{\"typ\": \"JWT\", \"alg\": \"HS256\"}"
now := time.Now().Unix() now := time.Now().Unix()
payload := PayLoad{ payload := PayLoad{
ID: u.ID, ID: u.ID,
Username: u.Username, Iat: now,
Nickname: u.Nickname, Exp: now + 60*60*24,
Icon: u.Icon, Auth: map[uint]*simpleAuth{},
Iat: now,
Exp: now + 60*60*24,
} }
for _, r := range u.Roles { for _, r := range u.Roles {
payload.Roles = append(payload.Roles, r.ID) for _, a := range r.Auths {
if appID == a.AppID {
payload.Auth[a.ID] = &simpleAuth{
RID: a.RID,
RUID: a.RUID,
Level: a.Level,
}
}
}
}
for _, a := range u.Auths {
if appID == a.AppID {
payload.Auth[a.ID] = &simpleAuth{
RID: a.RID,
RUID: a.RUID,
Level: a.Level,
}
}
} }
a, err := json.Marshal(header) a, err := json.Marshal(header)
if err != nil { if err != nil {

@ -4,7 +4,7 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0"> <meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico"> <!-- <link rel="icon" href="<%= BASE_URL %>favicon.ico">-->
<title><%= htmlWebpackPlugin.options.title %></title> <title><%= htmlWebpackPlugin.options.title %></title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900"> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css">

@ -21,6 +21,7 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue' import Vue from 'vue'
import util from '@/libs/util'
export default Vue.extend({ export default Vue.extend({
name: 'App', name: 'App',
@ -32,7 +33,7 @@ export default Vue.extend({
}), }),
mounted() { mounted() {
document.title = '统一认证' util.title('统一认证')
} }
}) })
</script> </script>

@ -4,10 +4,14 @@
* Distributed under terms of the MIT license. * Distributed under terms of the MIT license.
*/ */
import Vue from 'vue'
import {Base64} from 'js-base64' import {Base64} from 'js-base64'
import ajax from './ajax' import ajax from './ajax'
import store from '@/store' import store from '@/store'
export type SuccessFunction<T> = (e: any) => void;
export type FailedFunction<T> = (e: any) => void;
const Code = { const Code = {
42011: '无操作权限', 42011: '无操作权限',
22031: '资源不存在 或 您无权操作该资源' 22031: '资源不存在 或 您无权操作该资源'
@ -24,15 +28,16 @@ class Interface {
this.data = data this.data = data
} }
Start(success: Function, fail?: Function) { Start(success: SuccessFunction<any>, fail?: FailedFunction<any>) {
const newFail = function (data: any) { const newFail = function (data: any) {
if (data && data.code === 40001) { if (data && data.code === 40001) {
// no login // no login
store.dispatch('handleLogOut') store.dispatch('handleLogOut')
return return
} }
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
if (data && data.code > 0 && Code[data.code]) { if (data && data.code > 0 && Code[data.code]) {
// Message.warning({message: Code[data.code] || data.err, offset: 100})
} }
if (fail) { if (fail) {
fail(data) fail(data)
@ -103,12 +108,32 @@ const app = {
local: '/api/app/', local: '/api/app/',
get(id: string) { get(id: string) {
return new Interface(ajax.get, this.local + id) return new Interface(ajax.get, this.local + id)
},
list() {
return new Interface(ajax.get, this.local)
}
}
const user = {
local: '/api/user/',
register(username: string, password: string, prop?: any) {
const data = Object.assign({
username: username,
password: Base64.encode(password)
}, prop)
return new Interface(ajax.post, this.local, data)
},
login(username: string, password: string) {
return new Interface(ajax.head, this.local + username, {
password: Base64.encode(password)
})
} }
} }
const api = { const api = {
role: role, role: role,
app: app, app: app,
user: user,
admin: { admin: {
auths() { auths() {
return new Interface(ajax.get, '/api/auth/') return new Interface(ajax.get, '/api/auth/')
@ -192,8 +217,8 @@ const api = {
} }
const Api = { const Api = {
install(Vue: any) { install(vue: typeof Vue): void {
Vue.prototype.api = api vue.prototype.$api = api
} }
} }
export {Api} export {Api}

@ -0,0 +1,64 @@
function padLeftZero(str: string): string {
return ('00' + str).substr(str.length)
}
const util = {
title: function (title: string) {
window.document.title = title ? title + ' - Home' : 'veypi project'
},
getCookie(name: string) {
const reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)')
const arr = document.cookie.match(reg)
if (arr) {
return unescape(arr[2])
} else return null
},
delCookie(name: string) {
const exp = new Date()
exp.setTime(exp.getTime() - 1)
const cval = this.getCookie(name)
if (cval !== null) {
document.cookie = name + '=' + cval + ';expires=' + exp.toLocaleString()
}
},
setCookie(name: string, value: string, time: number) {
const exp = new Date()
exp.setTime(exp.getTime() + time)
document.cookie =
name + '=' + escape(value) + ';expires=' + exp.toLocaleString()
},
checkLogin() {
// return parseInt(this.getCookie('stat')) === 1
return Boolean(localStorage.auth_token)
},
formatDate(date: Date, fmt: string) {
if (/(y+)/.test(fmt)) {
fmt = fmt.replace(
RegExp.$1,
(date.getFullYear() + '').substr(4 - RegExp.$1.length)
)
}
const o = {
'M+': date.getMonth() + 1,
'd+': date.getDate(),
'h+': date.getHours(),
'm+': date.getMinutes(),
's+': date.getSeconds()
}
for (const k in o) {
if (new RegExp(`(${k})`).test(fmt)) {
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
const str = o[k] + ''
fmt = fmt.replace(
RegExp.$1,
RegExp.$1.length === 1 ? str : padLeftZero(str)
)
}
}
return fmt
}
}
export default util

@ -1,6 +1,9 @@
import Vue from 'vue' import Vue from 'vue'
import VueRouter, {RouteConfig} from 'vue-router' import VueRouter, {RouteConfig} from 'vue-router'
import Home from '../views/Home.vue' import Home from '../views/Home.vue'
import Demo from '@/views/demo.vue'
import Login from '@/views/login.vue'
import Register from '@/views/register.vue'
Vue.use(VueRouter) Vue.use(VueRouter)
@ -11,12 +14,19 @@ const routes: Array<RouteConfig> = [
component: Home component: Home
}, },
{ {
path: '/about', path: '/app',
name: 'About', name: 'app',
// route level code-splitting component: Demo
// this generates a separate chunk (about.[hash].js) for this route },
// which is lazy-loaded when the route is visited. {
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue') path: '/login',
name: 'login',
component: Login
},
{
path: '/register',
name: 'register',
component: Register
}, },
{ {
path: '/wx', path: '/wx',

@ -1,3 +1,4 @@
declare module '*.js'
declare module '*.vue' { declare module '*.vue' {
import Vue from 'vue' import Vue from 'vue'
export default Vue export default Vue

@ -5,6 +5,7 @@ Vue.use(Vuex)
export default new Vuex.Store({ export default new Vuex.Store({
state: { state: {
user: null
}, },
mutations: { mutations: {
}, },

@ -0,0 +1,14 @@
// 1. 确保在声明补充的类型之前导入 'vue'
import Vue from 'vue'
import api from '@/api'
export type PluginFunction<T> = (Vue: typeof Vue, options?: T) => void;
// 2. 定制一个文件,设置你想要补充的类型
// 在 types/vue.d.ts 里 Vue 有构造函数类型
declare module 'vue/types/vue' {
// 3. 声明为 Vue 补充的东西
interface Vue {
$api: typeof api;
}
}

@ -1,5 +0,0 @@
<template>
<div class="about">
<h1>This is an about page</h1>
</div>
</template>

@ -0,0 +1,21 @@
<style>
</style>
<template>
<div class='home d-flex justify-center align-center'>
</div>
</template>
<script lang='ts'>
import {Component, Vue} from 'vue-property-decorator'
@Component({
components: {}
})
export default class Demo extends Vue {
mounted() {
}
created() {
}
}
</script>

@ -0,0 +1,116 @@
<style>
</style>
<template>
<v-row align="center" class="fill-height" justify="center" style="background: #ebebeb">
<v-col cols="12" sm="8" md="6" lg="4" xl="3">
<v-card class="elevation-12 mx-5" style="opacity: 0.8">
<v-row justify="center">
<v-col cols="10">
<v-card class="elevation-1 mt-n12 primary theme--dark">
<v-card-text class="text-center">
<h1 class="display-2 font-weight-bold mb-2">Login</h1>
<v-tooltip left>
<template v-slot:activator="{ on }">
<v-btn icon large v-on="on">
<v-icon>mdi-cellphone</v-icon>
</v-btn>
</template>
<span style="font-family:'Noto Sans Armenian'">手机登录</span>
</v-tooltip>
<v-tooltip right>
<template v-slot:activator="{ on }">
<v-btn icon large v-on="on">
<v-icon>mdi-barcode</v-icon>
</v-btn>
</template>
<span>授权码登录</span>
</v-tooltip>
</v-card-text>
</v-card>
</v-col>
</v-row>
<v-card-text>
<v-form ref="form">
<v-text-field
v-model="formInline.user"
:counter="16"
:rules="ruleInline.user"
label="账号"
required
prepend-inner-icon="mdi-account-circle"
></v-text-field>
<v-text-field
v-model="formInline.password"
type="password"
:counter="16"
:rules="ruleInline.password"
label="密码"
prepend-inner-icon="mdi-lock"
@keyup.enter="handleSubmit"
required
></v-text-field>
</v-form>
</v-card-text>
<v-card-actions>
<v-spacer/>
<v-btn type="primary" @click="handleSubmit"></v-btn>
<router-link to="/register" style="text-decoration: none;">
<v-btn type="primary" style="margin-left:8px">注册</v-btn>
</router-link>
</v-card-actions>
</v-card>
</v-col>
</v-row>
</template>
<script lang='ts'>
import {Component, Vue} from 'vue-property-decorator'
import util from '@/libs/util'
@Component({
components: {}
})
export default class Login extends Vue {
formInline = {
user: '',
password: ''
}
ruleInline = {
user: [
(v: string) => !!v || 'required',
(v: string) => (v && v.length >= 3 && v.length <= 16) || '长度要求3~16'
],
password: [
(v: string) => !!v || 'required',
(v: string) => (v && v.length >= 6 && v.length <= 16) || '长度要求6~16'
]
}
handleSubmit() {
this.$api.auth.login(this.formInline.user, this.formInline.password).Start(
data => {
if (util.checkLogin()) {
// this.$message.success('')
// EventBus.$emit('login', true)
this.$nextTick(() => {
this.$router.push({name: 'home'})
})
} else {
// this.$message.error('')
}
},
() => {
// this.$message.error('')
}
)
}
mounted() {
}
created() {
console.log(this.formInline)
}
}
</script>

@ -0,0 +1,118 @@
<style>
</style>
<template>
<v-row class="fill-height" align="center" justify="center" style="background: #ebebeb">
<v-col cols="12" sm="8" md="6" lg="4" xl="3">
<v-card class="elevation-12 mx-5" style="opacity: 0.8">
<v-row justify="center">
<v-card class="elevation-1 mt-n7 primary" style="width: 80%">
<v-card-actions>
<v-row>
<v-icon
style="position: absolute;left: 10px;top:19px;z-index: 1"
@click="$router.back()"
size="36"
>mdi-arrow-left-circle
</v-icon>
<v-col cols="12" class="text-center">
<h1 class="display-2 ">注册</h1>
</v-col>
</v-row>
</v-card-actions>
</v-card>
</v-row>
<v-card-text class="text-center">
<v-form ref="form">
<v-text-field
type="text"
prepend-inner-icon="mdi-account-circle"
v-model="form.username"
label="账号"
:rules="ruleInline.user"
:counter="16"
>
</v-text-field>
<v-text-field
type="password"
v-model="form.passwd"
label="密码"
prepend-inner-icon="mdi-lock"
:rules="ruleInline.password"
:counter="16"
></v-text-field>
<v-text-field
type="password"
v-model="form.passwdCheck"
label="密码"
prepend-inner-icon="mdi-lock"
:rules="ruleInline.passwordCheck"
:counter="16"
@keyup.enter="handleSubmit"
></v-text-field>
</v-form>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn type="primary" @click="handleSubmit"></v-btn>
<v-btn @click="handleReset()"></v-btn>
</v-card-actions>
</v-card>
</v-col>
</v-row>
</template>
<script lang='ts'>
import {Component, Vue} from 'vue-property-decorator'
@Component({
components: {}
})
export default class Register extends Vue {
form = {
passwd: '',
passwdCheck: '',
email: '',
username: ''
}
ruleInline = {
user: [
(v: string) => !!v || 'required',
(v: string) => (v && v.length >= 3 && v.length <= 16) || '长度要求3~16'
],
password: [
(v: string) => !!v || 'required',
(v: string) => (v && v.length >= 6 && v.length <= 16) || '长度要求6~16'
],
passwordCheck: [
(v: string) => !!v || 'required',
(v: string) => (v && v === this.form.passwd) || '密码不一致'
]
}
handleSubmit() {
if (!this.$refs.form.validate()) {
return
}
this.$api.user.register(this.form.username, this.form.passwd).Start(
(data) => {
// this.$message.success('!')
this.$router.push({name: 'login'})
},
(data) => {
if (data && data.code === '31011') {
// this.$message.error('')
} else {
// this.$message.error('')
}
}
)
}
handleReset() {
this.form.username = ''
this.form.passwd = ''
this.form.passwdCheck = ''
}
}
</script>

@ -52,7 +52,7 @@ export default class Wx extends Vue {
created() { created() {
if (this.uuid) { if (this.uuid) {
this.api.app.get(this.uuid).Start(e => { this.$api.app.get(this.uuid as string).Start(e => {
this.url = e.wx.url + '/api/wx/login/' + this.uuid this.url = e.wx.url + '/api/wx/login/' + this.uuid
this.aid = e.wx.corp_id this.aid = e.wx.corp_id
this.agentID = e.wx.agent_id this.agentID = e.wx.agent_id

@ -10,7 +10,7 @@ module.exports = {
outputDir: '../sub/static', outputDir: '../sub/static',
devServer: { devServer: {
host: '0.0.0.0', host: '0.0.0.0',
port: 19528, port: 19520,
disableHostCheck: true, disableHostCheck: true,
proxy: { proxy: {
'^/api': { '^/api': {

@ -76,7 +76,6 @@ func runSyncDB(*cli.Context) error {
log.HandlerErrs( log.HandlerErrs(
db.SetupJoinTable(&models.User{}, "Roles", &models.UserRole{}), db.SetupJoinTable(&models.User{}, "Roles", &models.UserRole{}),
db.SetupJoinTable(&models.Role{}, "Users", &models.UserRole{}), db.SetupJoinTable(&models.Role{}, "Users", &models.UserRole{}),
db.SetupJoinTable(&models.Role{}, "Auths", &models.RoleAuth{}),
db.AutoMigrate(&models.User{}, &models.Role{}, &models.Auth{}), db.AutoMigrate(&models.User{}, &models.Role{}, &models.Auth{}),
) )
log.HandlerErrs( log.HandlerErrs(

Loading…
Cancel
Save