登录鉴权
一个后台服务在 90% 以上的场景都是需要登录鉴权的,毕竟在大多数情况下我们的后台操作是直接操作数据库的。而在 tushan 中我们自然提供了封装好的登录逻辑。
我们需要准备 authProvider 来告诉 tushan 我们要如何处理登录的流程。
<Tushan
  authProvider={authProvider}
>
</Tushan>

其中 authProvider 的接口定义如下:
import { AuthProvider  } from 'tushan';
const authProvider: AuthProvider = {
  login: params => Promise.resolve(/* ... */),
  checkError: error => Promise.resolve(/* ... */),
  checkAuth: params => Promise.resolve(/* ... */),
  logout: () => Promise.resolve(/* ... */),
  getIdentity: () => Promise.resolve(/* ... */),
  handleCallback: () => Promise.resolve(/* ... */), // for 
  getPermissions: () => Promise.resolve(/* ... */),
};
快速开始
Tushan 也提供了一个内置的通用鉴权创建函数
import { AuthProvider, createAuthProvider } from 'tushan';
const authProvider: AuthProvider = createAuthProvider({
  loginUrl: '/api/login',
});
其中配置参数 loginUrl 表示登录接口url地址。
具体的后端登录逻辑需要自行实现。接口需要返回如下内容:
{
  username: string;
  token: string; // 发送请求时可以携带
  expiredAt: number; // timestamp
}
返 回的内容会记录在 authStorageKey(默认"tushan:auth") 指定的 localStorage 中。你可以在后续的网络请求中获取token并在发送的请求头中携带。
配套的,你可以使用对应的createAuthHttpClient函数来创建httpClient在发送请求的时候携带token
const dataProvider = jsonServerProvider(
  config.dataProvider.url,
  createAuthHttpClient()
)
请求携带Token
为了使请求能够携带Token, 我们需要修改一下发送请求时请求函数。
一个简单的示例如下:
import { fetchJSON } from 'tushan';
const authStorageKey = 'tushan:auth';
const httpClient: typeof fetchJSON = (url, options = {}) => {
  try {
    if (!options.headers) {
      options.headers = new Headers({ Accept: 'application/json' });
    }
    const { token } = JSON.parse(
      window.localStorage.getItem(authStorageKey) ?? '{}'
    );
    (options.headers as Headers).set('Authorization', `Bearer ${token}`);
    return fetchJSON(url, options);
  } catch (err) {
    return Promise.reject();
  }
};
const dataProvider = jsonServerProvider('/admin/api', httpClient);
其中 fetchJSON 是对原生fetch方法的简单封装。
node 示例
登录
以下是一个登录的node express服务的中间件的示例,其他语言的实现可以自行参考
const adminAuth = {
  username: process.env.ADMIN_USER,
  password: process.env.ADMIN_PASS,
};
router.post('/api/login', (req, res) => {
  if (!adminAuth.username || !adminAuth.password) {
    res.status(401).end('Server not set env: ADMIN_USER, ADMIN_PASS');
    return;
  }
  const { username, password } = req.body;
  if (username === adminAuth.username && password === adminAuth.password) {
    // 用户名和密码都正确,返回token
    const token = jwt.sign(
      {
        username,
        platform: 'admin',
      },
      authSecret,
      {
        expiresIn: '2h',
      }
    );
    res.json({
      username,
      token: token,
      expiredAt: new Date().valueOf() + 2 * 60 * 60 * 1000,
    });
  } else {
    res.status(401).end('username or password incorrect');
  }
});
在这里我们定义了一个/api/login接口,对账号和密码进行简单比较,如果账号密码都匹配则进行签发 jwt Token, 并将相关信息返回给前端
鉴权
以下是一个鉴权的node express服务的中间件的示例,其他语言的实现可以自行参考
import type { NextFunction, Request, Response } from 'express';
import jwt from 'jsonwebtoken';
import md5 from 'md5';
const adminAuth = {
  username: process.env.ADMIN_USER,
  password: process.env.ADMIN_PASS,
};
const authSecret = 'any-string';
export function auth() {
  return (req: Request, res: Response, next: NextFunction) => {
    try {
      const authorization = req.headers.authorization;
      if (!authorization) {
        res.status(401).end('not found authorization in headers');
        return;
      }
      const token = authorization.slice('Bearer '.length);
      const payload = jwt.verify(token, authSecret);
      if (typeof payload === 'string') {
        res.status(401).end('payload type error');
        return;
      }
      if (payload.platform !== 'admin') {
        res.status(401).end('Payload invalid');
        return;
      }
      next();
    } catch (err) {
      res.status(401).end(String(err));
    }
  };
}
这里使用了单用户模式,从环境变量中拿到了用户名和密码。然后将请求 Header 中携带的 token 与服务端记录的秘钥进行一次 jwt 的验证,以确保用户的 token 合法的
我们可以通过如下的使用方式来对请求进行鉴权校验
router.use(
  '/users',
  auth(),
  (req, res) => {
    // ....
  }
);