mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-02 15:30:25 +08:00
refactor: oauth2登录调整
This commit is contained in:
@@ -1,24 +1,25 @@
|
||||
window.globalConfig = {
|
||||
// 默认为空,以访问根目录为api请求地址。若前后端分离部署可单独配置该后端api请求地址
|
||||
"BaseApiUrl": "",
|
||||
"BaseWsUrl": ""
|
||||
}
|
||||
BaseApiUrl: '',
|
||||
BaseWsUrl: '',
|
||||
};
|
||||
|
||||
// index.html添加百秒级时间戳,防止被浏览器缓存
|
||||
!function () {
|
||||
let t = "t=" + new Date().getTime().toString().substring(0, 8)
|
||||
let search = location.search;
|
||||
let m = search && search.match(/t=\d*/g)
|
||||
// !(function () {
|
||||
// let t = 't=' + new Date().getTime().toString().substring(0, 8);
|
||||
// let search = location.search;
|
||||
// let m = search && search.match(/t=\d*/g);
|
||||
|
||||
if (m[0]) {
|
||||
if (m[0] !== t) {
|
||||
location.search = search.replace(m[0], t)
|
||||
}
|
||||
} else {
|
||||
if (search.indexOf('?') > -1) {
|
||||
location.search = search + '&' + t
|
||||
} else {
|
||||
location.search = t
|
||||
}
|
||||
}
|
||||
}()
|
||||
// console.log(location);
|
||||
// if (m[0]) {
|
||||
// if (m[0] !== t) {
|
||||
// location.search = search.replace(m[0], t);
|
||||
// }
|
||||
// } else {
|
||||
// if (search.indexOf('?') > -1) {
|
||||
// location.search = search + '&' + t;
|
||||
// } else {
|
||||
// location.search = t;
|
||||
// }
|
||||
// }
|
||||
// })();
|
||||
|
||||
@@ -10,4 +10,5 @@ export default {
|
||||
captcha: () => request.get('/sys/captcha'),
|
||||
logout: () => request.post('/auth/accounts/logout'),
|
||||
getPermissions: () => request.get('/sys/accounts/permissions'),
|
||||
oauth2Callback: (params: any) => request.get('/auth/oauth2/callback', params),
|
||||
};
|
||||
|
||||
@@ -257,7 +257,7 @@ router.beforeEach(async (to, from, next) => {
|
||||
}
|
||||
|
||||
const token = getSession('token');
|
||||
if (to.path === '/login' && !token) {
|
||||
if ((to.path === '/login' || to.path == '/oauth2/callback') && !token) {
|
||||
next();
|
||||
NProgress.done();
|
||||
return;
|
||||
|
||||
@@ -143,6 +143,14 @@ export const staticRoutes: Array<RouteRecordRaw> = [
|
||||
title: '没有权限',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/oauth2/callback',
|
||||
name: 'oauth2Callback',
|
||||
component: () => import('@/views/oauth/Oauth2Callback.vue'),
|
||||
meta: {
|
||||
title: 'oauth2回调',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/machine/terminal',
|
||||
name: 'machineTerminal',
|
||||
|
||||
@@ -104,6 +104,24 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="修改基本信息" v-model="baseInfoDialog.visible" :close-on-click-modal="false" width="450px" :destroy-on-close="true">
|
||||
<el-form :model="baseInfoDialog.form" :rules="baseInfoDialog.rules" ref="changePwdFormRef" label-width="auto">
|
||||
<el-form-item prop="username" label="用户名" required>
|
||||
<el-input v-model.trim="baseInfoDialog.form.username"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="name" label="姓名" required>
|
||||
<el-input v-model.trim="baseInfoDialog.form.name"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<!-- <el-button @click="cancelChangePwd">取 消</el-button> -->
|
||||
<el-button @click="updateUserInfo()" type="primary" :loading="loading.updateUserConfirm">确 定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -112,7 +130,7 @@ import { nextTick, onMounted, ref, toRefs, reactive, computed } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { initRouter } from '@/router/index';
|
||||
import { setSession, setUserInfo2Session, setUseWatermark2Session } from '@/common/utils/storage';
|
||||
import { getSession, setSession, setUserInfo2Session, setUseWatermark2Session } from '@/common/utils/storage';
|
||||
import { formatAxis } from '@/common/utils/format';
|
||||
import openApi from '@/common/openApi';
|
||||
import { RsaEncrypt } from '@/common/rsa';
|
||||
@@ -120,6 +138,7 @@ import { getAccountLoginSecurity, useWartermark } from '@/common/sysconfig';
|
||||
import { letterAvatar } from '@/common/utils/string';
|
||||
import { useUserInfo } from '@/store/userInfo';
|
||||
import QrcodeVue from 'qrcode.vue';
|
||||
import { personApi } from '@/views/personal/api';
|
||||
|
||||
const rules = {
|
||||
username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
|
||||
@@ -149,6 +168,7 @@ const state = reactive({
|
||||
captcha: '',
|
||||
cid: '',
|
||||
},
|
||||
loginRes: {} as any,
|
||||
changePwdDialog: {
|
||||
visible: false,
|
||||
form: {
|
||||
@@ -178,14 +198,26 @@ const state = reactive({
|
||||
code: [{ required: true, message: '请输入OTP授权码', trigger: 'blur' }],
|
||||
},
|
||||
},
|
||||
baseInfoDialog: {
|
||||
visible: false,
|
||||
form: {
|
||||
username: '',
|
||||
name: '',
|
||||
},
|
||||
rules: {
|
||||
username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
|
||||
name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
|
||||
},
|
||||
},
|
||||
loading: {
|
||||
signIn: false,
|
||||
changePwd: false,
|
||||
otpConfirm: false,
|
||||
updateUserConfirm: false,
|
||||
},
|
||||
});
|
||||
|
||||
const { accountLoginSecurity, showLoginFailTips, captchaImage, loginForm, changePwdDialog, otpDialog, loading } = toRefs(state);
|
||||
const { accountLoginSecurity, showLoginFailTips, captchaImage, loginForm, changePwdDialog, otpDialog, baseInfoDialog, loading } = toRefs(state);
|
||||
|
||||
onMounted(async () => {
|
||||
nextTick(async () => {
|
||||
@@ -268,7 +300,17 @@ const onSignIn = async () => {
|
||||
loginResDeal(loginRes);
|
||||
};
|
||||
|
||||
const updateUserInfo = async () => {
|
||||
const form = state.baseInfoDialog.form;
|
||||
await personApi.updateAccount.request(state.baseInfoDialog.form);
|
||||
state.baseInfoDialog.visible = false;
|
||||
useUserInfo().userInfo.username = form.username;
|
||||
useUserInfo().userInfo.name = form.name;
|
||||
await toIndex();
|
||||
};
|
||||
|
||||
const loginResDeal = (loginRes: any) => {
|
||||
state.loginRes = loginRes;
|
||||
// 用户信息
|
||||
const userInfos = {
|
||||
name: loginRes.name,
|
||||
@@ -300,16 +342,26 @@ const loginResDeal = (loginRes: any) => {
|
||||
}, 400);
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
loginResDeal,
|
||||
});
|
||||
|
||||
// 登录成功后的跳转
|
||||
const signInSuccess = async (accessToken: string = '') => {
|
||||
if (!accessToken) {
|
||||
accessToken = getSession('token');
|
||||
}
|
||||
// 存储 token 到浏览器缓存
|
||||
setSession('token', accessToken);
|
||||
// 初始化路由
|
||||
await initRouter();
|
||||
|
||||
// 判断是否为第一次oauth2登录,是的话需要用户填写姓名和用户名
|
||||
if (state.loginRes.isFirstOauth2Login) {
|
||||
state.baseInfoDialog.form.username = state.loginRes.username;
|
||||
state.baseInfoDialog.visible = true;
|
||||
} else {
|
||||
await toIndex();
|
||||
}
|
||||
};
|
||||
|
||||
const toIndex = async () => {
|
||||
// 初始化登录成功时间问候语
|
||||
let currentTimeInfo = currentTime.value;
|
||||
// 登录成功,跳到转首页
|
||||
@@ -356,6 +408,10 @@ const cancelChangePwd = () => {
|
||||
state.changePwdDialog.form.username = '';
|
||||
getCaptcha();
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
loginResDeal,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -68,19 +68,22 @@ onMounted(async () => {
|
||||
});
|
||||
|
||||
const oauth2Login = () => {
|
||||
const width = 700;
|
||||
const height = 500;
|
||||
var iTop = (window.screen.height - 30 - height) / 2; //获得窗口的垂直位置;
|
||||
var iLeft = (window.screen.width - 10 - width) / 2; //获得窗口的水平位置;
|
||||
// 小窗口打开oauth2鉴权
|
||||
let oauthWindoe = window.open(config.baseApiUrl + '/auth/oauth2/login', 'oauth2', 'width=600,height=600');
|
||||
if (oauthWindoe) {
|
||||
let oauthWindow = window.open(config.baseApiUrl + '/auth/oauth2/login', 'oauth2', `height=${height},width=${width},top=${iTop},left=${iLeft},location=no`);
|
||||
if (oauthWindow) {
|
||||
const handler = (e: any) => {
|
||||
if (e.data.action === 'oauthLogin') {
|
||||
oauthWindoe!.close();
|
||||
window.removeEventListener('message', handler);
|
||||
loginForm.value!.loginResDeal(e.data);
|
||||
}
|
||||
};
|
||||
window.addEventListener('message', handler);
|
||||
setInterval(() => {
|
||||
if (oauthWindoe!.closed) {
|
||||
if (oauthWindow!.closed) {
|
||||
window.removeEventListener('message', handler);
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
39
mayfly_go_web/src/views/oauth/Oauth2Callback.vue
Normal file
39
mayfly_go_web/src/views/oauth/Oauth2Callback.vue
Normal file
@@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<div></div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import openApi from '@/common/openApi';
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const queryParam = route.query;
|
||||
// 使用hash路由,回调code可能会被设置到search
|
||||
// 如 localhost:8888/?code=xxxx/oauth2/callback,导致route.query获取不到值
|
||||
if (location.search) {
|
||||
const searchParams = location.search.split('?')[1];
|
||||
if (searchParams) {
|
||||
for (let searchParam of searchParams.split('&')) {
|
||||
const searchParamSplit = searchParam.split('=');
|
||||
queryParam[searchParamSplit[0]] = searchParamSplit[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const res: any = await openApi.oauth2Callback(queryParam);
|
||||
ElMessage.success('授权认证成功');
|
||||
top?.opener.postMessage(res);
|
||||
window.close();
|
||||
} catch (e: any) {
|
||||
setTimeout(() => {
|
||||
window.close();
|
||||
}, 1500);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="scss"></style>
|
||||
@@ -5,4 +5,5 @@ export const personApi = {
|
||||
updateAccount: Api.newPut('/sys/accounts/self'),
|
||||
authStatus: Api.newGet('/auth/oauth2/status'),
|
||||
getMsgs: Api.newGet('/msgs/self'),
|
||||
unbindOauth2: Api.newGet('/auth/oauth2/unbind'),
|
||||
};
|
||||
|
||||
@@ -136,7 +136,8 @@
|
||||
<div class="personal-edit-safe-item-left-value">当前状态:{{ authStatus.bind ? '已绑定' : '未绑定' }}</div>
|
||||
</div>
|
||||
<div class="personal-edit-safe-item-right">
|
||||
<el-button link @click="bindOAuth2" :disabled="authStatus.bind" type="primary">立即绑定</el-button>
|
||||
<el-button v-if="!authStatus.bind" link @click="bindOAuth2" type="primary">立即绑定</el-button>
|
||||
<el-button v-else link @click="unbindOAuth2()" type="warning">解绑</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -202,8 +203,8 @@ const state = reactive({
|
||||
password: '',
|
||||
},
|
||||
authStatus: {
|
||||
enable: { oauth2: false },
|
||||
bind: { oauth2: false },
|
||||
enable: false,
|
||||
bind: false,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -241,12 +242,19 @@ const updateAccount = async () => {
|
||||
};
|
||||
|
||||
const bindOAuth2 = () => {
|
||||
const width = 700;
|
||||
const height = 500;
|
||||
var iTop = (window.screen.height - 30 - height) / 2; //获得窗口的垂直位置;
|
||||
var iLeft = (window.screen.width - 10 - width) / 2; //获得窗口的水平位置;
|
||||
// 小窗口打开oauth2鉴权
|
||||
let oauthWindow = window.open(config.baseApiUrl + '/auth/oauth2/bind?token=' + getSession('token'), 'oauth2', 'width=600,height=600');
|
||||
let oauthWindow = window.open(
|
||||
config.baseApiUrl + '/auth/oauth2/bind?token=' + getSession('token'),
|
||||
'oauth2',
|
||||
`height=${height},width=${width},top=${iTop},left=${iLeft},location=no`
|
||||
);
|
||||
if (oauthWindow) {
|
||||
const handler = (e: any) => {
|
||||
if (e.data.action === 'oauthBind') {
|
||||
oauthWindow!.close();
|
||||
window.removeEventListener('message', handler);
|
||||
// 处理登录token
|
||||
ElMessage.success('绑定成功');
|
||||
@@ -264,6 +272,12 @@ const bindOAuth2 = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const unbindOAuth2 = async () => {
|
||||
await personApi.unbindOauth2.request();
|
||||
ElMessage.success('解绑成功');
|
||||
state.authStatus = await personApi.authStatus.request();
|
||||
};
|
||||
|
||||
const getMsgs = async () => {
|
||||
const res = await personApi.getMsgs.request(state.msgDialog.query);
|
||||
state.msgDialog.msgs = res;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"mayfly-go/internal/auth/api/vo"
|
||||
@@ -59,7 +58,7 @@ func (a *Oauth2Login) OAuth2Callback(rc *req.Ctx) {
|
||||
biz.NotEmpty(stateAction, "state已过期, 请重新登录")
|
||||
|
||||
token, err := client.Exchange(rc.GinCtx, code)
|
||||
biz.ErrIsNilAppendErr(err, "获取token失败: %s")
|
||||
biz.ErrIsNilAppendErr(err, "获取OAuth2 accessToken失败: %s")
|
||||
|
||||
// 获取用户信息
|
||||
httpCli := client.Client(rc.GinCtx.Request.Context(), token)
|
||||
@@ -104,7 +103,12 @@ func (a *Oauth2Login) OAuth2Callback(rc *req.Ctx) {
|
||||
err = a.Oauth2App.GetOAuthAccount(&entity.Oauth2Account{
|
||||
AccountId: accountId,
|
||||
}, "account_id", "identity")
|
||||
biz.IsTrue(err != nil, "该账号已被绑定")
|
||||
biz.IsTrue(err != nil, "该账号已被其他用户绑定")
|
||||
|
||||
err = a.Oauth2App.GetOAuthAccount(&entity.Oauth2Account{
|
||||
Identity: userId,
|
||||
}, "account_id", "identity")
|
||||
biz.IsTrue(err != nil, "您已绑定其他账号")
|
||||
|
||||
now := time.Now()
|
||||
err = a.Oauth2App.BindOAuthAccount(&entity.Oauth2Account{
|
||||
@@ -118,13 +122,7 @@ func (a *Oauth2Login) OAuth2Callback(rc *req.Ctx) {
|
||||
"action": "oauthBind",
|
||||
"bind": true,
|
||||
}
|
||||
b, err = json.Marshal(res)
|
||||
biz.ErrIsNil(err, "数据序列化失败")
|
||||
rc.GinCtx.Header("Content-Type", "text/html; charset=utf-8")
|
||||
rc.GinCtx.Writer.WriteHeader(http.StatusOK)
|
||||
_, _ = rc.GinCtx.Writer.WriteString("<html>" +
|
||||
"<script>top.opener.postMessage(" + string(b) + ")</script>" +
|
||||
"</html>")
|
||||
rc.ResData = res
|
||||
} else {
|
||||
panic(biz.NewBizErr("state不合法"))
|
||||
}
|
||||
@@ -132,14 +130,12 @@ func (a *Oauth2Login) OAuth2Callback(rc *req.Ctx) {
|
||||
|
||||
// 指定登录操作
|
||||
func (a *Oauth2Login) doLoginAction(rc *req.Ctx, userId string, oauth *sysentity.ConfigOauth2Login) {
|
||||
clientIp := getIpAndRegion(rc)
|
||||
rc.ReqParam = fmt.Sprintf("oauth2 login username: %s | ip: %s", userId, clientIp)
|
||||
|
||||
// 查询用户是否存在
|
||||
oauthAccount := &entity.Oauth2Account{Identity: userId}
|
||||
err := a.Oauth2App.GetOAuthAccount(oauthAccount, "account_id", "identity")
|
||||
|
||||
var accountId uint64
|
||||
isFirst := false
|
||||
// 不存在,进行注册
|
||||
if err != nil {
|
||||
biz.IsTrue(oauth.AutoRegister, "系统未开启自动注册, 请先让管理员添加对应账号")
|
||||
@@ -164,6 +160,7 @@ func (a *Oauth2Login) doLoginAction(rc *req.Ctx, userId string, oauth *sysentity
|
||||
})
|
||||
biz.ErrIsNilAppendErr(err, "绑定用户失败: %s")
|
||||
accountId = account.Id
|
||||
isFirst = true
|
||||
} else {
|
||||
accountId = oauthAccount.AccountId
|
||||
}
|
||||
@@ -175,15 +172,13 @@ func (a *Oauth2Login) doLoginAction(rc *req.Ctx, userId string, oauth *sysentity
|
||||
err = a.AccountApp.GetAccount(account, "Id", "Name", "Username", "Password", "Status", "LastLoginTime", "LastLoginIp", "OtpSecret")
|
||||
biz.ErrIsNilAppendErr(err, "获取用户信息失败: %s")
|
||||
|
||||
clientIp := getIpAndRegion(rc)
|
||||
rc.ReqParam = fmt.Sprintf("oauth2 login username: %s | ip: %s", account.Username, clientIp)
|
||||
|
||||
res := LastLoginCheck(account, a.ConfigApp.GetConfig(sysentity.ConfigKeyAccountLoginSecurity).ToAccountLoginSecurity(), clientIp)
|
||||
res["action"] = "oauthLogin"
|
||||
b, err := json.Marshal(res)
|
||||
biz.ErrIsNil(err, "数据序列化失败")
|
||||
rc.GinCtx.Header("Content-Type", "text/html; charset=utf-8")
|
||||
rc.GinCtx.Writer.WriteHeader(http.StatusOK)
|
||||
_, _ = rc.GinCtx.Writer.WriteString("<html>" +
|
||||
"<script>top.opener.postMessage(" + string(b) + ")</script>" +
|
||||
"</html>")
|
||||
res["isFirstOauth2Login"] = isFirst
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
func (a *Oauth2Login) getOAuthClient() (*oauth2.Config, *sysentity.ConfigOauth2Login) {
|
||||
@@ -198,7 +193,7 @@ func (a *Oauth2Login) getOAuthClient() (*oauth2.Config, *sysentity.ConfigOauth2L
|
||||
AuthURL: oath2LoginConfig.AuthorizationURL,
|
||||
TokenURL: oath2LoginConfig.AccessTokenURL,
|
||||
},
|
||||
RedirectURL: oath2LoginConfig.RedirectURL + "/api/auth/oauth2/callback",
|
||||
RedirectURL: oath2LoginConfig.RedirectURL + "/#/oauth2/callback",
|
||||
Scopes: strings.Split(oath2LoginConfig.Scopes, ","),
|
||||
}
|
||||
return client, oath2LoginConfig
|
||||
@@ -217,3 +212,7 @@ func (a *Oauth2Login) Oauth2Status(ctx *req.Ctx) {
|
||||
|
||||
ctx.ResData = res
|
||||
}
|
||||
|
||||
func (a *Oauth2Login) Oauth2Unbind(rc *req.Ctx) {
|
||||
a.Oauth2App.Unbind(rc.LoginAccount.Id)
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ type Oauth2 interface {
|
||||
GetOAuthAccount(condition *entity.Oauth2Account, cols ...string) error
|
||||
|
||||
BindOAuthAccount(e *entity.Oauth2Account) error
|
||||
|
||||
Unbind(accountId uint64)
|
||||
}
|
||||
|
||||
func newAuthApp(oauthAccountRepo repository.Oauth2Account) Oauth2 {
|
||||
@@ -36,3 +38,7 @@ func (a *oauth2AppImpl) GetOAuthAccount(condition *entity.Oauth2Account, cols ..
|
||||
func (a *oauth2AppImpl) BindOAuthAccount(e *entity.Oauth2Account) error {
|
||||
return a.oauthAccountRepo.SaveOAuthAccount(e)
|
||||
}
|
||||
|
||||
func (a *oauth2AppImpl) Unbind(accountId uint64) {
|
||||
a.oauthAccountRepo.DeleteBy(&entity.Oauth2Account{AccountId: accountId})
|
||||
}
|
||||
|
||||
@@ -7,4 +7,6 @@ type Oauth2Account interface {
|
||||
GetOAuthAccount(condition *entity.Oauth2Account, cols ...string) error
|
||||
|
||||
SaveOAuthAccount(e *entity.Oauth2Account) error
|
||||
|
||||
DeleteBy(e *entity.Oauth2Account)
|
||||
}
|
||||
|
||||
@@ -22,3 +22,7 @@ func (a *oauth2AccountRepoImpl) SaveOAuthAccount(e *entity.Oauth2Account) error
|
||||
}
|
||||
return gormx.UpdateById(e)
|
||||
}
|
||||
|
||||
func (a *oauth2AccountRepoImpl) DeleteBy(e *entity.Oauth2Account) {
|
||||
gormx.DeleteByCondition(e)
|
||||
}
|
||||
|
||||
@@ -45,9 +45,11 @@ func Init(router *gin.RouterGroup) {
|
||||
req.NewGet("/oauth2/bind", oauth2Login.OAuth2Bind),
|
||||
|
||||
// oauth2回调地址
|
||||
req.NewGet("/oauth2/callback", oauth2Login.OAuth2Callback).Log(req.NewLogSave("oauth2回调")).NoRes().DontNeedToken(),
|
||||
req.NewGet("/oauth2/callback", oauth2Login.OAuth2Callback).Log(req.NewLogSave("oauth2回调")).DontNeedToken(),
|
||||
|
||||
req.NewGet("/oauth2/status", oauth2Login.Oauth2Status),
|
||||
|
||||
req.NewGet("/oauth2/unbind", oauth2Login.Oauth2Unbind).Log(req.NewLogSave("oauth2解绑")),
|
||||
}
|
||||
|
||||
req.BatchSetGroup(rg, reqs[:])
|
||||
|
||||
@@ -105,6 +105,13 @@ func (a *Account) UpdateAccount(rc *req.Ctx) {
|
||||
biz.IsTrue(utils.CheckAccountPasswordLever(updateAccount.Password), "密码强度必须8位以上且包含字⺟⼤⼩写+数字+特殊符号")
|
||||
updateAccount.Password = cryptox.PwdHash(updateAccount.Password)
|
||||
}
|
||||
|
||||
oldAcc := a.AccountApp.GetById(updateAccount.Id)
|
||||
// 账号创建十分钟内允许修改用户名(兼容oauth2首次登录修改用户名),否则不允许修改
|
||||
if oldAcc.CreateTime.Add(10 * time.Minute).Before(time.Now()) {
|
||||
// 禁止更新用户名,防止误传被更新
|
||||
updateAccount.Username = ""
|
||||
}
|
||||
a.AccountApp.Update(updateAccount)
|
||||
}
|
||||
|
||||
@@ -133,6 +140,8 @@ func (a *Account) SaveAccount(rc *req.Ctx) {
|
||||
biz.IsTrue(utils.CheckAccountPasswordLever(account.Password), "密码强度必须8位以上且包含字⺟⼤⼩写+数字+特殊符号")
|
||||
account.Password = cryptox.PwdHash(account.Password)
|
||||
}
|
||||
// 更新操作不允许修改用户名、防止误传更新
|
||||
account.Username = ""
|
||||
a.AccountApp.Update(account)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@ type AccountCreateForm struct {
|
||||
}
|
||||
|
||||
type AccountUpdateForm struct {
|
||||
Password *string `json:"password" binding:"min=6,max=16"`
|
||||
Name string `json:"name" binding:"max=16"` // 姓名
|
||||
Username string `json:"username" binding:"max=20"`
|
||||
Password *string `json:"password"`
|
||||
}
|
||||
|
||||
type AccountChangePasswordForm struct {
|
||||
|
||||
@@ -14,6 +14,8 @@ import (
|
||||
type Account interface {
|
||||
GetAccount(condition *entity.Account, cols ...string) error
|
||||
|
||||
GetById(id uint64) *entity.Account
|
||||
|
||||
GetPageList(condition *entity.Account, pageParam *model.PageParam, toEntity any, orderBy ...string) *model.PageResult[any]
|
||||
|
||||
Create(account *entity.Account)
|
||||
@@ -38,6 +40,10 @@ func (a *accountAppImpl) GetAccount(condition *entity.Account, cols ...string) e
|
||||
return a.accountRepo.GetAccount(condition, cols...)
|
||||
}
|
||||
|
||||
func (a *accountAppImpl) GetById(id uint64) *entity.Account {
|
||||
return a.accountRepo.GetById(id)
|
||||
}
|
||||
|
||||
func (a *accountAppImpl) GetPageList(condition *entity.Account, pageParam *model.PageParam, toEntity any, orderBy ...string) *model.PageResult[any] {
|
||||
return a.accountRepo.GetPageList(condition, pageParam, toEntity)
|
||||
}
|
||||
@@ -51,8 +57,12 @@ func (a *accountAppImpl) Create(account *entity.Account) {
|
||||
}
|
||||
|
||||
func (a *accountAppImpl) Update(account *entity.Account) {
|
||||
// 禁止更新用户名,防止误传被更新
|
||||
account.Username = ""
|
||||
if account.Username != "" {
|
||||
unAcc := &entity.Account{Username: account.Username}
|
||||
err := a.GetAccount(unAcc)
|
||||
biz.IsTrue(err != nil || unAcc.Id == account.Id, "该用户名已存在")
|
||||
}
|
||||
|
||||
a.accountRepo.Update(account)
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ type Account interface {
|
||||
// 根据条件获取账号信息
|
||||
GetAccount(condition *entity.Account, cols ...string) error
|
||||
|
||||
GetById(id uint64) *entity.Account
|
||||
|
||||
GetPageList(condition *entity.Account, pageParam *model.PageParam, toEntity any, orderBy ...string) *model.PageResult[any]
|
||||
|
||||
Insert(account *entity.Account)
|
||||
|
||||
@@ -18,6 +18,14 @@ func (a *accountRepoImpl) GetAccount(condition *entity.Account, cols ...string)
|
||||
return gormx.GetBy(condition, cols...)
|
||||
}
|
||||
|
||||
func (a *accountRepoImpl) GetById(id uint64) *entity.Account {
|
||||
ac := new(entity.Account)
|
||||
if err := gormx.GetById(ac, id); err != nil {
|
||||
return nil
|
||||
}
|
||||
return ac
|
||||
}
|
||||
|
||||
func (m *accountRepoImpl) GetPageList(condition *entity.Account, pageParam *model.PageParam, toEntity any, orderBy ...string) *model.PageResult[any] {
|
||||
qd := gormx.NewQuery(new(entity.Account)).
|
||||
Like("name", condition.Name).
|
||||
|
||||
Reference in New Issue
Block a user