bjx-auth 1.3.0 → 1.5.1

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.
@@ -1,80 +0,0 @@
1
- const { hashHS256 } = require('./hash')
2
- const config = require('../../config')
3
-
4
- // 请求签名相关
5
- function sortData(data) {
6
- if (Object.prototype.toString.call(data) === '[object Object]') {
7
- return Object.keys(data)
8
- .sort() // 默认情况下 sort 会按字典顺序(ASCII 顺序)进行排序
9
- .reduce((newObj, key) => {
10
- newObj[key] = sortData(data[key])
11
- return newObj
12
- }, {})
13
- } else if (Object.prototype.toString.call(data) === '[object Array]') {
14
- return data.map((item) => sortData(item))
15
- } else {
16
- return data
17
- }
18
- }
19
- function objToStr(data) {
20
- let dataArr = []
21
- Object.entries(data).map(([key, value]) => {
22
- if (value === undefined || value === '' || value === null) {
23
- } else {
24
- if (
25
- Object.prototype.toString.call(value) === '[object Array]' ||
26
- Object.prototype.toString.call(value) === '[object Object]'
27
- ) {
28
- dataArr.push(`${key}=${encodeURIComponent(JSON.stringify(value))}`)
29
- } else {
30
- dataArr.push(`${key}=${encodeURIComponent(value)}`)
31
- }
32
- }
33
- })
34
- return dataArr.join('&')
35
- }
36
-
37
- function signFn(data, signKey) {
38
- const sorted = sortData(data)
39
- const dataStr = objToStr(sorted)
40
- const sign = hashHS256(dataStr, signKey)
41
- return {
42
- ...data,
43
- sign,
44
- }
45
- }
46
-
47
- function getReqSign(params, signKey) {
48
- const {
49
- eqp,
50
- os,
51
- ba,
52
- bp,
53
- ver,
54
- apiVersion,
55
- signVersion,
56
- clientId,
57
- clientSecret,
58
- data = {},
59
- } = params || {}
60
-
61
- const obj = {
62
- eqp: eqp || config.eqp || '',
63
- os: os * 1 || config.os * 1 || 1,
64
- ba: ba || config.ba || '',
65
- bp: bp || config.bp || '',
66
- ver: ver || config.ver || '1.0.0',
67
- ts: Date.now(),
68
- apiVersion: apiVersion || config.apiVersion || '1.0.0',
69
- signVersion: signVersion || config.signVersion || 'V1',
70
- clientId: clientId || config.clientId || '',
71
- clientSecret: clientSecret || config.clientSecret || '',
72
- data,
73
- }
74
- signKey = signKey || config.signKey
75
-
76
- const res = signFn(obj, signKey)
77
- return res
78
- }
79
-
80
- exports = module.exports = getReqSign
@@ -1,72 +0,0 @@
1
- const { auth, enterpriseapi } = require('./axios')
2
- const { getReqSign } = require('./sign')
3
-
4
- /**
5
- * nodejs授权
6
- */
7
- async function baseInfo(opts, signObj = {}) {
8
- opts.data = { ...opts.data, ...getReqSign(signObj) }
9
- return auth({
10
- url: '/api/User/' + opts.__type__,
11
- method: 'POST',
12
- ...opts,
13
- })
14
- }
15
-
16
- async function userInfo(opts, signObj = {}) {
17
- opts.data = { ...opts.data, ...getReqSign(signObj) }
18
- return enterpriseapi({
19
- url: '/enterprise/user/userInfo',
20
- method: 'POST',
21
- ...opts,
22
- }).then((res) => {
23
- const { data: rsp_obj } = res
24
- res.data = {
25
- HttpStatusCode: rsp_obj.code,
26
- Error: rsp_obj.errMsg,
27
- IsError: !rsp_obj.success,
28
- Data:
29
- rsp_obj.data && Object.keys(rsp_obj.data).length
30
- ? {
31
- HasCompany: rsp_obj.data.hasCompanyState > 0, // BaseInfoV1和BaseInfo的差别
32
- Id: rsp_obj.data.id || '',
33
- UId: rsp_obj.data.userId || 0,
34
- UserName: rsp_obj.data.userName || '',
35
- NickName: rsp_obj.data.nickName || '',
36
- Email: rsp_obj.data.userEmail || '',
37
- EmailIsCheck: rsp_obj.data.emailCheckState > 0,
38
- RegionCode: rsp_obj.data.regionCode || '',
39
- Phone: rsp_obj.data.userPhone || '',
40
- PhoneIsCheck: rsp_obj.data.phoneCheckState > 0,
41
- HeadUrl: rsp_obj.data.headUrl || '',
42
- BackImage: rsp_obj.data.backImage || '',
43
- BriefIntro: rsp_obj.data.briefIntro || '',
44
- Industry: rsp_obj.data.industryId || 0,
45
- Source: rsp_obj.data.registerSource || 0,
46
- ShowName: rsp_obj.data.showName || '',
47
- HeadIsDef: rsp_obj.data.headDefState > 0,
48
- Nick: rsp_obj.data.nick || '',
49
- NickShowName: rsp_obj.data.nickShowName || '',
50
- RegDate: rsp_obj.data.registerDate || '',
51
- }
52
- : {},
53
- }
54
- return res
55
- })
56
- }
57
-
58
- async function getUserInfo(opts, signArr) {
59
- const typeArr = ['BaseInfo', 'BaseInfoV1', 'ExtendInfo', 'userInfo']
60
- if (!typeArr.includes(opts.__type__)) {
61
- opts.__type__ = typeArr[0]
62
- }
63
- if (opts.__type__ === 'userInfo') {
64
- return userInfo(opts, signArr)
65
- } else {
66
- return baseInfo(opts, signArr)
67
- }
68
- }
69
-
70
- exports = module.exports = {
71
- getUserInfo,
72
- }
@@ -1,66 +0,0 @@
1
- const {
2
- setConfig,
3
- getConfig,
4
- getToken: getTokenApi,
5
- getUserInfo: getUserInfoApi,
6
- } = require('../request')
7
-
8
- async function getToken(cookies, isRefresh, { headers, ctx }) {
9
- return getTokenApi(
10
- {
11
- __isRefresh__: isRefresh,
12
- headers: {
13
- Cookie: Object.entries(cookies)
14
- .map((v) => v.join('='))
15
- .join('; '),
16
- ...headers,
17
- },
18
- },
19
- {
20
- ctx,
21
- },
22
- ).then(({ data }) => {
23
- if (data.isError === false) {
24
- return {
25
- bjx_token_flag: cookies['idsrv.session'],
26
- token_type: '',
27
- access_token: data.data.authToken,
28
- expires_at: data.data.expiresAt || (Date.now() / 1000 + 4 * 3600) | 0, // 4小时过期
29
- }
30
- }
31
- })
32
- }
33
-
34
- async function getUserInfo(token, type, { headers, ctx }) {
35
- // 兼容老的token 以支持老版本APP嵌入页面
36
- const tokenHeader = {}
37
- if (token.startsWith('Bearer ')) {
38
- tokenHeader['Authorization'] = token
39
- } else {
40
- tokenHeader['AuthToken'] = token
41
- }
42
-
43
- return getUserInfoApi(
44
- {
45
- __type__: type,
46
- headers: {
47
- ...headers,
48
- ...tokenHeader,
49
- },
50
- },
51
- {
52
- ctx,
53
- },
54
- ).then(({ data }) => {
55
- if (data.IsError === false) {
56
- return data.Data
57
- }
58
- })
59
- }
60
-
61
- module.exports = {
62
- setConfig,
63
- getConfig,
64
- getToken,
65
- getUserInfo,
66
- }
@@ -1,49 +0,0 @@
1
- async function setConfig(obj) {
2
- console.log('设置配置方法', obj)
3
- }
4
-
5
- async function getToken(cookie, { headers }) {
6
- console.log('令牌方法', cookie, headers)
7
- return {
8
- is_bjx_token: true,
9
- token_type: '',
10
- access_token:
11
- 'xn4d9DmczNYqDDHt1kHA95l923HSjqNbaYUNVzDNw9FTkxZGg7tsZutqFgfg0gTXoxH9LCxs5uuQ_sBBi54WWg01gESgzSMQV7a1f2utaBr90QPSCy-oLu5YUdHzCPDszfx2P8M4xm4LRMNehjXl8cG0Xph80FdJ7-OntP_ZdGE8ERWAppZATpSZML1oOMj2AKg_Z8YReHTqdp6BuFJqpk2qdCtsFyrqsCbh87pRcghj93eDB4sbSXmKOoc-96LO',
12
- expires_at: 1756375244,
13
- }
14
- }
15
-
16
- async function getUserInfo(token, type, { headers }) {
17
- console.log('用户信息方法', token, type, headers)
18
- return {
19
- ts: new Date().toLocaleString('sv-SE'),
20
- Id: '4316B40F-DBB6-4AA5-8242-5425C7D36DFB',
21
- UId: 1100000010,
22
- UserName: 'bjxadmin',
23
- NickName: '徐薇',
24
- Email: 'caoce5158@qq.com',
25
- EmailIsCheck: true,
26
- RegionCode: '+86',
27
- Phone: '13911112222',
28
- PhoneIsCheck: true,
29
- HeadUrl:
30
- 'https://static.bjx.com.cn/EnterpriseNew/SeekerImg/1100000010/2023050813520237_74810.jpeg',
31
- BackImage:
32
- 'http://img01.mybjx.net/webupload/image/20231109/3e06d701bc00002.png',
33
- BriefIntro: '',
34
- Industry: 1300,
35
- Source: 9,
36
- ShowName: '徐薇',
37
- HeadIsDef: false,
38
- Nick: '',
39
- NickShowName: '星友2222',
40
- RegDate: '2016-05-09T17:14:56.473',
41
- PwdIsSet: false,
42
- }
43
- }
44
-
45
- module.exports = {
46
- setConfig,
47
- getToken,
48
- getUserInfo,
49
- }
@@ -1,9 +0,0 @@
1
- const { setConfig, getConfig } = require('../config')
2
- const strategy = require('./strategy')
3
- const utils = require('./utils')
4
-
5
- exports = module.exports = Object.assign(
6
- { setConfig, getConfig },
7
- strategy,
8
- utils,
9
- )
@@ -1,268 +0,0 @@
1
- const { Strategy } = require('passport')
2
- const { setConfig, getConfig, getToken, getUserInfo } = require('./handle')
3
- const { errorLogger, debugLogger } = require('../logger')
4
-
5
- class BjxStrategy extends Strategy {
6
- constructor(options = {}, verify) {
7
- super()
8
-
9
- // 策略名称
10
- this.name = 'bjx'
11
-
12
- // 配置选项
13
- this.loadUserInfo = options.loadUserInfo || false
14
- this.userInfoDuration = this.normalizeDuration(options.loadUserInfo)
15
- this.userInfoType = options.userInfoType || ''
16
- this.handleHeadrToken = options.handleHeadrToken || false
17
- this.verify = verify || ((user, done) => done(null, user))
18
-
19
- // 设置配置缓存
20
- setConfig(options.authConfig)
21
- if (options.authConfig.debug) {
22
- debugLogger('配置项', getConfig())
23
- }
24
- }
25
-
26
- normalizeDuration(val) {
27
- // loadUserInfo参数 单位为分钟 但是这里返回毫秒 减少比较时的计算
28
- if (typeof val === 'number' && val > 0) {
29
- return val * 60 * 1000
30
- }
31
- return 30 * 60 * 1000
32
- }
33
-
34
- authenticate(req, options) {
35
- // 检查必要的cookie
36
- const hasRequiredCookies =
37
- req.cookies &&
38
- req.cookies.get('idsrv.session') &&
39
- req.cookies.get('.AspNetCore.Identity.Application')
40
-
41
- // 检查必要的header
42
- const hasRequiredHeaders =
43
- req.headers && (req.headers.authtoken || req.headers.authorization)
44
-
45
- // 假如不存在 则认证失败
46
- if (!hasRequiredCookies && !(this.handleHeadrToken && hasRequiredHeaders)) {
47
- debugLogger('认证失败', hasRequiredCookies, '(', this.handleHeadrToken, hasRequiredHeaders, ')')
48
- return this.fail()
49
- }
50
-
51
- if (this.handleHeadrToken && hasRequiredHeaders) {
52
- this.executeCosplayAuthentication(req)
53
- .then((user) => {
54
- this.success(user)
55
- })
56
- .catch((err) => {
57
- if (err.message === '__goto_next__') {
58
- this.pass()
59
- } else {
60
- this.error(err)
61
- }
62
- })
63
- } else if (hasRequiredCookies) {
64
- // 否则 执行认证流程
65
- this.executeAuthentication(req)
66
- .then((user) =>
67
- // 创建策略时 可传入回调函数 已追加认证逻辑
68
- this.verify(user, (err, verifiedUser) => {
69
- if (err) return this.error(err)
70
- this.success(verifiedUser)
71
- }),
72
- )
73
- .catch((err) => {
74
- if (err.message === '__goto_next__') {
75
- this.pass()
76
- } else {
77
- this.error(err)
78
- }
79
- })
80
- } else {
81
- this.pass()
82
- }
83
- }
84
-
85
- async executeCosplayAuthentication(req) {
86
- // 以Bearer开始的 为Authorization头 否则为Authtoken头
87
- let token = ''
88
- let isAuthorization = false
89
- if (req.headers?.authtoken) {
90
- token = req.headers.authtoken
91
- } else if (req.headers?.authorization) {
92
- token = req.headers.authorization.replace('Bearer ', '')
93
- isAuthorization = true
94
- }
95
-
96
- const su = req?.session?.passport?.user || {}
97
- if (su.token?.access_token === token) {
98
- throw new Error('__goto_next__')
99
- }
100
-
101
- // 假如session里面没有不是该token说明换了 需要重新获取
102
- su.info = null
103
- const userInfo = await this.getUserInfoWithRefresh(
104
- (isAuthorization ? 'Bearer ' : '') + token,
105
- req,
106
- {},
107
- )
108
- if (userInfo) {
109
- debugLogger(
110
- `通过${isAuthorization ? 'Authorization' : 'Authtoken'}头信息登录系统`,
111
- )
112
- if (isAuthorization) {
113
- return {
114
- token: {
115
- token_type: 'Bearer',
116
- access_token: token,
117
- expires_at: (Date.now() / 1000 + 4 * 600) | 0,
118
- },
119
- info: userInfo,
120
- }
121
- } else {
122
- return {
123
- token: {
124
- bjx_token_flag: 'This token from authtoken/authorization header',
125
- token_type: '',
126
- access_token: token,
127
- expires_at: (Date.now() / 1000 + 4 * 600) | 0,
128
- },
129
- info: userInfo,
130
- }
131
- }
132
- } else {
133
- throw new Error('Invalid headers token')
134
- }
135
- }
136
-
137
- async executeAuthentication(req) {
138
- // 获取请求头信息
139
- const headers = this.extractHeaders(req)
140
-
141
- // 获取cookie
142
- const cookies = {
143
- 'idsrv.session': req.cookies.get('idsrv.session'),
144
- '.AspNetCore.Identity.Application': req.cookies.get(
145
- '.AspNetCore.Identity.Application',
146
- ),
147
- }
148
-
149
- // 获取令牌 不存在时直接抛出错误
150
- const tokenData = await this.getTokenWithRefresh(cookies, req, headers)
151
- if (!tokenData) throw new Error('Get token failed')
152
-
153
- // 获取用户信息 假如配置中为false 则不获取
154
- let userInfo
155
- if (this.loadUserInfo) {
156
- const token = tokenData.access_token
157
- userInfo = await this.getUserInfoWithRefresh(token, req, headers)
158
- }
159
-
160
- return {
161
- token: tokenData,
162
- info: userInfo,
163
- }
164
- }
165
-
166
- extractHeaders(req) {
167
- return {
168
- 'X-Forwarded-For': req.headers['x-forwarded-for'],
169
- 'X-Forwarded-Host': req.headers['x-forwarded-host'],
170
- 'User-Agent': req.headers['user-agent'],
171
- Referer: req.headers['referer'],
172
- }
173
- }
174
-
175
- async getTokenWithRefresh(cookies, req, headers) {
176
- const su = req?.session?.passport?.user || {}
177
- const now = Date.now()
178
-
179
- // 判断token的标识和cookie中的标识是否一致
180
- if (su.token?.bjx_token_flag !== cookies['idsrv.session']) {
181
- su.token = undefined
182
- su.info = undefined
183
- debugLogger('令牌标识和cookie内的不一致')
184
- }
185
-
186
- // 检查passport中的令牌 是否需要刷新
187
- if (su.token && su.token.expires_at * 1000 > now) {
188
- throw new Error('__goto_next__')
189
- }
190
-
191
- // 假如存在token 还到这一步 说明是刷新token
192
- const isRefresh = !!su.token
193
- if (isRefresh) {
194
- debugLogger('刷新令牌')
195
- }
196
-
197
- // 获取新token
198
- const newToken = await getToken(cookies, isRefresh, {
199
- ctx: req.ctx,
200
- headers,
201
- })
202
-
203
- debugLogger('获取“新”令牌', cookies, isRefresh, newToken || '无')
204
- return newToken
205
- }
206
-
207
- async getUserInfoWithRefresh(token, req, headers) {
208
- const su = req?.session?.passport?.user || {}
209
- const now = Date.now()
210
-
211
- // 检查passport中的用户信息 是否需要刷新
212
- if (su.info && su.info.__expires_at__ > now) {
213
- throw new Error('__goto_next__')
214
- }
215
-
216
- // 获取新用户信息
217
- const newUserInfo = await getUserInfo(token, this.userInfoType, {
218
- ctx: req.ctx,
219
- headers,
220
- })
221
-
222
- // 设置用户信息的过期时间 这里用毫秒
223
- if (newUserInfo) {
224
- newUserInfo.__expires_at__ = now + this.userInfoDuration
225
- }
226
-
227
- debugLogger('获取“新”用户信息', token, newUserInfo || '无')
228
- return newUserInfo
229
- }
230
- }
231
-
232
- // Koa中间件
233
- function createBjxAuthMiddleware(passport) {
234
- return async (ctx, next) => {
235
- // 缓存app对象 方便打印日志
236
- if (!getConfig('app')) {
237
- setConfig('app', ctx.app)
238
- }
239
-
240
- // 等待每次鉴权结果
241
- await new Promise((resolve) => {
242
- passport.authenticate('bjx', (err, user, info, status) => {
243
- if (err) {
244
- // this.error()进到这里
245
- errorLogger(err)
246
- ctx.logout()
247
- } else if (user) {
248
- // this.success()进到这里
249
- ctx.login(user)
250
- } else {
251
- // this.fail()进到这里
252
- ctx.logout()
253
- }
254
- resolve()
255
- })(ctx, () => {
256
- // this.pass()进到这里
257
- resolve()
258
- })
259
- })
260
-
261
- await next()
262
- }
263
- }
264
-
265
- module.exports = {
266
- BjxStrategy,
267
- createBjxAuthMiddleware,
268
- }