fix: 合并 exchange+login 为一步,直接从 jumpToken 响应提取用户信息签发JWT
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -70,32 +70,19 @@ export default function AuthProvider({ children }: { children: ReactNode }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 3. 通过后端代理交换 jumpToken
|
// 3. 一步完成:jumpToken → 用户信息 + JWT
|
||||||
const exchangeRes = await fetch(`${AUTH_API}/exchange?jumpToken=${encodeURIComponent(jumpToken)}`);
|
const res = await fetch(`${AUTH_API}/exchange?jumpToken=${encodeURIComponent(jumpToken)}`);
|
||||||
const exchangeData = await exchangeRes.json();
|
const data = await res.json();
|
||||||
|
|
||||||
if (!exchangeRes.ok || !exchangeData.token) {
|
if (!res.ok || !data.token) {
|
||||||
setState({ isLoading: false, isAuthenticated: false, user: null, error: '跳转令牌无效或已过期' });
|
setState({ isLoading: false, isAuthenticated: false, user: null, error: data.message || '跳转令牌无效或已过期' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. 用 sessionToken 登录获取 JWT
|
// 4. 存储 JWT
|
||||||
const loginRes = await fetch(`${AUTH_API}/login`, {
|
tokenRef.current = data.token;
|
||||||
method: 'POST',
|
sessionStorage.setItem('bi_jwt', data.token);
|
||||||
headers: { 'Content-Type': 'application/json' },
|
sessionStorage.setItem('bi_user', JSON.stringify(data.user));
|
||||||
body: JSON.stringify({ token: exchangeData.token }),
|
|
||||||
});
|
|
||||||
const loginData = await loginRes.json();
|
|
||||||
|
|
||||||
if (!loginRes.ok || !loginData.token) {
|
|
||||||
setState({ isLoading: false, isAuthenticated: false, user: null, error: '获取用户信息失败' });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. 存储 JWT
|
|
||||||
tokenRef.current = loginData.token;
|
|
||||||
sessionStorage.setItem('bi_jwt', loginData.token);
|
|
||||||
sessionStorage.setItem('bi_user', JSON.stringify(loginData.user));
|
|
||||||
|
|
||||||
// 6. 清除 URL 中的 jumpToken
|
// 6. 清除 URL 中的 jumpToken
|
||||||
params.delete('jumpToken');
|
params.delete('jumpToken');
|
||||||
@@ -107,7 +94,7 @@ export default function AuthProvider({ children }: { children: ReactNode }) {
|
|||||||
setState({
|
setState({
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
isAuthenticated: true,
|
isAuthenticated: true,
|
||||||
user: loginData.user,
|
user: data.user,
|
||||||
error: null,
|
error: null,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ const app = new Hono();
|
|||||||
const EXTERNAL_API_BASE = process.env.EXTERNAL_API_BASE || 'https://beta.lnh2e.com';
|
const EXTERNAL_API_BASE = process.env.EXTERNAL_API_BASE || 'https://beta.lnh2e.com';
|
||||||
const JWT_SECRET = process.env.JWT_SECRET || 'ln-bi-default-secret';
|
const JWT_SECRET = process.env.JWT_SECRET || 'ln-bi-default-secret';
|
||||||
|
|
||||||
/** GET /api/auth/exchange?jumpToken=xxx — 代理 jumpToken 换取 sessionToken */
|
/** GET /api/auth/exchange?jumpToken=xxx — 一步完成:换取用户信息 + 签发 JWT */
|
||||||
app.get('/exchange', async (c) => {
|
app.get('/exchange', async (c) => {
|
||||||
const jumpToken = c.req.query('jumpToken');
|
const jumpToken = c.req.query('jumpToken');
|
||||||
if (!jumpToken) return c.json({ error: 'Missing jumpToken' }, 400);
|
if (!jumpToken) return c.json({ error: 'Missing jumpToken' }, 400);
|
||||||
@@ -18,47 +18,27 @@ app.get('/exchange', async (c) => {
|
|||||||
const res = await fetch(
|
const res = await fetch(
|
||||||
`${EXTERNAL_API_BASE}/api/lingniu-manager-v1/v1/auth/issueTokenByJump?jumpToken=${encodeURIComponent(jumpToken)}`
|
`${EXTERNAL_API_BASE}/api/lingniu-manager-v1/v1/auth/issueTokenByJump?jumpToken=${encodeURIComponent(jumpToken)}`
|
||||||
);
|
);
|
||||||
const data = await res.json() as { code: number; data: string | null; message: string };
|
|
||||||
|
|
||||||
if (data.code !== 0 || !data.data) {
|
|
||||||
return c.json({ error: 'Token exchange failed', message: data.message }, 401);
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.json({ token: data.data });
|
|
||||||
} catch (e: unknown) {
|
|
||||||
console.error('jumpToken exchange error:', e);
|
|
||||||
return c.json({ error: 'Token exchange failed' }, 500);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/** POST /api/auth/login — 用外部 sessionToken 获取用户信息,签发 JWT */
|
|
||||||
app.post('/login', async (c) => {
|
|
||||||
const body = await c.req.json<{ token: string }>().catch(() => null);
|
|
||||||
if (!body?.token) return c.json({ error: 'Missing token' }, 400);
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 调用外部 API 获取用户信息
|
|
||||||
const res = await fetch(
|
|
||||||
`${EXTERNAL_API_BASE}/api/lingniu-manager-v1/v1/auth/getLoginUserInfo`,
|
|
||||||
{ headers: { g7litegtoken: body.token } }
|
|
||||||
);
|
|
||||||
const data = await res.json() as {
|
const data = await res.json() as {
|
||||||
code: number;
|
code: number;
|
||||||
data: {
|
data: {
|
||||||
|
userInfo: {
|
||||||
id: string;
|
id: string;
|
||||||
userName: string;
|
userName: string;
|
||||||
loginName: string;
|
loginName: string;
|
||||||
depCode: string;
|
depCode: string;
|
||||||
orgId: string;
|
orgId: string;
|
||||||
roles: { roleName: string; id: string }[];
|
roles: { roleName: string; id: string }[];
|
||||||
|
};
|
||||||
|
token: string;
|
||||||
} | null;
|
} | null;
|
||||||
|
message: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (data.code !== 0 || !data.data) {
|
if (data.code !== 0 || !data.data?.userInfo) {
|
||||||
return c.json({ error: 'Failed to get user info' }, 401);
|
return c.json({ error: 'Token exchange failed', message: data.message }, 401);
|
||||||
}
|
}
|
||||||
|
|
||||||
const userInfo = data.data;
|
const userInfo = data.data.userInfo;
|
||||||
const roleNames = userInfo.roles.map(r => r.roleName);
|
const roleNames = userInfo.roles.map(r => r.roleName);
|
||||||
|
|
||||||
// 确定权限级别
|
// 确定权限级别
|
||||||
@@ -89,13 +69,12 @@ app.post('/login', async (c) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const token = jwt.sign(payload, JWT_SECRET, { expiresIn: '8h' });
|
const token = jwt.sign(payload, JWT_SECRET, { expiresIn: '8h' });
|
||||||
|
|
||||||
const authUser: AuthUser = { ...payload };
|
const authUser: AuthUser = { ...payload };
|
||||||
|
|
||||||
return c.json({ token, user: authUser });
|
return c.json({ token, user: authUser });
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
console.error('login error:', e);
|
console.error('auth exchange error:', e);
|
||||||
return c.json({ error: 'Login failed' }, 500);
|
return c.json({ error: 'Authentication failed' }, 500);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user