lgsso-sdk 1.0.0
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/README.md +99 -0
- package/dist/lgsso-sdk.js +2 -0
- package/dist/lgsso-sdk.js.LICENSE.txt +1 -0
- package/package.json +31 -0
- package/src/api.js +77 -0
- package/src/index.d.ts +76 -0
- package/src/index.js +264 -0
- package/webpack.config.js +32 -0
package/README.md
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# lgsso-sdk
|
|
2
|
+
|
|
3
|
+
LG SSO登录工具库,用于处理单点登录流程、token管理和系统间跳转。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
npm install lgsso-sdk --save
|
|
7
|
+
# 或
|
|
8
|
+
yarn add lgsso-sdk
|
|
9
|
+
## 引入
|
|
10
|
+
// ES模块引入
|
|
11
|
+
import lgsso from 'lgsso-sdk';
|
|
12
|
+
|
|
13
|
+
// CommonJS引入
|
|
14
|
+
const lgsso = require('lgsso-sdk').default;
|
|
15
|
+
## 初始化
|
|
16
|
+
// 在应用入口处初始化
|
|
17
|
+
async function setupApp() {
|
|
18
|
+
const result = await lgsso.init({
|
|
19
|
+
logOutUrl: "http://lgsso2-watest.zlgx.com/",
|
|
20
|
+
tokenApi: "/v2/scm/sso/exchangeToken",
|
|
21
|
+
refreshCodeApi: "/v2/scm/sso/exchangeAccessCode",
|
|
22
|
+
logoutApi: "/v2/scm/sso/logout",
|
|
23
|
+
accessCodeKey: "accessCode",
|
|
24
|
+
tokenKey: "scm_token",
|
|
25
|
+
timeout: 8000, // 可选,默认5000ms
|
|
26
|
+
headers: { // 可选,自定义请求头
|
|
27
|
+
'X-App-ID': 'your-application-id'
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// 处理初始化结果
|
|
32
|
+
if (result.code === 0) {
|
|
33
|
+
console.log('初始化成功', result.data);
|
|
34
|
+
// 初始化完成后执行后续逻辑
|
|
35
|
+
startApplication();
|
|
36
|
+
} else {
|
|
37
|
+
console.error('初始化失败', result.msg);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// 启动应用
|
|
42
|
+
setupApp();
|
|
43
|
+
## 核心方法
|
|
44
|
+
|
|
45
|
+
### 1. 获取当前token
|
|
46
|
+
const token = lgsso.getToken();
|
|
47
|
+
if (token) {
|
|
48
|
+
console.log('当前token:', token);
|
|
49
|
+
} else {
|
|
50
|
+
console.log('未找到token');
|
|
51
|
+
}
|
|
52
|
+
### 2. 退出登录
|
|
53
|
+
async function handleLogout() {
|
|
54
|
+
const result = await lgsso.logout();
|
|
55
|
+
if (result.code !== 0) {
|
|
56
|
+
console.error('退出失败:', result.msg);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
### 3. 跳转到其他系统
|
|
60
|
+
async function goToOtherSystem() {
|
|
61
|
+
const result = await lgsso.toUrl('https://other-system.com');
|
|
62
|
+
if (result.code !== 0) {
|
|
63
|
+
console.error('跳转失败:', result.msg);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
### 4. 手动删除token
|
|
67
|
+
// 仅删除本地token,不调用退出接口
|
|
68
|
+
lgsso.removeToken();
|
|
69
|
+
## 错误码说明
|
|
70
|
+
|
|
71
|
+
| 错误码 | 说明 |
|
|
72
|
+
|--------|------|
|
|
73
|
+
| 0 | 成功 |
|
|
74
|
+
| -100 | 初始化失败 |
|
|
75
|
+
| -101 | 未初始化 |
|
|
76
|
+
| -102 | 未配置logoutApi |
|
|
77
|
+
| -103 | 退出失败 |
|
|
78
|
+
| -104 | 未提供跳转地址 |
|
|
79
|
+
| -105 | 未配置refreshCodeApi |
|
|
80
|
+
| -106 | 未找到有效token |
|
|
81
|
+
| -107 | 跳转失败 |
|
|
82
|
+
| -200 | 非浏览器环境 |
|
|
83
|
+
| -201 | 响应数据格式错误 |
|
|
84
|
+
| -202 | 请求超时 |
|
|
85
|
+
| -203 | 网络请求失败 |
|
|
86
|
+
|
|
87
|
+
## 构建与发布
|
|
88
|
+
# 安装依赖
|
|
89
|
+
npm install
|
|
90
|
+
|
|
91
|
+
# 开发模式(实时监听)
|
|
92
|
+
npm run dev
|
|
93
|
+
|
|
94
|
+
# 构建生产版本
|
|
95
|
+
npm run build
|
|
96
|
+
|
|
97
|
+
# 发布到npm(需先登录)
|
|
98
|
+
npm publish
|
|
99
|
+
构建后会在`dist`目录生成`lgsso-sdk.js`文件,可直接引入使用。
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/*! For license information please see lgsso-sdk.js.LICENSE.txt */
|
|
2
|
+
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("lgssoSdk",[],t):"object"==typeof exports?exports.lgssoSdk=t():e.lgssoSdk=t()}(this,()=>(()=>{"use strict";var e={d:(t,r)=>{for(var n in r)e.o(r,n)&&!e.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:r[n]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};function r(e){return r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},r(e)}function n(){var e,t,r="function"==typeof Symbol?Symbol:{},c=r.iterator||"@@iterator",i=r.toStringTag||"@@toStringTag";function u(r,n,c,i){var u=n&&n.prototype instanceof s?n:s,f=Object.create(u.prototype);return o(f,"_invoke",function(r,n,o){var c,i,u,s=0,f=o||[],l=!1,p={p:0,n:0,v:e,a:y,f:y.bind(e,4),d:function(t,r){return c=t,i=0,u=e,p.n=r,a}};function y(r,n){for(i=r,u=n,t=0;!l&&s&&!o&&t<f.length;t++){var o,c=f[t],y=p.p,d=c[2];r>3?(o=d===n)&&(u=c[(i=c[4])?5:(i=3,3)],c[4]=c[5]=e):c[0]<=y&&((o=r<2&&y<c[1])?(i=0,p.v=n,p.n=c[1]):y<d&&(o=r<3||c[0]>n||n>d)&&(c[4]=r,c[5]=n,p.n=d,i=0))}if(o||r>1)return a;throw l=!0,n}return function(o,f,d){if(s>1)throw TypeError("Generator is already running");for(l&&1===f&&y(f,d),i=f,u=d;(t=i<2?e:u)||!l;){c||(i?i<3?(i>1&&(p.n=-1),y(i,u)):p.n=u:p.v=u);try{if(s=2,c){if(i||(o="next"),t=c[o]){if(!(t=t.call(c,u)))throw TypeError("iterator result is not an object");if(!t.done)return t;u=t.value,i<2&&(i=0)}else 1===i&&(t=c.return)&&t.call(c),i<2&&(u=TypeError("The iterator does not provide a '"+o+"' method"),i=1);c=e}else if((t=(l=p.n<0)?u:r.call(n,p))!==a)break}catch(t){c=e,i=1,u=t}finally{s=1}}return{value:t,done:l}}}(r,c,i),!0),f}var a={};function s(){}function f(){}function l(){}t=Object.getPrototypeOf;var p=[][c]?t(t([][c]())):(o(t={},c,function(){return this}),t),y=l.prototype=s.prototype=Object.create(p);function d(e){return Object.setPrototypeOf?Object.setPrototypeOf(e,l):(e.__proto__=l,o(e,i,"GeneratorFunction")),e.prototype=Object.create(y),e}return f.prototype=l,o(y,"constructor",l),o(l,"constructor",f),f.displayName="GeneratorFunction",o(l,i,"GeneratorFunction"),o(y),o(y,i,"Generator"),o(y,c,function(){return this}),o(y,"toString",function(){return"[object Generator]"}),(n=function(){return{w:u,m:d}})()}function o(e,t,r,n){var c=Object.defineProperty;try{c({},"",{})}catch(e){c=0}o=function(e,t,r,n){function i(t,r){o(e,t,function(e){return this._invoke(t,r,e)})}t?c?c(e,t,{value:r,enumerable:!n,configurable:!n,writable:!n}):e[t]=r:(i("next",0),i("throw",1),i("return",2))},o(e,t,r,n)}function c(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,n)}return r}function i(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?c(Object(r),!0).forEach(function(t){u(e,t,r[t])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):c(Object(r)).forEach(function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))})}return e}function u(e,t,n){return(t=function(e){var t=function(e){if("object"!=r(e)||!e)return e;var t=e[Symbol.toPrimitive];if(void 0!==t){var n=t.call(e,"string");if("object"!=r(n))return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(e)}(e);return"symbol"==r(t)?t:t+""}(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t,r,n,o,c,i){try{var u=e[c](i),a=u.value}catch(e){return void r(e)}u.done?t(a):Promise.resolve(a).then(n,o)}function s(e){return f.apply(this,arguments)}function f(){var e;return e=n().m(function e(t){var r,o,c,u,a,s,f,l,p,y,d=arguments;return n().w(function(e){for(;;)switch(e.p=e.n){case 0:if(r=d.length>1&&void 0!==d[1]?d[1]:"GET",o=d.length>2&&void 0!==d[2]?d[2]:{},c=d.length>3&&void 0!==d[3]?d[3]:{},u=d.length>4&&void 0!==d[4]?d[4]:5e3,"undefined"!=typeof window){e.n=1;break}return e.a(2,{code:-200,msg:"request方法只能在浏览器环境使用",success:!1});case 1:return a=new AbortController,s=setTimeout(function(){return a.abort()},u),"GET"!==(f={method:r.toUpperCase(),headers:i({"Content-Type":"application/json"},c),signal:a.signal}).method&&o&&(f.body=JSON.stringify(o)),e.p=2,e.n=3,fetch(t,f);case 3:if(l=e.v,clearTimeout(s),l.ok){e.n=4;break}return e.a(2,{code:l.status,msg:"HTTP请求失败: ".concat(l.statusText),success:!1});case 4:return e.p=4,e.n=5,l.json();case 5:return p=e.v,e.a(2,p);case 6:return e.p=6,e.v,e.a(2,{code:-201,msg:"响应数据不是有效的JSON格式",success:!1});case 7:if(e.p=7,y=e.v,clearTimeout(s),"AbortError"!==y.name){e.n=8;break}return e.a(2,{code:-202,msg:"请求超时(".concat(u,"ms)"),success:!1});case 8:return e.a(2,{code:-203,msg:"网络请求失败: ".concat(y.message),success:!1});case 9:return e.a(2)}},e,null,[[4,6],[2,7]])}),f=function(){var t=this,r=arguments;return new Promise(function(n,o){var c=e.apply(t,r);function i(e){a(c,n,o,i,u,"next",e)}function u(e){a(c,n,o,i,u,"throw",e)}i(void 0)})},f.apply(this,arguments)}function l(){var e,t,r="function"==typeof Symbol?Symbol:{},n=r.iterator||"@@iterator",o=r.toStringTag||"@@toStringTag";function c(r,n,o,c){var a=n&&n.prototype instanceof u?n:u,s=Object.create(a.prototype);return p(s,"_invoke",function(r,n,o){var c,u,a,s=0,f=o||[],l=!1,p={p:0,n:0,v:e,a:y,f:y.bind(e,4),d:function(t,r){return c=t,u=0,a=e,p.n=r,i}};function y(r,n){for(u=r,a=n,t=0;!l&&s&&!o&&t<f.length;t++){var o,c=f[t],y=p.p,d=c[2];r>3?(o=d===n)&&(a=c[(u=c[4])?5:(u=3,3)],c[4]=c[5]=e):c[0]<=y&&((o=r<2&&y<c[1])?(u=0,p.v=n,p.n=c[1]):y<d&&(o=r<3||c[0]>n||n>d)&&(c[4]=r,c[5]=n,p.n=d,u=0))}if(o||r>1)return i;throw l=!0,n}return function(o,f,d){if(s>1)throw TypeError("Generator is already running");for(l&&1===f&&y(f,d),u=f,a=d;(t=u<2?e:a)||!l;){c||(u?u<3?(u>1&&(p.n=-1),y(u,a)):p.n=a:p.v=a);try{if(s=2,c){if(u||(o="next"),t=c[o]){if(!(t=t.call(c,a)))throw TypeError("iterator result is not an object");if(!t.done)return t;a=t.value,u<2&&(u=0)}else 1===u&&(t=c.return)&&t.call(c),u<2&&(a=TypeError("The iterator does not provide a '"+o+"' method"),u=1);c=e}else if((t=(l=p.n<0)?a:r.call(n,p))!==i)break}catch(t){c=e,u=1,a=t}finally{s=1}}return{value:t,done:l}}}(r,o,c),!0),s}var i={};function u(){}function a(){}function s(){}t=Object.getPrototypeOf;var f=[][n]?t(t([][n]())):(p(t={},n,function(){return this}),t),y=s.prototype=u.prototype=Object.create(f);function d(e){return Object.setPrototypeOf?Object.setPrototypeOf(e,s):(e.__proto__=s,p(e,o,"GeneratorFunction")),e.prototype=Object.create(y),e}return a.prototype=s,p(y,"constructor",s),p(s,"constructor",a),a.displayName="GeneratorFunction",p(s,o,"GeneratorFunction"),p(y),p(y,o,"Generator"),p(y,n,function(){return this}),p(y,"toString",function(){return"[object Generator]"}),(l=function(){return{w:c,m:d}})()}function p(e,t,r,n){var o=Object.defineProperty;try{o({},"",{})}catch(e){o=0}p=function(e,t,r,n){function c(t,r){p(e,t,function(e){return this._invoke(t,r,e)})}t?o?o(e,t,{value:r,enumerable:!n,configurable:!n,writable:!n}):e[t]=r:(c("next",0),c("throw",1),c("return",2))},p(e,t,r,n)}function y(e){return y="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},y(e)}function d(e,t,r,n,o,c,i){try{var u=e[c](i),a=u.value}catch(e){return void r(e)}u.done?t(a):Promise.resolve(a).then(n,o)}function b(e){return function(){var t=this,r=arguments;return new Promise(function(n,o){var c=e.apply(t,r);function i(e){d(c,n,o,i,u,"next",e)}function u(e){d(c,n,o,i,u,"throw",e)}i(void 0)})}}function m(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,n)}return r}function v(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?m(Object(r),!0).forEach(function(t){g(e,t,r[t])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):m(Object(r)).forEach(function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))})}return e}function g(e,t,r){return(t=function(e){var t=function(e){if("object"!=y(e)||!e)return e;var t=e[Symbol.toPrimitive];if(void 0!==t){var r=t.call(e,"string");if("object"!=y(r))return r;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(e)}(e);return"symbol"==y(t)?t:t+""}(t))in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}e.r(t),e.d(t,{default:()=>A,getToken:()=>T,init:()=>P,logout:()=>E,removeToken:()=>C,toUrl:()=>_});var h=null;function w(){return"undefined"!=typeof window&&"undefined"!=typeof document}function O(e){return w()?new URLSearchParams(window.location.search).get(e):null}function j(e){if(w()){var t=new URLSearchParams(window.location.search);if(t.has(e)){t.delete(e);var r="".concat(window.location.pathname).concat(t.toString()?"?".concat(t.toString()):"");window.history.replaceState({},document.title,r)}}}function S(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=v(v({},{accessCodeKey:"accessCode",tokenKey:"scm_token",timeout:5e3,headers:{},tokenApi:"",refreshCodeApi:"",logoutApi:"",logOutUrl:""}),e);if("string"!=typeof t.accessCodeKey||""===t.accessCodeKey.trim())throw new Error("accessCodeKey必须是有效的字符串");if("string"!=typeof t.tokenKey||""===t.tokenKey.trim())throw new Error("tokenKey必须是有效的字符串");return t}function P(){return k.apply(this,arguments)}function k(){return k=b(l().m(function e(){var t,r,n,o,c=arguments;return l().w(function(e){for(;;)switch(e.p=e.n){case 0:if(t=c.length>0&&void 0!==c[0]?c[0]:{},e.p=1,h=S(t),r=O(h.accessCodeKey),!h.tokenApi||!r){e.n=3;break}return e.n=2,s(h.tokenApi,"POST",{accessCode:r},h.headers,h.timeout);case 2:return 0===(n=e.v).code&&n.data&&localStorage.setItem(h.tokenKey,n.data),j(h.accessCodeKey),e.a(2,n);case 3:return e.a(2,{code:0,msg:"初始化成功,未检测到accessCode",success:!0});case 4:return e.p=4,o=e.v,e.a(2,{code:-100,msg:"初始化失败: ".concat(o.message),success:!1})}},e,null,[[1,4]])})),k.apply(this,arguments)}function T(){return h&&w()?localStorage.getItem(h.tokenKey):null}function C(){h&&w()&&localStorage.removeItem(h.tokenKey)}function E(){return K.apply(this,arguments)}function K(){return(K=b(l().m(function e(){var t,r;return l().w(function(e){for(;;)switch(e.p=e.n){case 0:if(h){e.n=1;break}return e.a(2,{code:-101,msg:"请先调用init方法初始化",success:!1});case 1:if(h.logoutApi){e.n=2;break}return e.a(2,{code:-102,msg:"未配置logoutApi",success:!1});case 2:return e.p=2,e.n=3,s(h.logoutApi,"POST",{},v(v({},h.headers),{},g({},h.tokenKey,T())),h.timeout);case 3:return t=e.v,C(),h.logOutUrl&&w()&&(window.location.href="".concat(h.logOutUrl,"?redirect_uri=").concat(encodeURIComponent(window.location.href))),e.a(2,t);case 4:return e.p=4,r=e.v,e.a(2,{code:-103,msg:"退出失败: ".concat(r.message),success:!1})}},e,null,[[2,4]])}))).apply(this,arguments)}function _(e){return G.apply(this,arguments)}function G(){return(G=b(l().m(function e(t){var r,n,o,c;return l().w(function(e){for(;;)switch(e.p=e.n){case 0:if(h){e.n=1;break}return e.a(2,{code:-101,msg:"请先调用init方法初始化",success:!1});case 1:if(t){e.n=2;break}return e.a(2,{code:-104,msg:"请提供跳转地址",success:!1});case 2:if(h.refreshCodeApi){e.n=3;break}return e.a(2,{code:-105,msg:"未配置refreshCodeApi",success:!1});case 3:if(r=T()){e.n=4;break}return h.logOutUrl&&w()&&(window.location.href=h.logOutUrl),e.a(2,{code:-106,msg:"未找到有效token",success:!1});case 4:return e.p=4,e.n=5,s(h.refreshCodeApi,"POST",{},v(v({},h.headers),{},g({},h.tokenKey,r)),h.timeout);case 5:return 0===(n=e.v).code&&n.data&&w()&&((o=new URL(t)).searchParams.set(h.accessCodeKey,n.data),window.location.href=o.toString()),e.a(2,n);case 6:return e.p=6,c=e.v,e.a(2,{code:-107,msg:"跳转失败: ".concat(c.message),success:!1})}},e,null,[[4,6]])}))).apply(this,arguments)}const A={init:P,getToken:T,removeToken:C,logout:E,toUrl:_};return t})());
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "lgsso-sdk",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "LG SSO登录工具库,处理单点登录、token管理和系统间跳转",
|
|
5
|
+
"main": "dist/lgsso-sdk.js",
|
|
6
|
+
"types": "src/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "webpack",
|
|
9
|
+
"dev": "webpack --watch",
|
|
10
|
+
"prepublishOnly": "npm run build"
|
|
11
|
+
},
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"sso",
|
|
17
|
+
"lg",
|
|
18
|
+
"token",
|
|
19
|
+
"auth",
|
|
20
|
+
"login"
|
|
21
|
+
],
|
|
22
|
+
"author": "",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"webpack": "^5.88.2",
|
|
26
|
+
"webpack-cli": "^5.1.4",
|
|
27
|
+
"babel-loader": "^9.1.3",
|
|
28
|
+
"@babel/core": "^7.22.9",
|
|
29
|
+
"@babel/preset-env": "^7.22.9"
|
|
30
|
+
}
|
|
31
|
+
}
|
package/src/api.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
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
|
+
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lgsso-sdk 类型定义
|
|
3
|
+
*/
|
|
4
|
+
declare namespace LgSso {
|
|
5
|
+
/** 初始化配置参数类型 */
|
|
6
|
+
interface InitOptions {
|
|
7
|
+
/** URL中accessCode的参数名 */
|
|
8
|
+
accessCodeKey?: string;
|
|
9
|
+
/** 存储token的localStorage键名 */
|
|
10
|
+
tokenKey?: string;
|
|
11
|
+
/** 请求超时时间(毫秒) */
|
|
12
|
+
timeout?: number;
|
|
13
|
+
/** 自定义请求头 */
|
|
14
|
+
headers?: Record<string, string>;
|
|
15
|
+
/** 用accessCode换取token的接口地址 */
|
|
16
|
+
tokenApi?: string;
|
|
17
|
+
/** 用token换取新accessCode的接口地址 */
|
|
18
|
+
refreshCodeApi?: string;
|
|
19
|
+
/** 退出登录接口地址 */
|
|
20
|
+
logoutApi?: string;
|
|
21
|
+
/** 退出后重定向的地址 */
|
|
22
|
+
logOutUrl?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** 接口返回结果类型 */
|
|
26
|
+
interface ApiResult {
|
|
27
|
+
/** 状态码(0为成功) */
|
|
28
|
+
code: number;
|
|
29
|
+
/** 提示信息 */
|
|
30
|
+
msg: string;
|
|
31
|
+
/** 是否成功 */
|
|
32
|
+
success: boolean;
|
|
33
|
+
/** 业务数据 */
|
|
34
|
+
data?: any;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** lgsso-sdk 实例类型 */
|
|
39
|
+
interface LgSsoInstance {
|
|
40
|
+
/**
|
|
41
|
+
* 初始化SDK
|
|
42
|
+
* @param options 配置参数
|
|
43
|
+
* @returns 接口返回结果
|
|
44
|
+
*/
|
|
45
|
+
init: (options?: LgSso.InitOptions) => Promise<LgSso.ApiResult>;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 获取localStorage中的token
|
|
49
|
+
* @returns token值或null
|
|
50
|
+
*/
|
|
51
|
+
getToken: () => string | null;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* 删除localStorage中的token
|
|
55
|
+
*/
|
|
56
|
+
removeToken: () => void;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* 退出登录
|
|
60
|
+
* @returns 接口返回结果
|
|
61
|
+
*/
|
|
62
|
+
logout: () => Promise<LgSso.ApiResult>;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* 用token换取新accessCode并跳转
|
|
66
|
+
* @param redirectUrl 目标跳转地址
|
|
67
|
+
* @returns 接口返回结果
|
|
68
|
+
*/
|
|
69
|
+
toUrl: (redirectUrl: string) => Promise<LgSso.ApiResult>;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
declare const lgsso: LgSsoInstance;
|
|
73
|
+
|
|
74
|
+
export default lgsso;
|
|
75
|
+
export { LgSso };
|
|
76
|
+
|
package/src/index.js
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
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
|
+
* 验证并合并配置
|
|
41
|
+
* @param {Object} options - 用户配置
|
|
42
|
+
* @returns {Object} 合并后的配置
|
|
43
|
+
*/
|
|
44
|
+
function validateAndMergeConfig(options = {}) {
|
|
45
|
+
const defaults = {
|
|
46
|
+
accessCodeKey: 'accessCode',
|
|
47
|
+
tokenKey: 'scm_token',
|
|
48
|
+
timeout: 5000,
|
|
49
|
+
headers: {},
|
|
50
|
+
tokenApi: '',
|
|
51
|
+
refreshCodeApi: '',
|
|
52
|
+
logoutApi: '',
|
|
53
|
+
logOutUrl: ''
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// 合并配置
|
|
57
|
+
const merged = { ...defaults, ...options };
|
|
58
|
+
|
|
59
|
+
// 基础验证
|
|
60
|
+
if (typeof merged.accessCodeKey !== 'string' || merged.accessCodeKey.trim() === '') {
|
|
61
|
+
throw new Error('accessCodeKey必须是有效的字符串');
|
|
62
|
+
}
|
|
63
|
+
if (typeof merged.tokenKey !== 'string' || merged.tokenKey.trim() === '') {
|
|
64
|
+
throw new Error('tokenKey必须是有效的字符串');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return merged;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 初始化SDK
|
|
72
|
+
* @param {Object} options - 配置参数
|
|
73
|
+
* @returns {Promise<Object>} 接口返回结果
|
|
74
|
+
*/
|
|
75
|
+
export async function init(options = {}) {
|
|
76
|
+
try {
|
|
77
|
+
// 验证配置
|
|
78
|
+
config = validateAndMergeConfig(options);
|
|
79
|
+
|
|
80
|
+
// 从URL获取accessCode
|
|
81
|
+
const accessCode = getQueryParam(config.accessCodeKey);
|
|
82
|
+
|
|
83
|
+
// 若配置了tokenApi且存在accessCode,自动换取token
|
|
84
|
+
if (config.tokenApi && accessCode) {
|
|
85
|
+
const result = await request(
|
|
86
|
+
config.tokenApi,
|
|
87
|
+
'POST',
|
|
88
|
+
{ accessCode },
|
|
89
|
+
config.headers,
|
|
90
|
+
config.timeout
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
// 无论code是否为0,只要返回了token就存储
|
|
94
|
+
if (result.code === 0 && result.data) {
|
|
95
|
+
localStorage.setItem(config.tokenKey, result.data);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// 移除URL中的accessCode
|
|
99
|
+
removeQueryParam(config.accessCodeKey);
|
|
100
|
+
|
|
101
|
+
return result;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// 没有accessCode的情况
|
|
105
|
+
return {
|
|
106
|
+
code: 0,
|
|
107
|
+
msg: '初始化成功,未检测到accessCode',
|
|
108
|
+
success: true
|
|
109
|
+
};
|
|
110
|
+
} catch (error) {
|
|
111
|
+
return {
|
|
112
|
+
code: -100,
|
|
113
|
+
msg: `初始化失败: ${error.message}`,
|
|
114
|
+
success: false
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* 获取localStorage中的token
|
|
121
|
+
* @returns {string|null} token值
|
|
122
|
+
*/
|
|
123
|
+
export function getToken() {
|
|
124
|
+
if (!config || !isBrowser()) return null;
|
|
125
|
+
return localStorage.getItem(config.tokenKey);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* 删除localStorage中的token
|
|
130
|
+
*/
|
|
131
|
+
export function removeToken() {
|
|
132
|
+
if (!config || !isBrowser()) return;
|
|
133
|
+
localStorage.removeItem(config.tokenKey);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* 退出登录
|
|
138
|
+
* @returns {Promise<Object>} 接口返回结果
|
|
139
|
+
*/
|
|
140
|
+
export async function logout() {
|
|
141
|
+
if (!config) {
|
|
142
|
+
return {
|
|
143
|
+
code: -101,
|
|
144
|
+
msg: '请先调用init方法初始化',
|
|
145
|
+
success: false
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (!config.logoutApi) {
|
|
150
|
+
return {
|
|
151
|
+
code: -102,
|
|
152
|
+
msg: '未配置logoutApi',
|
|
153
|
+
success: false
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
try {
|
|
158
|
+
// 调用退出接口
|
|
159
|
+
const result = await request(
|
|
160
|
+
config.logoutApi,
|
|
161
|
+
'POST',
|
|
162
|
+
{},
|
|
163
|
+
{ ...config.headers, [config.tokenKey]: getToken() },
|
|
164
|
+
config.timeout
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
// 无论接口返回如何,都删除本地token
|
|
168
|
+
removeToken();
|
|
169
|
+
|
|
170
|
+
// 跳转到退出地址
|
|
171
|
+
if (config.logOutUrl && isBrowser()) {
|
|
172
|
+
window.location.href = `${config.logOutUrl}?redirect_uri=${encodeURIComponent(window.location.href)}`;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return result;
|
|
176
|
+
} catch (error) {
|
|
177
|
+
return {
|
|
178
|
+
code: -103,
|
|
179
|
+
msg: `退出失败: ${error.message}`,
|
|
180
|
+
success: false
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* 用token换取新accessCode并跳转
|
|
187
|
+
* @param {string} redirectUrl - 目标跳转地址
|
|
188
|
+
* @returns {Promise<Object>} 接口返回结果
|
|
189
|
+
*/
|
|
190
|
+
export async function toUrl(redirectUrl) {
|
|
191
|
+
if (!config) {
|
|
192
|
+
return {
|
|
193
|
+
code: -101,
|
|
194
|
+
msg: '请先调用init方法初始化',
|
|
195
|
+
success: false
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (!redirectUrl) {
|
|
200
|
+
return {
|
|
201
|
+
code: -104,
|
|
202
|
+
msg: '请提供跳转地址',
|
|
203
|
+
success: false
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (!config.refreshCodeApi) {
|
|
208
|
+
return {
|
|
209
|
+
code: -105,
|
|
210
|
+
msg: '未配置refreshCodeApi',
|
|
211
|
+
success: false
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// 获取当前token
|
|
216
|
+
const token = getToken();
|
|
217
|
+
if (!token) {
|
|
218
|
+
// 没有token时跳转到登录页
|
|
219
|
+
if (config.logOutUrl && isBrowser()) {
|
|
220
|
+
window.location.href = config.logOutUrl;
|
|
221
|
+
}
|
|
222
|
+
return {
|
|
223
|
+
code: -106,
|
|
224
|
+
msg: '未找到有效token',
|
|
225
|
+
success: false
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
try {
|
|
230
|
+
// 调用接口换取新accessCode
|
|
231
|
+
const result = await request(
|
|
232
|
+
config.refreshCodeApi,
|
|
233
|
+
'POST',
|
|
234
|
+
{},
|
|
235
|
+
{ ...config.headers, [config.tokenKey]: token },
|
|
236
|
+
config.timeout
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
// 成功获取code后跳转
|
|
240
|
+
if (result.code === 0 && result.data && isBrowser()) {
|
|
241
|
+
const url = new URL(redirectUrl);
|
|
242
|
+
url.searchParams.set(config.accessCodeKey, result.data);
|
|
243
|
+
window.location.href = url.toString();
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return result;
|
|
247
|
+
} catch (error) {
|
|
248
|
+
return {
|
|
249
|
+
code: -107,
|
|
250
|
+
msg: `跳转失败: ${error.message}`,
|
|
251
|
+
success: false
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// 默认导出
|
|
257
|
+
export default {
|
|
258
|
+
init,
|
|
259
|
+
getToken,
|
|
260
|
+
removeToken,
|
|
261
|
+
logout,
|
|
262
|
+
toUrl
|
|
263
|
+
};
|
|
264
|
+
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
entry: './src/index.js',
|
|
5
|
+
output: {
|
|
6
|
+
path: path.resolve(__dirname, 'dist'),
|
|
7
|
+
filename: 'lgsso-sdk.js',
|
|
8
|
+
library: 'lgssoSdk',
|
|
9
|
+
libraryTarget: 'umd',
|
|
10
|
+
globalObject: 'this',
|
|
11
|
+
umdNamedDefine: true
|
|
12
|
+
},
|
|
13
|
+
module: {
|
|
14
|
+
rules: [
|
|
15
|
+
{
|
|
16
|
+
test: /\.js$/,
|
|
17
|
+
exclude: /node_modules/,
|
|
18
|
+
use: {
|
|
19
|
+
loader: 'babel-loader',
|
|
20
|
+
options: {
|
|
21
|
+
presets: ['@babel/preset-env']
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
mode: 'production',
|
|
28
|
+
optimization: {
|
|
29
|
+
minimize: true
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|