搭建个人知识付费应用系统-(2)用户|搭建个人知识付费应用系统-(2)用户 Session 管理

视频地址: https://www.bilibili.com/vide...
用户管理

graph TBStart1([Start])--> logout1[注销] --> check1{判断 url 参数} --F--> destroySession[清除 Session 然后 SSO 注销] -->logout1 check1 --T--> logout2[跳转 SSO 注销链接] logout2-->Stop1([Stop])Start2([Start]) --登录回调--> callback[记录 Session] --> redierct[跳回页面] --> loader[Loader 读取用户信息] --> check2{是否失效} --F--> getUserInfo[获取用户信息] --> Stop2([Stop]) check2 --T--> refreshToken[通过 refreshToken 更新 accessToken] --> getUserInfo

Session 管理
SessionStorage
import { createCookieSessionStorage } from '@remix-run/node'; export const sessionStorage = createCookieSessionStorage({ cookie: { name: '_session', sameSite: 'lax', path: '/', httpOnly: true, secrets: [process.env.COOKIE_SECRET || 's3cr3t'], secure: process.env.NODE_ENV === 'production' } }); export const { getSession, commitSession, destroySession } = sessionStorage;

Token 管理
将 code 换 AccessToken 和 RefreshToken 换 AccessToken 两个方法封装
async function tokenRequest(body) { const formBody = []; // eslint-disable-next-line for (const property in body) { const encodedKey = encodeURIComponent(property); // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unsafe-argument const encodedValue = https://www.it610.com/article/encodeURIComponent(body[property]); formBody.push(`${encodedKey}=${encodedValue}`); } const res = await fetch(`${process.env.AUTHING_APP_DOMAIN}/oidc/token`, { method:'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }, body: formBody.join('&') }); const oidcToken = (await res.json()) as OidcResponse; return oidcToken; }export function code2Token(code: string) { const body = { client_id: process.env.AUTHING_APP_ID, client_secret: process.env.AUTHING_APP_SECRET, grant_type: 'authorization_code', code }; return tokenRequest(body); }export function refreshToken(token: OidcResponse) { const body = { client_id: process.env.AUTHING_APP_ID, client_secret: process.env.AUTHING_APP_SECRET, grant_type: 'refresh_token', refresh_token: token.refresh_token }; return tokenRequest(body); }

i18n 【搭建个人知识付费应用系统-(2)用户|搭建个人知识付费应用系统-(2)用户 Session 管理】插件: https://remix-i18n.js.cool
安装
npm install --save remix-i18n

配置
export interface RemixI18nOptions { // 支持的语言 supportedLanguages: string[]; // 失败备选 fallbackLng: string; }

const i18n = new RemixI18n({ supportedLanguages: ['en', 'tl', 'da', 'zh'], fallbackLng: 'zh' });

添加语言翻译
i18n.set('locale', { hello: '你好' });

客户端设置
// entry.client.tsx import { hydrate } from 'react-dom'; import { RemixBrowser } from 'remix'; import { I18nProvider } from 'remix-i18n'; import { i18n, getLocale } from '~/i18n'; const locale = getLocale(window.location.pathname); i18n.locale(locale); hydrate( , document );

服务器端设置
// entry.server.tsx import { renderToString } from 'react-dom/server'; import { RemixServer } from 'remix'; import type { EntryContext } from 'remix'; import { I18nProvider } from 'remix-i18n'; import { i18n, getLocale } from '~/i18n'; export default function handleRequest( request: Request, responseStatusCode: number, responseHeaders: Headers, remixContext: EntryContext ) { const locale = getLocale(new URL(request.url).pathname); i18n.locale(locale); const markup = renderToString( ); responseHeaders.set('Content-Type', 'text/html'); return new Response(`${markup}`, { status: responseStatusCode, headers: responseHeaders }); }

语言切换
const i18n = useI18n(); const location = useLocation(); useEffect(() => { const locale = getLocale(location.pathname); if (locale !== i18n.locale()) { i18n.locale(locale); } }, [location]);

使用模板
const { t } = useI18n(); // jsx {t('hello')};

P.S. 遗留问题: 用户扩展信息的获取(抽空摸索清楚再继续)
下期主题: DaisyUI 主题切换实现

    推荐阅读