简述
登录认证是所有系统都会涉及到的问题,牵扯到的知识点有: 密码加密、cookie、session、token、JWT等。
在前后端不分离的应用中,基于Cookie-Session机制能较容易实现会话跟踪和token认证。但前后端分离后,需要前端主动发送cookie或请求头加上token信息才能维持会话。
基于Cookie-Session机制的登录场景有:
- 前后端同域——与普通登录没有区别
- 前后端不同域:
- JSONP方式实现
- CORS方式实现
Nuxt 3 comes with built-in utilities to support session and authentication. We are working on a new official module.
SpringBoot端开发配置
采用sa-token认证框架
Nuxt端
封装$fetch,在请求头里统一加上token
import { Message } from '@arco-design/web-vue'
import type { FetchResponse, SearchParameters } from 'ofetch'
import type { Ref } from 'vue'
import type { UseFetchOptions } from '#app'
import { PageEnum } from '~/enums/pageEnum'
import { RequestEnum, ResultEnum } from '~/enums/httpEnum'
import { useUserStore } from '~/stores/user.store'
import IconEmoticonDead from '~icons/mdi/emoticon-dead'
export interface ResOptions<T> {
data?: T
code?: number
message?: string
success?: boolean
}
type UrlType = string | Request | Ref<string | Request> | (() => string | Request)
export type HttpOption<T> = UseFetchOptions<ResOptions<T>>
const handleError = <T>(response: FetchResponse<ResOptions<T>> & FetchResponse<ResponseType>) => {
const err = (text: string) => {
Message.error({
content: response?._data?.message ?? text,
icon: () => h(IconEmoticonDead),
})
}
if (!response._data) {
err('请求超时,服务器无响应!')
return
}
const userStore = useUserStore()
const handleMap: { [key: number]: () => void } = {
404: () => err('服务器资源不存在'),
500: () => err('服务器内部错误'),
403: () => err('没有权限访问该资源'),
401: () => {
err('登录状态已过期,需要重新登录')
userStore.clearUserInfo()
// TODO 跳转实际登录页
navigateTo('/')
},
}
handleMap[response.status] ? handleMap[response.status]() : err('未知错误!')
}
// get方法传递数组形式参数
const paramsSerializer = (params?: SearchParameters) => {
if (!params)
return
const query = useCloneDeep(params)
Object.entries(query).forEach(([key, val]) => {
if (typeof val === 'object' && Array.isArray(val) && val !== null) {
query[`${key}[]`] = toRaw(val).map((v: any) => JSON.stringify(v))
delete query[key]
}
})
return query
}
const fetch = <T>(url: UrlType, option: UseFetchOptions<ResOptions<T>>) => {
return useFetch<ResOptions<T>>(url, {
// 请求拦截器
onRequest({ options }) {
// get方法传递数组形式参数
options.params = paramsSerializer(options.params)
// 添加baseURL,nuxt3环境变量要从useRuntimeConfig里面取
const { public: { apiBase } } = useRuntimeConfig()
options.baseURL = apiBase
// 添加请求头,没登录不携带token
const userStore = useUserStore()
if (!userStore.isLogin)
return
options.headers = new Headers(options.headers)
options.headers.set('Authorization', `Bearer ${userStore.getToken}`)
},
// 响应拦截
onResponse({ response }) {
if (response.headers.get('content-disposition') && response.status === 200)
return response
// 在这里判断错误
if (response._data.code !== 200) {
handleError<T>(response)
return Promise.reject(response._data)
}
// 成功返回
return response._data
},
// 错误处理
onResponseError({ response }) {
handleError<T>(response)
return Promise.reject(response?._data ?? null)
},
// 合并参数
...option,
})
}
// 自动导出
export const useHttp = {
get: <T>(url: UrlType, params?: any, option?: HttpOption<T>) => {
return fetch<T>(url, { method: 'get', params, ...option })
},
post: <T>(url: UrlType, body?: any, option?: HttpOption<T>) => {
return fetch<T>(url, { method: 'post', body, ...option })
},
put: <T>(url: UrlType, body?: any, option?: HttpOption<T>) => {
return fetch<T>(url, { method: 'put', body, ...option })
},
delete: <T>(url: UrlType, body?: any, option?: HttpOption<T>) => {
return fetch<T>(url, { method: 'delete', body, ...option })
},
}
参考链接
- auth starter template for Nuxt 3
- sidebase nuxt auth powered by Auth.js
- NUXT3 fetch拦截器简单封装