feat(msync): 用户令牌签发端点(UserTokenIssueView)+ Market 返回 Source/ServiceName/ServiceOnline
This commit is contained in:
1 parent
00340f818f
commit
a30e27e4bc
3 files changed
+57
No files matched your search
@@ -72,6 +72,11 @@ def _AppFlat(app, tenant_installed=None, user_record=None, company_installed=Non
|
||||
'AuthRequired': app.AuthRequired,
|
||||
'MenuRootUUID': UUIDObj(app.MenuRootUUID).str if app.MenuRootUUID else '',
|
||||
'AppSort': app.AppSort or 0,
|
||||
# 远端注入(MSYNC):来源/提供方服务/在线状态(前端"服务离线"提示用)
|
||||
'Source': app.Source or 'local',
|
||||
'ServiceName': app.ServiceName or '',
|
||||
'ServiceDomain': app.ServiceDomain or '',
|
||||
'ServiceOnline': app.ServiceOnline if app.ServiceOnline is not None else 1,
|
||||
# 前端入口(真应用注册协议:安装后"打开应用"跳转目标)
|
||||
'FrontendRoute': app.FrontendRoute or '',
|
||||
'FrontendType': app.FrontendType or 'vue',
|
||||
|
||||
@@ -74,6 +74,17 @@ def _GetCompanyBytes(request):
|
||||
return None
|
||||
|
||||
|
||||
def _ResolveRoleUUID(request):
|
||||
"""当前角色 UUID bytes(Cookie CurrentRoleUUID)"""
|
||||
role_uuid_str = request.COOKIES.get('CurrentRoleUUID', '')
|
||||
if not role_uuid_str:
|
||||
return None
|
||||
try:
|
||||
return UUIDObj(role_uuid_str).bytes
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def _ParseUUIDList(values):
|
||||
"""解析公司 UUID 列表(str/bytes 混合 → bytes 列表),过滤无效"""
|
||||
result = []
|
||||
@@ -532,3 +543,43 @@ class AppConfigCopyView(ELTAuthenticatedAPIView):
|
||||
if not ok:
|
||||
return Response({'code': 1, 'msg': err}, status=400)
|
||||
return Response({'code': 0, 'msg': f'已复制 {copied} 项配置到 {len(targets)} 家公司', 'data': {'Copied': copied}})
|
||||
|
||||
|
||||
class UserTokenIssueView(ELTAuthenticatedAPIView):
|
||||
"""签发用户级短期令牌——员工 iframe 访问子服务器远端 App 时透传用户身份
|
||||
|
||||
POST /API/MSync/UserToken/Issue
|
||||
Body: { AudienceService: 目标服务名(子服务器 MSYNC 注册名) }
|
||||
返回: { AccessToken, ExpiresAt(ts), AudienceService }
|
||||
|
||||
令牌由主服务器 MASTER_SECRET 签发(HMAC-SHA256,typ=user,TTL 10min),
|
||||
附带当前用户/角色/公司/租户上下文;子服务器通过
|
||||
/api/msync/user/token/introspect/ 自省获取用户身份。
|
||||
"""
|
||||
|
||||
def post(self, request):
|
||||
audience = (request.data.get('AudienceService') or '').strip()
|
||||
if not audience:
|
||||
return Response({'code': 1, 'msg': '缺少目标服务名 AudienceService'}, status=400)
|
||||
user_uuid = _GetUserBytes(request)
|
||||
if not user_uuid:
|
||||
return Response({'code': 401, 'msg': '无法识别当前用户'}, status=401)
|
||||
try:
|
||||
from gvsdsdk.msync.auth import IssueUserToken
|
||||
token, expires = IssueUserToken(
|
||||
user_uuid,
|
||||
RoleUUID=_ResolveRoleUUID(request),
|
||||
CompanyUUID=_GetCompanyBytes(request),
|
||||
TenantUUID=_GetTenantBytes(request),
|
||||
AudienceService=audience,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f'用户令牌签发异常: {e}')
|
||||
return Response({'code': 500, 'msg': '用户令牌签发失败'}, status=500)
|
||||
if not token:
|
||||
return Response({'code': 500, 'msg': '用户令牌签发失败'}, status=500)
|
||||
return Response({'code': 0, 'data': {
|
||||
'AccessToken': token,
|
||||
'ExpiresAt': expires,
|
||||
'AudienceService': audience,
|
||||
}})
|
||||
@@ -159,6 +159,7 @@ urlpatterns = [
|
||||
path('API/App/Pending', gerp_views.AppPendingView.as_view(), name='app_pending'),
|
||||
path('API/App/Config', gerp_views.AppConfigView.as_view(), name='app_config'),
|
||||
path('API/App/Config/Copy', gerp_views.AppConfigCopyView.as_view(), name='app_config_copy'),
|
||||
path('API/MSync/UserToken/Issue', gerp_views.UserTokenIssueView.as_view(), name='msync_user_token_issue'),
|
||||
|
||||
# 官网社区(discuss.gvsds.com 前端调用)
|
||||
path('API/Community/', include('gvsds.apps.community.urls')),
|
||||
|
||||
Reference in new issue
Block a user