This is an automated email from the ASF dual-hosted git repository.
lizhimins pushed a commit to branch rocketmq-studio
in repository https://gitbox.apache.org/repos/asf/rocketmq-dashboard.git
The following commit(s) were added to refs/heads/rocketmq-studio by this push:
new 23715480 fix: preserve sessions for public authentication failures
(#717)
23715480 is described below
commit 237154807955189a821fdb922a38ea6a240eff8f
Author: Rui <[email protected]>
AuthorDate: Mon Aug 3 11:12:30 2026 +0800
fix: preserve sessions for public authentication failures (#717)
---
web/src/api/client.test.ts | 29 +++++++++++++++++++++++++++++
web/src/api/client.ts | 13 ++++++++++++-
2 files changed, 41 insertions(+), 1 deletion(-)
diff --git a/web/src/api/client.test.ts b/web/src/api/client.test.ts
index 4be95ecb..27c31326 100644
--- a/web/src/api/client.test.ts
+++ b/web/src/api/client.test.ts
@@ -119,4 +119,33 @@ describe('API client response contract', () => {
await client.get('/clusters');
});
+
+ it.each(['/auth/login', '/auth/status'])(
+ 'does not clear the current session when public auth request %s returns
401',
+ async (path) => {
+ localStorage.setItem('token', 'current-token');
+ localStorage.setItem('rocketmq-studio-user', 'admin');
+ localStorage.setItem('rocketmq-studio-user-admin', 'true');
+ mock.onAny(path).reply(401, { code: 401, message: 'Unauthorized', data:
null });
+
+ await expect(client.get(path)).rejects.toMatchObject({ response: {
status: 401 } });
+
+ expect(localStorage.getItem('token')).toBe('current-token');
+ expect(localStorage.getItem('rocketmq-studio-user')).toBe('admin');
+ expect(localStorage.getItem('rocketmq-studio-user-admin')).toBe('true');
+ },
+ );
+
+ it('clears the current session when a protected API request returns 401',
async () => {
+ localStorage.setItem('token', 'expired-token');
+ localStorage.setItem('rocketmq-studio-user', 'admin');
+ localStorage.setItem('rocketmq-studio-user-admin', 'true');
+ mock.onGet('/clusters').reply(401, { code: 401, message: 'Unauthorized',
data: null });
+
+ await expect(client.get('/clusters')).rejects.toMatchObject({ response: {
status: 401 } });
+
+ expect(localStorage.getItem('token')).toBeNull();
+ expect(localStorage.getItem('rocketmq-studio-user')).toBeNull();
+ expect(localStorage.getItem('rocketmq-studio-user-admin')).toBeNull();
+ });
});
diff --git a/web/src/api/client.ts b/web/src/api/client.ts
index 41a97f1a..5b002a0f 100644
--- a/web/src/api/client.ts
+++ b/web/src/api/client.ts
@@ -21,6 +21,7 @@ import { clearAuthSession, TOKEN_STORAGE_KEY } from
'../stores/authStorage';
import { API_BASE_URL } from '../config';
const SUCCESS_BUSINESS_CODES = new Set([0, 200]);
+const PUBLIC_AUTH_PATHS = new Set(['/auth/login', '/auth/status']);
interface BusinessResponse {
code?: unknown;
@@ -44,6 +45,16 @@ function getBusinessError(data: unknown): string | null {
return typeof data.message === 'string' && data.message.trim() ?
data.message : '请求失败';
}
+function isPublicAuthRequest(url?: string): boolean {
+ if (!url) return false;
+ const requestPath = new URL(url, window.location.origin).pathname;
+ const apiBasePath = new URL(API_BASE_URL, window.location.origin).pathname;
+ const relativePath = requestPath.startsWith(`${apiBasePath}/`)
+ ? requestPath.slice(apiBasePath.length)
+ : requestPath;
+ return PUBLIC_AUTH_PATHS.has(relativePath);
+}
+
const client = axios.create({
baseURL: API_BASE_URL,
timeout: 30000,
@@ -72,7 +83,7 @@ client.interceptors.response.use(
return response;
},
(error) => {
- if (error.response?.status === 401) {
+ if (error.response?.status === 401 &&
!isPublicAuthRequest(error.config?.url)) {
clearAuthSession();
window.location.href = '/';
}