fix: 积木报表 API 数据集解析时 token 未正确解析的问题
parent
717dd1ab7c
commit
ee454ace9d
|
@ -2,7 +2,7 @@ package cn.iocoder.yudao.framework.common.enums;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Web 过滤器顺序的枚举类,保证过滤器按照符合我们的预期
|
* Web 过滤器顺序的枚举类,保证过滤器按照符合我们的预期
|
||||||
*
|
* <p>
|
||||||
* 考虑到每个 starter 都需要用到该工具类,所以放到 common 模块下的 enums 包下
|
* 考虑到每个 starter 都需要用到该工具类,所以放到 common 模块下的 enums 包下
|
||||||
*
|
*
|
||||||
* @author 芋道源码
|
* @author 芋道源码
|
||||||
|
@ -17,6 +17,8 @@ public interface WebFilterOrderEnum {
|
||||||
|
|
||||||
// OrderedRequestContextFilter 默认为 -105,用于国际化上下文等等
|
// OrderedRequestContextFilter 默认为 -105,用于国际化上下文等等
|
||||||
|
|
||||||
|
int JM_TOKEN_FILTER = -105; // 需要保证在 Spring Security 过滤前面
|
||||||
|
|
||||||
int TENANT_CONTEXT_FILTER = -104; // 需要保证在 ApiAccessLogFilter 前面
|
int TENANT_CONTEXT_FILTER = -104; // 需要保证在 ApiAccessLogFilter 前面
|
||||||
|
|
||||||
int API_ACCESS_LOG_FILTER = -103; // 需要保证在 RequestBodyCacheFilter 后面
|
int API_ACCESS_LOG_FILTER = -103; // 需要保证在 RequestBodyCacheFilter 后面
|
||||||
|
|
|
@ -20,8 +20,6 @@ public class SecurityProperties {
|
||||||
@NotEmpty(message = "Token Header 不能为空")
|
@NotEmpty(message = "Token Header 不能为空")
|
||||||
private String tokenHeader = "Authorization";
|
private String tokenHeader = "Authorization";
|
||||||
|
|
||||||
private String jmTokenHeader = "X-Access-Token";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mock 模式的开关
|
* mock 模式的开关
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -21,7 +21,6 @@ import javax.servlet.ServletException;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Token 过滤器,验证 token 的有效性
|
* Token 过滤器,验证 token 的有效性
|
||||||
|
@ -63,25 +62,6 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 积木请求头
|
|
||||||
String jmTokenHeader = request.getHeader(securityProperties.getJmTokenHeader());
|
|
||||||
if (StrUtil.isNotEmpty(jmTokenHeader)) {
|
|
||||||
try {
|
|
||||||
OAuth2AccessTokenCheckRespDTO accessToken = oauth2TokenApi.checkAccessToken(jmTokenHeader);
|
|
||||||
Optional<LoginUser> optUser = Optional.ofNullable(accessToken)
|
|
||||||
.map(
|
|
||||||
t -> new LoginUser().setId(t.getUserId())
|
|
||||||
.setUserType(t.getUserType())
|
|
||||||
.setTenantId(t.getTenantId())
|
|
||||||
.setScopes(t.getScopes())
|
|
||||||
);
|
|
||||||
if (optUser.isPresent()) {
|
|
||||||
SecurityFrameworkUtils.setLoginUser(optUser.get(), request);
|
|
||||||
}
|
|
||||||
} catch (ServiceException ignored) {
|
|
||||||
// do nothing:如果报错,说明认证失败,忽略即可
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 继续过滤链
|
// 继续过滤链
|
||||||
chain.doFilter(request, response);
|
chain.doFilter(request, response);
|
||||||
|
@ -108,7 +88,7 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 模拟登录用户,方便日常开发调试
|
* 模拟登录用户,方便日常开发调试
|
||||||
* <p>
|
*
|
||||||
* 注意,在线上环境下,一定要关闭该功能!!!
|
* 注意,在线上环境下,一定要关闭该功能!!!
|
||||||
*
|
*
|
||||||
* @param request 请求
|
* @param request 请求
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
package cn.iocoder.yudao.module.visualization.framework.jmreport.config;
|
package cn.iocoder.yudao.module.visualization.framework.jmreport.config;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;
|
||||||
import cn.iocoder.yudao.module.system.api.oauth2.OAuth2TokenApi;
|
import cn.iocoder.yudao.module.system.api.oauth2.OAuth2TokenApi;
|
||||||
import cn.iocoder.yudao.module.visualization.framework.jmreport.core.service.JmReportTokenServiceImpl;
|
import cn.iocoder.yudao.module.visualization.framework.jmreport.core.service.JmReportTokenServiceImpl;
|
||||||
|
import cn.iocoder.yudao.module.visualization.framework.jmreport.core.web.JmReportTokenFilter;
|
||||||
import org.jeecg.modules.jmreport.api.JmReportTokenServiceI;
|
import org.jeecg.modules.jmreport.api.JmReportTokenServiceI;
|
||||||
|
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.ComponentScan;
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
@ -22,4 +25,12 @@ public class JmReportConfiguration {
|
||||||
return new JmReportTokenServiceImpl(oAuth2TokenApi);
|
return new JmReportTokenServiceImpl(oAuth2TokenApi);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
|
||||||
|
public FilterRegistrationBean<JmReportTokenFilter> registerMyAnotherFilter(OAuth2TokenApi oAuth2TokenApi){
|
||||||
|
FilterRegistrationBean<JmReportTokenFilter> bean = new FilterRegistrationBean<>();
|
||||||
|
bean.setOrder(WebFilterOrderEnum.JM_TOKEN_FILTER);
|
||||||
|
bean.setFilter(new JmReportTokenFilter(oAuth2TokenApi));
|
||||||
|
return bean;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,132 @@
|
||||||
|
package cn.iocoder.yudao.module.visualization.framework.jmreport.core.web;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.iocoder.yudao.framework.security.core.LoginUser;
|
||||||
|
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
|
||||||
|
import cn.iocoder.yudao.module.system.api.oauth2.OAuth2TokenApi;
|
||||||
|
import cn.iocoder.yudao.module.system.api.oauth2.dto.OAuth2AccessTokenCheckRespDTO;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
import javax.servlet.Filter;
|
||||||
|
import javax.servlet.FilterChain;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.ServletRequest;
|
||||||
|
import javax.servlet.ServletResponse;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletRequestWrapper;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 积木报表 token 处理,将积木报表请求头中的 token 转换成 spring security 的 auth head
|
||||||
|
*/
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class JmReportTokenFilter implements Filter {
|
||||||
|
/**
|
||||||
|
* 积木 token 请求头
|
||||||
|
*/
|
||||||
|
private static final String JM_TOKEN_HEADER = "X-Access-Token";
|
||||||
|
/**
|
||||||
|
* 系统内置请求头
|
||||||
|
*/
|
||||||
|
private static final String TOKEN_HEADER = "Authorization";
|
||||||
|
/**
|
||||||
|
* auth 相关格式
|
||||||
|
*/
|
||||||
|
private static final String AUTHORIZATION_FORMAT = "Bearer %s";
|
||||||
|
|
||||||
|
private final OAuth2TokenApi oauth2TokenApi;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
|
||||||
|
// 积木请求头
|
||||||
|
HttpServletRequest req = (HttpServletRequest) servletRequest;
|
||||||
|
String token = req.getHeader(JM_TOKEN_HEADER);
|
||||||
|
if (StrUtil.isNotEmpty(token)) {
|
||||||
|
// 1. 增加请求头
|
||||||
|
HeaderMapRequestWrapper requestWrapper = new HeaderMapRequestWrapper(req);
|
||||||
|
requestWrapper.addHeader(TOKEN_HEADER, String.format(AUTHORIZATION_FORMAT, token));
|
||||||
|
|
||||||
|
OAuth2AccessTokenCheckRespDTO resp = oauth2TokenApi.checkAccessToken(token);
|
||||||
|
Optional<LoginUser> optUser = Optional.ofNullable(resp)
|
||||||
|
.map(
|
||||||
|
t -> new LoginUser().setId(t.getUserId())
|
||||||
|
.setUserType(t.getUserType())
|
||||||
|
.setTenantId(t.getTenantId())
|
||||||
|
.setScopes(t.getScopes())
|
||||||
|
);
|
||||||
|
if (optUser.isPresent()) {
|
||||||
|
// 2. 设置登录用户类型
|
||||||
|
WebFrameworkUtils.setLoginUserType(servletRequest, optUser.get().getUserType());
|
||||||
|
filterChain.doFilter(requestWrapper, servletResponse);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
filterChain.doFilter(servletRequest, servletResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* request 包装类,用于修改 head
|
||||||
|
*
|
||||||
|
* <a href="https://stackoverflow.com/questions/2811769/adding-an-http-header-to-the-request-in-a-servlet-filter">add request head</a>
|
||||||
|
*/
|
||||||
|
public class HeaderMapRequestWrapper extends HttpServletRequestWrapper {
|
||||||
|
/**
|
||||||
|
* construct a wrapper for this request
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
*/
|
||||||
|
public HeaderMapRequestWrapper(HttpServletRequest request) {
|
||||||
|
super(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, String> headerMap = new HashMap<String, String>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* add a header with given name and value
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
|
public void addHeader(String name, String value) {
|
||||||
|
headerMap.put(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHeader(String name) {
|
||||||
|
String headerValue = super.getHeader(name);
|
||||||
|
if (headerMap.containsKey(name)) {
|
||||||
|
headerValue = headerMap.get(name);
|
||||||
|
}
|
||||||
|
return headerValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the Header names
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Enumeration<String> getHeaderNames() {
|
||||||
|
List<String> names = Collections.list(super.getHeaderNames());
|
||||||
|
for (String name : headerMap.keySet()) {
|
||||||
|
names.add(name);
|
||||||
|
}
|
||||||
|
return Collections.enumeration(names);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Enumeration<String> getHeaders(String name) {
|
||||||
|
List<String> values = Collections.list(super.getHeaders(name));
|
||||||
|
if (headerMap.containsKey(name)) {
|
||||||
|
values.add(headerMap.get(name));
|
||||||
|
}
|
||||||
|
return Collections.enumeration(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue