feat: chat password auth (#3318)

This commit is contained in:
shaohuzhang1 2025-06-19 20:02:52 +08:00 committed by GitHub
parent 7aa73e7110
commit 4e170c6ed7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 100 additions and 44 deletions

View File

@ -45,7 +45,7 @@ class ChatAnonymousUserToken(AuthBaseHandle):
if application_setting_model is not None: if application_setting_model is not None:
application_setting = QuerySet(application_setting_model).filter(application_id=application_id).first() application_setting = QuerySet(application_setting_model).filter(application_id=application_id).first()
if application_setting.authentication: if application_setting.authentication:
if 'password' != chat_user_token.authentication.auth_type: if 'password' != application_setting.authentication_value.get('type', ''):
raise AppAuthenticationFailed(1002, _('Authentication information is incorrect')) raise AppAuthenticationFailed(1002, _('Authentication information is incorrect'))
return None, ChatAuth( return None, ChatAuth(
current_role_list=[RoleConstants.CHAT_ANONYMOUS_USER], current_role_list=[RoleConstants.CHAT_ANONYMOUS_USER],

View File

@ -59,6 +59,20 @@ const anonymousAuthentication: (
) => Promise<Result<any>> = (assessToken, loading) => { ) => Promise<Result<any>> = (assessToken, loading) => {
return post('/auth/anonymous', { access_token: assessToken }, {}, loading) return post('/auth/anonymous', { access_token: assessToken }, {}, loading)
} }
/**
*
* @param assessToken
* @param password
* @param loading
* @returns
*/
const passwordAuthentication: (
assessToken: string,
password: string,
loading?: Ref<boolean>,
) => Promise<Result<any>> = (assessToken, password, loading) => {
return post('auth/password', { access_token: assessToken, password: password }, {}, loading)
}
/** /**
* *
* @param loading * @param loading
@ -161,4 +175,5 @@ export default {
getQrSource, getQrSource,
ldapLogin, ldapLogin,
getAuthSetting, getAuthSetting,
passwordAuthentication,
} }

View File

@ -74,6 +74,12 @@ const useChatUserStore = defineStore('chat-user', {
return this.token return this.token
}) })
}, },
passwordAuthentication(password: string) {
return ChatAPI.passwordAuthentication(this.accessToken as string, password).then((ok) => {
this.setToken(ok.data)
return this.token
})
},
login(request: LoginRequest, loading?: Ref<boolean>) { login(request: LoginRequest, loading?: Ref<boolean>) {
return ChatAPI.login(this.accessToken as string, request, loading).then((ok) => { return ChatAPI.login(this.accessToken as string, request, loading).then((ok) => {
this.setToken(ok.data.token) this.setToken(ok.data.token)

View File

@ -40,7 +40,7 @@
class="mb-16" class="mb-16"
:class="form.authentication_value.type === 'password' ? 'active' : ''" :class="form.authentication_value.type === 'password' ? 'active' : ''"
> >
<el-radio value="replace" size="large"> <el-radio value="password" size="large">
<p class="mb-4 lighter"> <p class="mb-4 lighter">
{{ $t('views.applicationOverview.appInfo.LimitDialog.authenticationValue') }} {{ $t('views.applicationOverview.appInfo.LimitDialog.authenticationValue') }}
</p> </p>

View File

@ -1,53 +1,31 @@
<template> <template>
<el-dialog <el-form ref="FormRef" :model="form" @submit.prevent="validator">
:modelValue="show" <el-form-item prop="value" :rules="rules.password">
modal-class="positioned-mask" <el-input show-password v-model="form.password" />
width="300" </el-form-item>
:title="$t('chat.passwordValidator.title')" <el-button class="w-full mt-8" type="primary" @click="validator" :loading="loading">
custom-class="no-close-button" {{ $t('common.confirm') }}</el-button
:close-on-click-modal="false" >
:close-on-press-escape="false" </el-form>
:show-close="false"
top="25vh"
center
:modal="true"
>
<el-form ref="FormRef" :model="form" @submit.prevent="validator">
<el-form-item prop="value" :rules="rules.value">
<el-input show-password v-model="form.value" />
</el-form-item>
<el-button class="w-full mt-8" type="primary" @click="validator" :loading="loading">
{{ $t('common.confirm') }}</el-button
>
</el-form>
</el-dialog>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed } from 'vue' import { ref, computed } from 'vue'
import { useRoute } from 'vue-router'
import useStore from '@/stores' import useStore from '@/stores'
import { t } from '@/locales' import { t } from '@/locales'
import { useRoute, useRouter } from 'vue-router'
const route = useRoute() const route = useRoute()
const FormRef = ref() const FormRef = ref()
const {
params: { accessToken } const { chatUser } = useStore()
} = route as any
const { application } = useStore()
const props = defineProps<{ applicationProfile: any; modelValue: boolean }>() const props = defineProps<{ applicationProfile: any; modelValue: boolean }>()
const loading = ref<boolean>(false) const loading = ref<boolean>(false)
const show = computed(() => { const router = useRouter()
if (props.applicationProfile) {
if (props.modelValue) {
return false
}
return props.applicationProfile.authentication
}
return false
})
const emit = defineEmits(['update:modelValue']) const emit = defineEmits(['update:modelValue'])
const auth = () => { const auth = () => {
return application.asyncAppAuthentication(accessToken, loading, form.value).then(() => { return chatUser.passwordAuthentication(form.value.password).then((ok) => {
emit('update:modelValue', true) router.push({ name: 'chat', params: { accessToken: chatUser.accessToken } })
}) })
} }
const validator_auth = (rule: any, value: string, callback: any) => { const validator_auth = (rule: any, value: string, callback: any) => {
@ -64,12 +42,11 @@ const validator = () => {
} }
const rules = { const rules = {
value: [{ required: true, validator: validator_auth, trigger: 'manual' }] password: [{ required: true, validator: validator_auth, trigger: 'manual' }],
} }
const form = ref({ const form = ref({
type: 'password', password: '',
value: ''
}) })
</script> </script>
<style lang="scss"> <style lang="scss">

View File

@ -0,0 +1,50 @@
<template>
<div>
<el-form ref="FormRef" :model="form" @submit.prevent="validator">
<el-form-item prop="value" :rules="rules.value">
<el-input show-password v-model="form.value" />
</el-form-item>
<el-button class="w-full mt-8" type="primary" @click="validator" :loading="loading">
{{ $t('common.confirm') }}</el-button
>
</el-form>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { useRoute } from 'vue-router'
import useStore from '@/stores'
import { t } from '@/locales'
const route = useRoute()
const FormRef = ref()
const {
params: { accessToken },
} = route as any
const { application } = useStore()
const loading = ref<boolean>(false)
const auth = () => {
return application.asyncAppAuthentication(accessToken, loading, form.value).then(() => {})
}
const validator_auth = (rule: any, value: string, callback: any) => {
if (value === '') {
callback(new Error(t('chat.passwordValidator.errorMessage1')))
} else {
auth().catch(() => {
callback(new Error(t('chat.passwordValidator.errorMessage2')))
})
}
}
const validator = () => {
FormRef.value.validate()
}
const rules = {
value: [{ required: true, validator: validator_auth, trigger: 'manual' }],
}
const form = ref({
type: 'password',
value: '',
})
</script>
<style lang="scss" scoped></style>

View File

@ -1,6 +1,13 @@
<template> <template>
<UserLoginLayout v-if="!loading" v-loading="loading"> <UserLoginLayout v-if="!loading" v-loading="loading">
<LoginContainer :subTitle="theme.themeInfo?.slogan || $t('theme.defaultSlogan')"> <LoginContainer
v-if="chatUser.chat_profile?.authentication_type == 'password'"
:subTitle="theme.themeInfo?.slogan || $t('theme.defaultSlogan')"
>
<PasswordAuth></PasswordAuth>
</LoginContainer>
<LoginContainer v-else :subTitle="theme.themeInfo?.slogan || $t('theme.defaultSlogan')">
<h2 class="mb-24" v-if="!showQrCodeTab"> <h2 class="mb-24" v-if="!showQrCodeTab">
{{ loginMode == 'LOCAL' ? $t('views.login.title') : loginMode }} {{ loginMode == 'LOCAL' ? $t('views.login.title') : loginMode }}
</h2> </h2>
@ -139,6 +146,7 @@ import useStore from '@/stores'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import QrCodeTab from '@/views/login/scanCompinents/QrCodeTab.vue' import QrCodeTab from '@/views/login/scanCompinents/QrCodeTab.vue'
import { MsgConfirm, MsgError } from '@/utils/message.ts' import { MsgConfirm, MsgError } from '@/utils/message.ts'
import PasswordAuth from '@/views/chat/auth/component/password.vue'
import useUserStore from '@/stores/modules/user.ts' import useUserStore from '@/stores/modules/user.ts'
const router = useRouter() const router = useRouter()