houzhongjian
2024-12-03 874287a4c02d0a980d8b97c4a691b4f37ec5e812
提交 | 用户 | 时间
e7c126 1 package com.iailab.module.report.framework.jmreport.core.service;
H 2
3 import cn.hutool.core.util.ObjectUtil;
4 import cn.hutool.core.util.StrUtil;
5 import com.iailab.framework.common.exception.ServiceException;
6 import com.iailab.framework.common.util.servlet.ServletUtils;
7 import com.iailab.framework.security.config.SecurityProperties;
8 import com.iailab.framework.security.core.LoginUser;
9 import com.iailab.framework.security.core.util.SecurityFrameworkUtils;
10 import com.iailab.framework.tenant.core.context.TenantContextHolder;
11 import com.iailab.framework.web.core.util.WebFrameworkUtils;
12 import com.iailab.module.system.api.oauth2.OAuth2TokenApi;
13 import com.iailab.module.system.api.oauth2.dto.OAuth2AccessTokenCheckRespDTO;
14 import com.iailab.module.system.api.permission.PermissionApi;
15 import com.iailab.module.system.enums.permission.RoleCodeEnum;
16 import lombok.RequiredArgsConstructor;
17 import org.jeecg.modules.jmreport.api.JmReportTokenServiceI;
18 import org.springframework.http.HttpHeaders;
19
20 import javax.servlet.http.HttpServletRequest;
21 import java.util.Objects;
22
23 /**
24  * {@link JmReportTokenServiceI} 实现类,提供积木报表的 Token 校验、用户信息的查询等功能
25  *
26  * @author 随心
27  */
28 @RequiredArgsConstructor
29 public class JmReportTokenServiceImpl implements JmReportTokenServiceI {
30
31     /**
32      * 积木 token head 头
33      */
34     private static final String JM_TOKEN_HEADER = "X-Access-Token";
35     /**
36      * auth 相关格式
37      */
38     private static final String AUTHORIZATION_FORMAT = SecurityFrameworkUtils.AUTHORIZATION_BEARER + " %s";
39
40     private final OAuth2TokenApi oauth2TokenApi;
41     private final PermissionApi permissionApi;
42
43     private final SecurityProperties securityProperties;
44
45     /**
46      * 自定义 API 数据集appian自定义 Header,解决 Token 传递。
47      * 参考 <a href="http://report.jeecg.com/2222224">api数据集token机制详解</a> 文档
48      *
49      * @return 新 head
50      */
51     @Override
52     public HttpHeaders customApiHeader() {
53         // 读取积木标标系统的 token
54         HttpServletRequest request = ServletUtils.getRequest();
55         String token = request.getHeader(JM_TOKEN_HEADER);
56
57         // 设置到 iailab 系统的 token
58         HttpHeaders headers = new HttpHeaders();
59         headers.add(securityProperties.getTokenHeader(), String.format(AUTHORIZATION_FORMAT, token));
60         return headers;
61     }
62
63     /**
64      * 校验 Token 是否有效,即验证通过
65      *
66      * @param token JmReport 前端传递的 token
67      * @return 是否认证通过
68      */
69     @Override
70     public Boolean verifyToken(String token) {
71         Long userId = SecurityFrameworkUtils.getLoginUserId();
72         if (!Objects.isNull(userId)) {
73             return true;
74         }
75         return buildLoginUserByToken(token) != null;
76     }
77
78     /**
79      * 获得用户编号
80      * <p>
81      * 虽然方法名获得的是 username,实际对应到项目中是用户编号
82      *
83      * @param token JmReport 前端传递的 token
84      * @return 用户编号
85      */
86     @Override
87     public String getUsername(String token) {
88         Long userId = SecurityFrameworkUtils.getLoginUserId();
89         if (ObjectUtil.isNotNull(userId)) {
90             return String.valueOf(userId);
91         }
92         LoginUser user = buildLoginUserByToken(token);
93         return user == null ? null : String.valueOf(user.getId());
94     }
95
96     /**
97      * 基于 token 构建登录用户
98      *
99      * @param token token
100      * @return 返回 token 对应的用户信息
101      */
102     private LoginUser buildLoginUserByToken(String token) {
103         if (StrUtil.isEmpty(token)) {
104             return null;
105         }
106         // TODO 如下的实现不算特别优雅,主要咱是不想搞的太复杂,所以参考对应的 Filter 先实现了
107
108         // ① 参考 TokenAuthenticationFilter 的认证逻辑(Security 的上下文清理,交给 Spring Security 完成)
109         // 目的:实现基于 JmReport 前端传递的 token,实现认证
110         TenantContextHolder.setIgnore(true); // 忽略租户,保证可查询到 token 信息
111         LoginUser user = null;
112         try {
113             OAuth2AccessTokenCheckRespDTO accessToken = oauth2TokenApi.checkAccessToken(token).getCheckedData();
114             if (accessToken == null) {
115                 return null;
116             }
117             user = new LoginUser().setId(accessToken.getUserId()).setUserType(accessToken.getUserType())
118                     .setTenantId(accessToken.getTenantId()).setScopes(accessToken.getScopes());
119         } catch (ServiceException ignored) {
120             // do nothing:如果报错,说明认证失败,则返回 false 即可
121         }
122         if (user == null) {
123             return null;
124         }
125         SecurityFrameworkUtils.setLoginUser(user, WebFrameworkUtils.getRequest());
126
127         // ② 参考 TenantContextWebFilter 实现(Tenant 的上下文清理,交给 TenantContextWebFilter 完成)
128         // 目的:基于 LoginUser 获得到的租户编号,设置到 Tenant 上下文,避免查询数据库时的报错
129         TenantContextHolder.setIgnore(false);
130         TenantContextHolder.setTenantId(user.getTenantId());
131         return user;
132     }
133
134     @Override
135     public String[] getRoles(String token) {
874287 136         // 设置租户上下文。原因是:/jmreport/** 纯前端地址,不会走 buildLoginUserByToken 逻辑
H 137         LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
138         if (loginUser == null) {
139             return null;
140         }
141         TenantContextHolder.setTenantId(loginUser.getTenantId());
142
e7c126 143         // 参见文档 https://help.jeecg.com/jimureport/prodSafe.html 文档
H 144         // 适配:如果是本系统的管理员,则转换成 jimu 报表的管理员
145         Long userId = SecurityFrameworkUtils.getLoginUserId();
146         return permissionApi.hasAnyRoles(userId, RoleCodeEnum.SUPER_ADMIN.getCode()).getCheckedData()
147                 ? new String[]{"admin"} : null;
148     }
149
150     @Override
151     public String getTenantId() {
152         // 补充说明:不能直接通过 TenantContext 获取,因为 jimu 报表前端请求时,没有带上 tenant-id Header
153         LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
154         if (loginUser == null) {
155             return null;
156         }
157         return StrUtil.toStringOrNull(loginUser.getTenantId());
158     }
159
160 }