lgsso-sdk 1.0.4 → 1.0.6

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,247 +1,15 @@
1
- import { request } from './api';
2
-
3
- // 私有配置存储
4
- let config = null;
5
-
6
- /**
7
- * 检查是否在浏览器环境
8
- */
9
- function isBrowser() {
10
- return typeof window !== 'undefined' && typeof document !== 'undefined';
11
- }
12
-
13
- /**
14
- * 解析URL中的查询参数
15
- * @param {string} name - 参数名
16
- * @returns {string|null} 参数值
17
- */
18
- function getQueryParam(name) {
19
- if (!isBrowser()) return null;
20
- const params = new URLSearchParams(window.location.search);
21
- return params.get(name);
22
- }
23
-
24
- /**
25
- * 移除URL中的指定参数(无痕修改)
26
- * @param {string} name - 参数名
27
- */
28
- function removeQueryParam(name) {
29
- if (!isBrowser()) return;
30
-
31
- const params = new URLSearchParams(window.location.search);
32
- if (params.has(name)) {
33
- params.delete(name);
34
- const newUrl = `${window.location.pathname}${params.toString() ? `?${params.toString()}` : ''}`;
35
- window.history.replaceState({}, document.title, newUrl);
36
- }
37
- }
38
-
39
- /**
40
- * 获取当前完整URL(包含所有参数)
41
- * @returns {string} 当前完整URL(如 local.watest.zlgx.com:3000?a=1)
42
- */
43
- function getCurrentUrlWithParams() {
44
- if (!isBrowser()) return '';
45
- return window.location.href; // 直接返回完整URL(含参数)
46
- }
47
-
48
- /**
49
- * 验证并合并配置
50
- * @param {Object} options - 用户配置
51
- * @returns {Object} 合并后的配置
52
- */
53
- function validateAndMergeConfig(options = {}) {
54
- const defaults = {
55
- accessCodeKey: 'accessCode',
56
- tokenKey: 'scm_token',
57
- timeout: 5000,
58
- headers: {},
59
- tokenApi: '',
60
- refreshCodeApi: '',
61
- logoutApi: '',
62
- logOutUrl: ''
63
- };
64
-
65
- const merged = { ...defaults, ...options };
66
-
67
- // 基础验证
68
- if (typeof merged.accessCodeKey !== 'string' || merged.accessCodeKey.trim() === '') {
69
- throw new Error('accessCodeKey必须是有效的字符串');
70
- }
71
- if (typeof merged.tokenKey !== 'string' || merged.tokenKey.trim() === '') {
72
- throw new Error('tokenKey必须是有效的字符串');
73
- }
74
-
75
- return merged;
76
- }
77
-
78
- /**
79
- * 初始化SDK
80
- * @param {Object} options - 配置参数
81
- * @returns {Promise<Object>} 接口返回结果
82
- */
83
- export async function init(options = {}) {
84
- try {
85
- config = validateAndMergeConfig(options);
86
-
87
- const accessCode = getQueryParam(config.accessCodeKey);
88
- removeQueryParam(config.accessCodeKey);
89
- if (!config.tokenApi) {
90
- return { code: -100, msg: `缺少tokenApi`, success: false };
91
- }
92
-
93
- if (config.tokenApi && accessCode) {
94
- const result = await request(
95
- config.tokenApi,
96
- 'POST',
97
- { accessCode },
98
- config.headers,
99
- config.timeout
100
- );
101
-
102
- if (result.code === 0 && result.data) {
103
- localStorage.setItem(config.tokenKey, result.data);
104
- }
105
- setTimeout(() => removeQueryParam(config.accessCodeKey), 300);
106
- return result;
107
- } else if (!getToken()) {
108
- // 跳转时携带完整URL参数(如 local.watest.zlgx.com:3000?a=1)
109
- window.location.href = `${config.logOutUrl}?redirect_uri=${encodeURIComponent(getCurrentUrlWithParams())}`;
110
- }
111
-
112
- return { code: 0, msg: '', success: true };
113
- } catch (error) {
114
- return { code: -100, msg: `初始化失败: ${error.message}`, success: false };
115
- }
116
- }
117
-
118
- /**
119
- * 获取localStorage中的token
120
- * @returns {string|null} token值
121
- */
122
- export function getToken() {
123
- if (!config || !isBrowser()) return null;
124
- return localStorage.getItem(config.tokenKey);
125
- }
126
-
127
- /**
128
- * 删除localStorage中的token
129
- */
130
- export function removeToken() {
131
- if (!config || !isBrowser()) return;
132
- localStorage.removeItem(config.tokenKey);
133
- }
134
-
135
- /**
136
- * 退出登录
137
- * @returns {Promise<Object>} 接口返回结果
138
- */
139
- export async function logout() {
140
- if (!config) {
141
- return { code: -101, msg: '请先调用init方法初始化', success: false };
142
- }
143
-
144
- if (!config.logoutApi) {
145
- return { code: -102, msg: '未配置logoutApi', success: false };
146
- }
147
-
148
- try {
149
- const result = await request(
150
- config.logoutApi,
151
- 'POST',
152
- {},
153
- { ...config.headers, [config.tokenKey]: getToken() },
154
- config.timeout
155
- );
156
-
157
- removeToken();
158
-
159
- if (config.logOutUrl && isBrowser()) {
160
- // 携带完整URL参数跳转
161
- window.location.href = `${config.logOutUrl}?redirect_uri=${encodeURIComponent(getCurrentUrlWithParams())}`;
162
- }
163
-
164
- return result;
165
- } catch (error) {
166
- return { code: -103, msg: `退出失败: ${error.message}`, success: false };
167
- }
168
- }
169
-
170
- /**
171
- * 用token换取新accessCode并跳转
172
- * @param {string} redirectUrl - 目标跳转地址
173
- * @returns {Promise<Object>} 接口返回结果
174
- */
175
- export async function toUrl(redirectUrl) {
176
- if (!config) {
177
- return { code: -101, msg: '请先调用init方法初始化', success: false };
178
- }
179
-
180
- if (!redirectUrl) {
181
- return { code: -104, msg: '请提供跳转地址', success: false };
182
- }
183
-
184
- if (!config.refreshCodeApi) {
185
- return { code: -105, msg: '未配置refreshCodeApi', success: false };
186
- }
187
-
188
- const token = getToken();
189
- if (!token) {
190
- if (config.logOutUrl && isBrowser()) {
191
- // 携带完整URL参数跳转
192
- window.location.href = `${config.logOutUrl}?redirect_uri=${encodeURIComponent(getCurrentUrlWithParams())}`;
193
- }
194
- return { code: -106, msg: '未找到有效token', success: false };
195
- }
196
-
197
- try {
198
- const result = await request(
199
- config.refreshCodeApi,
200
- 'POST',
201
- {},
202
- { ...config.headers, [config.tokenKey]: token },
203
- config.timeout
204
- );
205
-
206
- if (result.code === 0 && result.data && isBrowser()) {
207
- const url = new URL(redirectUrl);
208
- url.searchParams.set(config.accessCodeKey, result.data);
209
- window.location.href = url.toString();
210
- }
211
-
212
- return result;
213
- } catch (error) {
214
- return { code: -107, msg: `跳转失败: ${error.message}`, success: false };
215
- }
216
- }
217
-
218
- /**
219
- * 获取accessCode(修复token未定义问题)
220
- * @returns {Promise<Object>} 接口返回结果
221
- */
222
- export const getAccessCode = async () => {
223
- if (!config) {
224
- return { code: -101, msg: '请先调用init方法初始化', success: false };
225
- }
226
- const token = getToken(); // 从localStorage获取token
227
- if (!token) {
228
- return { code: -106, msg: '未找到有效token', success: false };
229
- }
230
- return await request(
231
- config.refreshCodeApi,
232
- 'POST',
233
- {},
234
- { ...config.headers, [config.tokenKey]: token }, // 使用获取到的token
235
- config.timeout
236
- );
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,
237
12
  };
238
13
 
239
- // 默认导出(修复方法名拼写错误:getCode → getAccessCode)
240
- export default {
241
- init,
242
- getToken,
243
- removeToken,
244
- logout,
245
- toUrl,
246
- getAccessCode
247
- };
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();