lgsso-sdk 1.0.5 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.js CHANGED
@@ -1,272 +1,15 @@
1
- import { request } from './api';
2
-
3
- // 私有配置存储(新增router实例)
4
- let config = null;
5
- let router = null;
6
-
7
- /**
8
- * 检查是否在浏览器环境
9
- */
10
- function isBrowser() {
11
- return typeof window !== 'undefined' && typeof document !== 'undefined';
12
- }
13
-
14
- /**
15
- * 解析URL中的查询参数(兼容Vue Router的query)
16
- * @param {string} name - 参数名
17
- * @returns {string|null} 参数值
18
- */
19
- function getQueryParam(name) {
20
- if (!isBrowser()) return null;
21
- // 优先从Vue Router的当前路由中获取query(更准确)
22
- if (router && router.currentRoute.value.query[name] !== undefined) {
23
- return router.currentRoute.value.query[name];
24
- }
25
- // 降级使用原生URL解析
26
- const params = new URLSearchParams(window.location.search);
27
- return params.get(name);
28
- }
29
-
30
- /**
31
- * 移除URL中的指定参数(通过Vue Router API,确保状态同步)
32
- * @param {string} name - 参数名
33
- */
34
- function removeQueryParam(name) {
35
- if (!isBrowser() || !router) {
36
- // 无Vue Router时降级使用原生方法
37
- _removeQueryParamNative(name);
38
- return;
39
- }
40
-
41
- // 使用Vue Router的replace方法修改query(核心逻辑)
42
- const currentQuery = { ...router.currentRoute.value.query };
43
- if (currentQuery[name] !== undefined) {
44
- delete currentQuery[name]; // 删除目标参数
45
- // 调用Vue Router的replace,确保路由状态同步
46
- router.replace({
47
- ...router.currentRoute.value,
48
- query: currentQuery
49
- }).catch(err => {
50
- // 忽略导航重复的错误
51
- if (!err.message.includes('NavigationDuplicated')) {
52
- console.error('移除参数失败:', err);
53
- }
54
- });
55
- }
56
- }
57
-
58
- /**
59
- * 原生移除参数的降级方法(无Vue Router时使用)
60
- */
61
- function _removeQueryParamNative(name) {
62
- const params = new URLSearchParams(window.location.search);
63
- if (params.has(name)) {
64
- params.delete(name);
65
- const newUrl = `${window.location.pathname}${params.toString() ? `?${params.toString()}` : ''}`;
66
- window.history.replaceState({}, document.title, newUrl);
67
- }
68
- }
69
-
70
- /**
71
- * 获取当前完整URL(包含所有参数)
72
- */
73
- function getCurrentUrlWithParams() {
74
- if (!isBrowser()) return '';
75
- return window.location.href;
76
- }
77
-
78
- /**
79
- * 验证并合并配置(新增router参数)
80
- */
81
- function validateAndMergeConfig(options = {}) {
82
- const defaults = {
83
- accessCodeKey: 'accessCode',
84
- tokenKey: 'scm_token',
85
- timeout: 5000,
86
- headers: {},
87
- tokenApi: '',
88
- refreshCodeApi: '',
89
- logoutApi: '',
90
- logOutUrl: '',
91
- router: null // 允许传入Vue Router实例
92
- };
93
-
94
- const merged = { ...defaults, ...options };
95
- // 验证router(如果传入则必须是Vue Router实例)
96
- if (merged.router && typeof merged.router.replace !== 'function') {
97
- throw new Error('传入的router必须是Vue Router实例');
98
- }
99
-
100
- // 基础验证(保持不变)
101
- if (typeof merged.accessCodeKey !== 'string' || merged.accessCodeKey.trim() === '') {
102
- throw new Error('accessCodeKey必须是有效的字符串');
103
- }
104
- if (typeof merged.tokenKey !== 'string' || merged.tokenKey.trim() === '') {
105
- throw new Error('tokenKey必须是有效的字符串');
106
- }
107
-
108
- return merged;
109
- }
110
-
111
- /**
112
- * 初始化SDK(新增路由钩子注册)
113
- */
114
- export async function init(options = {}) {
115
- try {
116
- config = validateAndMergeConfig(options);
117
- router = config.router; // 存储router实例
118
-
119
- const accessCode = getQueryParam(config.accessCodeKey);
120
-
121
- // 关键:在路由导航完成后再移除参数(确保重定向后执行)
122
- if (router) {
123
- // 注册一次afterEach钩子,移除参数后销毁
124
- const removeHook = router.afterEach(() => {
125
- removeQueryParam(config.accessCodeKey);
126
- removeHook(); // 执行后立即销毁钩子,避免重复调用
127
- });
128
- } else {
129
- // 无router时直接移除(可能需要延迟,应对原生重定向)
130
- setTimeout(() => removeQueryParam(config.accessCodeKey), 0);
131
- }
132
-
133
- // 后续逻辑保持不变...
134
- if (!config.tokenApi) {
135
- return { code: -100, msg: `缺少tokenApi`, success: false };
136
- }
137
-
138
- if (config.tokenApi && accessCode) {
139
- const result = await request(
140
- config.tokenApi,
141
- 'POST',
142
- { accessCode },
143
- config.headers,
144
- config.timeout
145
- );
146
-
147
- if (result.code === 0 && result.data) {
148
- localStorage.setItem(config.tokenKey, result.data);
149
- }
150
- return result;
151
- } else if (!getToken()) {
152
- window.location.href = `${config.logOutUrl}?redirect_uri=${encodeURIComponent(getCurrentUrlWithParams())}`;
153
- }
154
-
155
- return { code: 0, msg: '', success: true };
156
- } catch (error) {
157
- return { code: -100, msg: `初始化失败: ${error.message}`, success: false };
158
- }
159
- }
160
-
161
- // 其他方法(getToken、removeToken、logout、toUrl、getAccessCode)保持不变...
162
- export function getToken() {
163
- if (!config || !isBrowser()) return null;
164
- return localStorage.getItem(config.tokenKey);
165
- }
166
-
167
- export function removeToken() {
168
- if (!config || !isBrowser()) return;
169
- localStorage.removeItem(config.tokenKey);
170
- }
171
-
172
- export async function logout() {
173
- if (!config) {
174
- return { code: -101, msg: '请先调用init方法初始化', success: false };
175
- }
176
-
177
- if (!config.logoutApi) {
178
- return { code: -102, msg: '未配置logoutApi', success: false };
179
- }
180
- if (getToken()) {
181
- try {
182
- const result = await request(
183
- config.logoutApi,
184
- 'POST',
185
- {},
186
- { ...config.headers, [config.tokenKey]: getToken() },
187
- config.timeout
188
- );
189
- removeToken();
190
- if (config.logOutUrl && isBrowser()) {
191
- window.location.href = `${config.logOutUrl}?redirect_uri=${encodeURIComponent(getCurrentUrlWithParams())}`;
192
- }
193
- return result;
194
- } catch (error) {
195
- return { code: -103, msg: `退出失败: ${error.message}`, success: false };
196
- }
197
- } else {
198
- removeToken();
199
- if (config.logOutUrl && isBrowser()) {
200
- window.location.href = `${config.logOutUrl}?redirect_uri=${encodeURIComponent(getCurrentUrlWithParams())}`;
201
- }
202
- }
203
-
204
- }
205
-
206
- export async function toUrl(redirectUrl) {
207
- if (!config) {
208
- return { code: -101, msg: '请先调用init方法初始化', success: false };
209
- }
210
-
211
- if (!redirectUrl) {
212
- return { code: -104, msg: '请提供跳转地址', success: false };
213
- }
214
-
215
- if (!config.refreshCodeApi) {
216
- return { code: -105, msg: '未配置refreshCodeApi', success: false };
217
- }
218
-
219
- const token = getToken();
220
- if (!token) {
221
- if (config.logOutUrl && isBrowser()) {
222
- window.location.href = `${config.logOutUrl}?redirect_uri=${encodeURIComponent(getCurrentUrlWithParams())}`;
223
- }
224
- return { code: -106, msg: '未找到有效token', success: false };
225
- }
226
-
227
- try {
228
- const result = await request(
229
- config.refreshCodeApi,
230
- 'POST',
231
- {},
232
- { ...config.headers, [config.tokenKey]: token },
233
- config.timeout
234
- );
235
-
236
- if (result.code === 0 && result.data && isBrowser()) {
237
- const url = new URL(redirectUrl);
238
- url.searchParams.set(config.accessCodeKey, result.data);
239
- window.location.href = url.toString();
240
- }
241
-
242
- return result;
243
- } catch (error) {
244
- return { code: -107, msg: `跳转失败: ${error.message}`, success: false };
245
- }
246
- }
247
-
248
- export const getAccessCode = async () => {
249
- if (!config) {
250
- return { code: -101, msg: '请先调用init方法初始化', success: false };
251
- }
252
- const token = getToken();
253
- if (!token) {
254
- return { code: -106, msg: '未找到有效token', success: false };
255
- }
256
- return await request(
257
- config.refreshCodeApi,
258
- 'POST',
259
- {},
260
- { ...config.headers, [config.tokenKey]: token },
261
- config.timeout
262
- );
1
+ /*
2
+ * @Author: Robin LEI
3
+ * @Date: 2025-08-21 15:08:08
4
+ * @LastEditTime: 2025-08-21 15:12:09
5
+ * @FilePath: \lg-wms-admind:\业务代码\中联钢信\五服一管\lg-ssosdk\src\index.js
6
+ */
7
+ import { lgsso, createSSO } from './sso';
8
+ // 导出所有公共API
9
+ export {
10
+ lgsso,
11
+ createSSO,
263
12
  };
264
13
 
265
- export default {
266
- init,
267
- getToken,
268
- removeToken,
269
- logout,
270
- toUrl,
271
- getAccessCode
272
- };
14
+ // 默认导出默认实例
15
+ export default lgsso;
@@ -1,77 +1,89 @@
1
- /**
2
- * 统一接口请求工具
3
- * 不做业务状态码校验,直接返回所有结果
4
- */
5
- export async function request(url, method = 'GET', data = {}, headers = {}, timeout = 5000) {
6
- if (typeof window === 'undefined') {
7
- return {
8
- code: -200,
9
- msg: 'request方法只能在浏览器环境使用',
10
- success: false
11
- };
12
- }
13
-
14
- // 超时控制
15
- const controller = new AbortController();
16
- const timeoutId = setTimeout(() => controller.abort(), timeout);
17
-
18
- // 构建请求配置
19
- const options = {
20
- method: method.toUpperCase(),
21
- headers: {
22
- 'Content-Type': 'application/json',
23
- ...headers
24
- },
25
- signal: controller.signal
26
- };
27
-
28
- // 处理请求体(GET请求不携带body)
29
- if (options.method !== 'GET' && data) {
30
- options.body = JSON.stringify(data);
31
- }
32
-
33
- try {
34
- const response = await fetch(url, options);
35
- clearTimeout(timeoutId);
36
-
37
- // 处理HTTP错误状态码
38
- if (!response.ok) {
39
- return {
40
- code: response.status,
41
- msg: `HTTP请求失败: ${response.statusText}`,
42
- success: false
43
- };
44
- }
45
-
46
- // 尝试解析JSON响应
47
- try {
48
- const result = await response.json();
49
- return result;
50
- } catch (jsonError) {
51
- return {
52
- code: -201,
53
- msg: '响应数据不是有效的JSON格式',
54
- success: false
55
- };
56
- }
57
- } catch (error) {
58
- clearTimeout(timeoutId);
59
-
60
- // 超时错误处理
61
- if (error.name === 'AbortError') {
62
- return {
63
- code: -202,
64
- msg: `请求超时(${timeout}ms)`,
65
- success: false
66
- };
67
- }
68
-
69
- // 其他网络错误
70
- return {
71
- code: -203,
72
- msg: `网络请求失败: ${error.message}`,
73
- success: false
74
- };
75
- }
76
- }
77
-
1
+ import { isBrowser } from './utils';
2
+
3
+ /**
4
+ * 通用HTTP请求工具
5
+ * @param {string} url - 请求URL
6
+ * @param {Object} options - 请求选项
7
+ * @param {string} [options.method='GET'] - 请求方法
8
+ * @param {Object} [options.data] - 请求数据
9
+ * @param {Object} [options.headers] - 请求头
10
+ * @param {number} [options.timeout=5000] - 超时时间(ms)
11
+ * @returns {Promise<Object>} 请求结果
12
+ */
13
+ export async function request(url, {
14
+ method = 'GET',
15
+ data = {},
16
+ headers = {},
17
+ timeout = 5000
18
+ } = {}) {
19
+ if (!isBrowser()) {
20
+ return {
21
+ code: -200,
22
+ msg: 'request方法只能在浏览器环境使用',
23
+ success: false
24
+ };
25
+ }
26
+
27
+ // 超时控制
28
+ const controller = new AbortController();
29
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
30
+
31
+ // 构建请求配置
32
+ const options = {
33
+ method: method.toUpperCase(),
34
+ headers: {
35
+ 'Content-Type': 'application/json',
36
+ ...headers
37
+ },
38
+ signal: controller.signal
39
+ };
40
+
41
+ // 处理请求体(GET请求不携带body)
42
+ if (options.method !== 'GET' && data) {
43
+ options.body = JSON.stringify(data);
44
+ }
45
+
46
+ try {
47
+ const response = await fetch(url, options);
48
+ clearTimeout(timeoutId);
49
+
50
+ // 处理HTTP错误状态码
51
+ if (!response.ok) {
52
+ return {
53
+ code: response.status,
54
+ msg: `HTTP请求失败: ${response.statusText}`,
55
+ success: false
56
+ };
57
+ }
58
+
59
+ // 尝试解析JSON响应
60
+ try {
61
+ const result = await response.json();
62
+ return result;
63
+ } catch (jsonError) {
64
+ return {
65
+ code: -201,
66
+ msg: '响应数据不是有效的JSON格式',
67
+ success: false
68
+ };
69
+ }
70
+ } catch (error) {
71
+ clearTimeout(timeoutId);
72
+
73
+ // 超时错误处理
74
+ if (error.name === 'AbortError') {
75
+ return {
76
+ code: -202,
77
+ msg: `请求超时(${timeout}ms)`,
78
+ success: false
79
+ };
80
+ }
81
+
82
+ // 其他网络错误
83
+ return {
84
+ code: -203,
85
+ msg: `网络请求失败: ${error.message}`,
86
+ success: false
87
+ };
88
+ }
89
+ }
package/src/sso.js ADDED
@@ -0,0 +1,238 @@
1
+ import { request } from './request';
2
+ import { isBrowser, getQueryParam, removeQueryParam, getCurrentUrlWithParams, mergeConfigs } from './utils';
3
+
4
+ // 默认配置
5
+ const DEFAULT_CONFIG = {
6
+ accessCodeKey: 'accessCode',
7
+ tokenKey: 'scm_token',
8
+ timeout: 5000,
9
+ headers: {},
10
+ tokenApi: '',
11
+ refreshCodeApi: '',
12
+ logoutApi: '',
13
+ logOutUrl: '',
14
+ storage: localStorage, // 可替换为sessionStorage
15
+ };
16
+
17
+ let config = null;
18
+
19
+ /**
20
+ * 验证配置的有效性
21
+ * @param {Object} options - 配置选项
22
+ * @throws {Error} 当配置无效时抛出错误
23
+ */
24
+ function validateConfig(options) {
25
+ if (typeof options.accessCodeKey !== 'string' || options.accessCodeKey.trim() === '') {
26
+ throw new Error('accessCodeKey必须是有效的字符串');
27
+ }
28
+ if (typeof options.tokenKey !== 'string' || options.tokenKey.trim() === '') {
29
+ throw new Error('tokenKey必须是有效的字符串');
30
+ }
31
+ if (options.storage && (typeof options.storage.setItem !== 'function' || typeof options.storage.getItem !== 'function')) {
32
+ throw new Error('storage必须实现setItem和getItem方法');
33
+ }
34
+ }
35
+
36
+ /**
37
+ * 创建SSO实例
38
+ * @returns {Object} SSO实例
39
+ */
40
+ export function createSSO() {
41
+ return {
42
+ /**
43
+ * 初始化SDK
44
+ * @param {Object} options - 配置参数
45
+ * @returns {Promise<Object>} 接口返回结果
46
+ */
47
+ async init(options = {}) {
48
+ try {
49
+ // 合并并验证配置
50
+ config = mergeConfigs(DEFAULT_CONFIG, options);
51
+ validateConfig(config);
52
+
53
+ const accessCode = getQueryParam(config.accessCodeKey);
54
+
55
+ // 验证必要的API配置
56
+ if (!config.tokenApi) {
57
+ return { code: -100, msg: '缺少tokenApi配置', success: false };
58
+ }
59
+
60
+ // 如果存在accessCode,尝试获取token
61
+ if (accessCode) {
62
+ const result = await request(
63
+ config.tokenApi,
64
+ {
65
+ method: 'POST',
66
+ data: { accessCode },
67
+ headers: config.headers,
68
+ timeout: config.timeout
69
+ }
70
+ );
71
+
72
+ if (result.code === 0 && result.data) {
73
+ config.storage.setItem(config.tokenKey, result.data);
74
+ removeQueryParam(config.accessCodeKey);
75
+ }
76
+ return result;
77
+ }
78
+ // 如果没有token,跳转到登录页
79
+ else if (!this.getToken()) {
80
+ if (isBrowser() && config.logOutUrl) {
81
+ window.location.href = `${config.logOutUrl}?redirect_uri=${encodeURIComponent(getCurrentUrlWithParams())}`;
82
+ }
83
+ }
84
+
85
+ return { code: 0, msg: '初始化成功', success: true };
86
+ } catch (error) {
87
+ return { code: -100, msg: `初始化失败: ${error.message}`, success: false };
88
+ }
89
+ },
90
+
91
+ /**
92
+ * 获取存储的token
93
+ * @returns {string|null} token值或null
94
+ */
95
+ getToken() {
96
+ if (!config || !isBrowser()) return null;
97
+ return config.storage.getItem(config.tokenKey);
98
+ },
99
+
100
+ /**
101
+ * 删除存储的token
102
+ */
103
+ removeToken() {
104
+ if (!config || !isBrowser()) return;
105
+ config.storage.removeItem(config.tokenKey);
106
+ },
107
+
108
+ /**
109
+ * 退出登录
110
+ * @returns {Promise<Object>} 接口返回结果
111
+ */
112
+ async logout() {
113
+ if (!config) {
114
+ return { code: -101, msg: '请先调用init方法初始化', success: false };
115
+ }
116
+
117
+ if (!config.logoutApi) {
118
+ return { code: -102, msg: '未配置logoutApi', success: false };
119
+ }
120
+
121
+ const token = this.getToken();
122
+ if (token) {
123
+ try {
124
+ const result = await request(
125
+ config.logoutApi,
126
+ {
127
+ method: 'POST',
128
+ headers: { ...config.headers, [config.tokenKey]: token },
129
+ timeout: config.timeout
130
+ }
131
+ );
132
+ this.removeToken();
133
+
134
+ if (isBrowser() && config.logOutUrl) {
135
+ window.location.href = `${config.logOutUrl}?redirect_uri=${encodeURIComponent(getCurrentUrlWithParams())}`;
136
+ }
137
+ return result;
138
+ } catch (error) {
139
+ return { code: -103, msg: `退出失败: ${error.message}`, success: false };
140
+ }
141
+ } else {
142
+ this.removeToken();
143
+ if (isBrowser() && config.logOutUrl) {
144
+ window.location.href = `${config.logOutUrl}?redirect_uri=${encodeURIComponent(getCurrentUrlWithParams())}`;
145
+ }
146
+ return { code: 0, msg: '已成功清除token', success: true };
147
+ }
148
+ },
149
+
150
+ /**
151
+ * 用token换取新accessCode并跳转到指定URL
152
+ * @param {string} redirectUrl - 目标跳转地址
153
+ * @returns {Promise<Object>} 接口返回结果
154
+ */
155
+ async toUrl(redirectUrl) {
156
+ if (!config) {
157
+ return { code: -101, msg: '请先调用init方法初始化', success: false };
158
+ }
159
+
160
+ if (!redirectUrl) {
161
+ return { code: -104, msg: '请提供跳转地址', success: false };
162
+ }
163
+
164
+ if (!config.refreshCodeApi) {
165
+ return { code: -105, msg: '未配置refreshCodeApi', success: false };
166
+ }
167
+
168
+ const token = this.getToken();
169
+ if (!token) {
170
+ if (isBrowser() && config.logOutUrl) {
171
+ // 修改:使用参数传入的redirectUrl作为重定向地址
172
+ window.location.href = `${config.logOutUrl}?redirect_uri=${encodeURIComponent(redirectUrl)}`;
173
+ }
174
+ return { code: -106, msg: '未找到有效token', success: false };
175
+ }
176
+
177
+ try {
178
+ const result = await request(
179
+ config.refreshCodeApi,
180
+ {
181
+ method: 'POST',
182
+ headers: { ...config.headers, [config.tokenKey]: token },
183
+ timeout: config.timeout
184
+ }
185
+ );
186
+
187
+ if (result.code === 0 && result.data && isBrowser()) {
188
+ const url = new URL(redirectUrl);
189
+ url.searchParams.set(config.accessCodeKey, result.data);
190
+ window.location.href = url.toString();
191
+ }
192
+
193
+ return result;
194
+ } catch (error) {
195
+ return { code: -107, msg: `跳转失败: ${error.message}`, success: false };
196
+ }
197
+ },
198
+
199
+ /**
200
+ * 获取accessCode(通过token换取)
201
+ * @returns {Promise<Object>} 接口返回结果(data为accessCode)
202
+ */
203
+ async getAccessCode() {
204
+ if (!config) {
205
+ return { code: -101, msg: '请先调用init方法初始化', success: false };
206
+ }
207
+
208
+ const token = this.getToken();
209
+ if (!token) {
210
+ return { code: -106, msg: '未找到有效token', success: false };
211
+ }
212
+
213
+ if (!config.refreshCodeApi) {
214
+ return { code: -105, msg: '未配置refreshCodeApi', success: false };
215
+ }
216
+
217
+ return request(
218
+ config.refreshCodeApi,
219
+ {
220
+ method: 'POST',
221
+ headers: { ...config.headers, [config.tokenKey]: token },
222
+ timeout: config.timeout
223
+ }
224
+ );
225
+ },
226
+
227
+ /**
228
+ * 获取当前配置
229
+ * @returns {Object|null} 当前配置
230
+ */
231
+ getConfig() {
232
+ return { ...config }; // 返回配置的副本,防止外部修改
233
+ }
234
+ };
235
+ }
236
+
237
+ // 创建默认实例
238
+ export const lgsso = createSSO();