feat:限制必须api-key或登录
parent
82ea3bfa7d
commit
fc73869fa5
|
|
@ -14,6 +14,7 @@ from django.utils.translation import gettext_lazy as _
|
|||
from rest_framework import serializers
|
||||
|
||||
from application.models import ApplicationAccessToken, ChatUserType, Application, ApplicationVersion
|
||||
from application.models.application_api_key import ApplicationApiKey
|
||||
from application.serializers.application import ApplicationSerializerModel
|
||||
from common.auth.common import ChatUserToken, ChatAuthentication
|
||||
from common.auth.handle.impl.user_token import UserToken
|
||||
|
|
@ -26,12 +27,27 @@ from common.utils.rsa_util import get_key_pair_by_sql
|
|||
|
||||
class AnonymousAuthenticationSerializer(serializers.Serializer):
|
||||
access_token = serializers.CharField(required=True, label=_("access_token"))
|
||||
api_key = serializers.CharField(required=False, allow_blank=True, allow_null=True, label=_("api key"))
|
||||
|
||||
@staticmethod
|
||||
def get_platform_user(request):
|
||||
if request is None:
|
||||
return None
|
||||
platform_auth = request.META.get('HTTP_X_PLATFORM_AUTHORIZATION')
|
||||
if platform_auth is None or not platform_auth.startswith('Bearer '):
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def validate_application_api_key(application_id, api_key):
|
||||
if not api_key:
|
||||
raise AppUnauthorizedFailed(401, _("Authentication information is incorrect"))
|
||||
application_api_key = QuerySet(ApplicationApiKey).filter(
|
||||
application_id=application_id,
|
||||
secret_key=api_key,
|
||||
is_active=True
|
||||
).first()
|
||||
if application_api_key is None:
|
||||
raise AppUnauthorizedFailed(401, _("Authentication information is incorrect"))
|
||||
token = platform_auth[7:]
|
||||
token_details = None
|
||||
|
||||
|
|
@ -67,6 +83,8 @@ class AnonymousAuthenticationSerializer(serializers.Serializer):
|
|||
chat_user_type = ChatUserType.PLATFORM_USER.value
|
||||
user_id = platform_user.id
|
||||
else:
|
||||
api_key = self.data.get('api_key') or request.META.get('HTTP_X_APPLICATION_API_KEY')
|
||||
self.validate_application_api_key(application_access_token.application_id, api_key)
|
||||
chat_user_id = token_details.get('chat_user_id') or str(uuid.uuid7())
|
||||
chat_user_type = ChatUserType.ANONYMOUS_USER.value
|
||||
user_id = None
|
||||
|
|
@ -80,8 +98,9 @@ class AnonymousAuthenticationSerializer(serializers.Serializer):
|
|||
|
||||
class AuthProfileSerializer(serializers.Serializer):
|
||||
access_token = serializers.CharField(required=True, label=_("access_token"))
|
||||
api_key = serializers.CharField(required=False, allow_blank=True, allow_null=True, label=_("api key"))
|
||||
|
||||
def profile(self):
|
||||
def profile(self, request=None):
|
||||
self.is_valid(raise_exception=True)
|
||||
access_token = self.data.get("access_token")
|
||||
application_access_token = QuerySet(ApplicationAccessToken).filter(access_token=access_token).first()
|
||||
|
|
@ -90,6 +109,8 @@ class AuthProfileSerializer(serializers.Serializer):
|
|||
if not application_access_token.is_active:
|
||||
raise NotFound404(404, _("Invalid access_token"))
|
||||
application_id = application_access_token.application_id
|
||||
if AnonymousAuthenticationSerializer.get_platform_user(request) is None:
|
||||
AnonymousAuthenticationSerializer.validate_application_api_key(application_id, self.data.get("api_key"))
|
||||
profile = {
|
||||
'authentication': False
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,4 +103,6 @@ class ChatEmbedSerializer(serializers.Serializer):
|
|||
query += f"&{field['variable']}={params[field['variable']]}"
|
||||
if 'asker' in params:
|
||||
query += f"&asker={params.get('asker')}"
|
||||
if 'api_key' in params:
|
||||
query += f"&api_key={params.get('api_key')}"
|
||||
return query
|
||||
|
|
|
|||
|
|
@ -138,7 +138,10 @@ class AuthProfile(APIView):
|
|||
)
|
||||
def get(self, request: Request):
|
||||
return result.success(
|
||||
AuthProfileSerializer(data={'access_token': request.query_params.get("access_token")}).profile())
|
||||
AuthProfileSerializer(data={
|
||||
'access_token': request.query_params.get("access_token"),
|
||||
'api_key': request.query_params.get("api_key")
|
||||
}).profile(request))
|
||||
|
||||
|
||||
class ChatView(APIView):
|
||||
|
|
|
|||
|
|
@ -47,11 +47,12 @@ const chat: (chat_id: string, data: any) => Promise<any> = (chat_id, data) => {
|
|||
/**
|
||||
* 应用认证信息
|
||||
*/
|
||||
const chatProfile: (assessToken: string, loading?: Ref<boolean>) => Promise<Result<ChatProfile>> = (
|
||||
const chatProfile: (assessToken: string, apiKey?: string, loading?: Ref<boolean>) => Promise<Result<ChatProfile>> = (
|
||||
assessToken,
|
||||
apiKey,
|
||||
loading,
|
||||
) => {
|
||||
return get('/profile', {access_token: assessToken}, loading)
|
||||
return get('/profile', {access_token: assessToken, api_key: apiKey}, loading)
|
||||
}
|
||||
/**
|
||||
* 匿名认证
|
||||
|
|
@ -61,9 +62,10 @@ const chatProfile: (assessToken: string, loading?: Ref<boolean>) => Promise<Resu
|
|||
*/
|
||||
const anonymousAuthentication: (
|
||||
assessToken: string,
|
||||
apiKey?: string,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<any>> = (assessToken, loading) => {
|
||||
return post('/auth/anonymous', {access_token: assessToken}, {}, loading)
|
||||
) => Promise<Result<any>> = (assessToken, apiKey, loading) => {
|
||||
return post('/auth/anonymous', {access_token: assessToken, api_key: apiKey}, {}, loading)
|
||||
}
|
||||
/**
|
||||
* 密码认证
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ router.beforeEach(
|
|||
const { chatUser } = useStore()
|
||||
if (['login', 'chat'].includes(to.name ? to.name.toString() : '')) {
|
||||
chatUser.setAccessToken(to.params.accessToken.toString())
|
||||
chatUser.setApiKey(typeof to.query.api_key === 'string' ? to.query.api_key : undefined)
|
||||
} else {
|
||||
next({
|
||||
path: '/404',
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ interface Chat {
|
|||
token?: string
|
||||
accessToken?: string
|
||||
chatUserType?: string
|
||||
apiKey?: string
|
||||
}
|
||||
|
||||
const useChatUserStore = defineStore('chat-user', {
|
||||
|
|
@ -32,8 +33,11 @@ const useChatUserStore = defineStore('chat-user', {
|
|||
setAccessToken(accessToken: string) {
|
||||
this.accessToken = accessToken
|
||||
},
|
||||
setApiKey(apiKey?: string) {
|
||||
this.apiKey = apiKey
|
||||
},
|
||||
getChatProfile() {
|
||||
return ChatAPI.chatProfile(this.accessToken as string).then((ok) => {
|
||||
return ChatAPI.chatProfile(this.accessToken as string, this.apiKey).then((ok) => {
|
||||
this.chat_profile = ok.data
|
||||
|
||||
return this.chat_profile
|
||||
|
|
@ -92,7 +96,7 @@ const useChatUserStore = defineStore('chat-user', {
|
|||
*匿名认证
|
||||
*/
|
||||
anonymousAuthentication() {
|
||||
return ChatAPI.anonymousAuthentication(this.accessToken as string).then((ok) => {
|
||||
return ChatAPI.anonymousAuthentication(this.accessToken as string, this.apiKey).then((ok) => {
|
||||
const data = typeof ok.data === 'string' ? { token: ok.data, chat_user_type: undefined } : ok.data
|
||||
this.setToken(data.token)
|
||||
this.setChatUserType(data.chat_user_type)
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ const apiType = computed(() => {
|
|||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['addData'])
|
||||
const emit = defineEmits(['addData', 'refresh'])
|
||||
|
||||
const SettingAPIKeyDialogRef = ref()
|
||||
const dialogVisible = ref<boolean>(false)
|
||||
|
|
@ -112,6 +112,7 @@ function deleteApiKey(row: any) {
|
|||
.then(() => {
|
||||
MsgSuccess(t('common.deleteSuccess'))
|
||||
getApiKeyList()
|
||||
emit('refresh')
|
||||
})
|
||||
})
|
||||
.catch(() => {})
|
||||
|
|
@ -129,6 +130,7 @@ async function changeState(row: any) {
|
|||
.then(() => {
|
||||
MsgSuccess(str)
|
||||
getApiKeyList()
|
||||
emit('refresh')
|
||||
return true
|
||||
})
|
||||
.catch(() => {
|
||||
|
|
@ -141,6 +143,7 @@ function createApiKey() {
|
|||
.postAPIKey(id as string, loading)
|
||||
.then(() => {
|
||||
getApiKeyList()
|
||||
emit('refresh')
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -90,6 +90,7 @@ const { application } = useStore()
|
|||
const props = defineProps({
|
||||
data: Object,
|
||||
apiInputParams: String,
|
||||
apiKey: String,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['addData'])
|
||||
|
|
@ -101,10 +102,12 @@ const source1 = ref('')
|
|||
const source2 = ref('')
|
||||
const source3 = ref('')
|
||||
|
||||
const urlParams1 = computed(() => (props.apiInputParams ? '?' + props.apiInputParams : ''))
|
||||
const urlParams2 = computed(() => (props.apiInputParams ? '&' + props.apiInputParams : ''))
|
||||
const apiKeyParam = computed(() => props.apiKey ? `api_key=${encodeURIComponent(props.apiKey)}` : '')
|
||||
const mergedParams = computed(() => [apiKeyParam.value, props.apiInputParams].filter(Boolean).join('&'))
|
||||
const urlParams1 = computed(() => (mergedParams.value ? '?' + mergedParams.value : ''))
|
||||
const urlParams2 = computed(() => (mergedParams.value ? '&' + mergedParams.value : ''))
|
||||
const urlParams3 = computed(() =>
|
||||
props.apiInputParams ? '?mode=mobile&' + props.apiInputParams : '?mode=mobile',
|
||||
mergedParams.value ? '?mode=mobile&' + mergedParams.value : '?mode=mobile',
|
||||
)
|
||||
watch(dialogVisible, (bool) => {
|
||||
if (!bool) {
|
||||
|
|
|
|||
|
|
@ -184,8 +184,9 @@
|
|||
ref="EmbedDialogRef"
|
||||
:data="detail"
|
||||
:api-input-params="mapToUrlParams(apiInputParams)"
|
||||
:api-key="publicApiKey"
|
||||
/>
|
||||
<APIKeyDialog ref="APIKeyDialogRef"/>
|
||||
<APIKeyDialog ref="APIKeyDialogRef" @refresh="getPublicApiKey"/>
|
||||
|
||||
<!-- 社区版访问限制 -->
|
||||
<component :is="currentLimitDialog" ref="LimitDialogRef" @refresh="refresh"/>
|
||||
|
|
@ -238,13 +239,18 @@ const APIKeyDialogRef = ref()
|
|||
const EmbedDialogRef = ref()
|
||||
|
||||
const accessToken = ref<any>({})
|
||||
const publicApiKey = ref('')
|
||||
const detail = ref<any>(null)
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const urlParams = computed(() =>
|
||||
mapToUrlParams(apiInputParams.value) ? '?' + mapToUrlParams(apiInputParams.value) : '',
|
||||
)
|
||||
const urlParams = computed(() => {
|
||||
const params = [
|
||||
publicApiKey.value ? `api_key=${encodeURIComponent(publicApiKey.value)}` : '',
|
||||
mapToUrlParams(apiInputParams.value),
|
||||
].filter(Boolean)
|
||||
return params.length ? '?' + params.join('&') : ''
|
||||
})
|
||||
const shareUrl = computed(
|
||||
() =>
|
||||
`${window.location.origin}${window.MaxKB.chatPrefix}/` +
|
||||
|
|
@ -424,6 +430,24 @@ function openDialog() {
|
|||
EmbedDialogRef.value.open(accessToken.value?.access_token)
|
||||
}
|
||||
|
||||
function getPublicApiKey() {
|
||||
publicApiKey.value = ''
|
||||
loadSharedApi({type: 'applicationKey', systemType: apiType.value})
|
||||
.getAPIKey(id as string, loading)
|
||||
.then((res: any) => {
|
||||
const key = res.data?.find((item: any) => item.is_active) || res.data?.[0]
|
||||
if (key) {
|
||||
publicApiKey.value = key.secret_key
|
||||
return
|
||||
}
|
||||
loadSharedApi({type: 'applicationKey', systemType: apiType.value})
|
||||
.postAPIKey(id as string, loading)
|
||||
.then((apiKeyRes: any) => {
|
||||
publicApiKey.value = apiKeyRes.data?.secret_key || ''
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function getAccessToken() {
|
||||
loadSharedApi({type: 'application', systemType: apiType.value})
|
||||
.getAccessToken(id, loading)
|
||||
|
|
@ -468,6 +492,7 @@ function refresh() {
|
|||
onMounted(() => {
|
||||
getDetail()
|
||||
getAccessToken()
|
||||
getPublicApiKey()
|
||||
changeDayHandle(history_day.value)
|
||||
})
|
||||
</script>
|
||||
|
|
|
|||
Loading…
Reference in New Issue