积木报表安全配置与扩展开发
本文详细介绍了积木报表的安全配置体系与扩展开发能力。文章涵盖了Spring Security集成与自定义认证机制、API安全过滤与跨域配置、自定义Token服务与租户隔离实现,以及外部服务集成与日志记录扩展等核心内容。通过灵活的配置和自定义扩展,开发者可以构建安全可靠的企业级报表解决方案,满足多租户数据隔离、外部系统集成和精细化权限控制等复杂业务需求。
Spring Security集成与自定义认证
积木报表采用Spring Security作为核心安全框架,提供了完整的认证授权机制。通过灵活的配置和自定义扩展,开发者可以轻松实现企业级的安全需求,包括用户认证、权限控制、会话管理等功能。
安全配置核心架构
积木报表的安全配置采用现代化的Spring Security 5.x+架构,基于Java配置方式实现。核心配置类SpringSecurityConfig负责定义整个安全策略:
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
// 权限配置
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login/login.html")
.loginProcessingUrl("/login")
.successHandler(new CustomLoginSuccessHandler())
.permitAll();
return http.build();
}
}
权限控制策略
积木报表实现了精细化的URL权限控制,通过antMatchers方法定义不同路径的访问规则:
| 路径模式 | 权限设置 | 说明 |
|---|---|---|
/login/** | permitAll() | 登录相关页面完全开放 |
/jmreport/**/cdn/** | permitAll() | 静态资源文件无需认证 |
/jmreport/view/** | 自定义访问控制 | 视图页面支持灵活配置 |
| 其他所有路径 | authenticated() | 需要登录认证 |
自定义登录成功处理器
CustomLoginSuccessHandler类实现了登录成功后的自定义跳转逻辑,确保用户体验的一致性:
关键代码实现:
public class CustomLoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication) {
// 设置会话属性标识登录来源
session.setAttribute("loginFrom", "jimu_example");
// 检查重定向URL是否符合业务规则
if (isValidRedirectUrl(redirectUrl)) {
super.onAuthenticationSuccess(request, response, authentication);
} else {
response.sendRedirect("/");
}
}
}
视图页面访问控制
积木报表提供了灵活的视图页面访问控制机制,通过ViewPageCustomAccess组件实现:
@Component("viewPageCustomAccess")
public class ViewPageCustomAccess {
@Value("${spring.security.open-view-page:false}")
boolean openViewPage = false;
public boolean check(HttpServletRequest request, Authentication authentication) {
// 检查用户认证状态
if (未登录用户) {
// 支持配置开关控制
if (openViewPage) {
return true;
}
// 支持特定参数访问
if (hasAccessParameters(request)) {
return true;
}
return false;
}
return true;
}
}
防火墙配置
为确保系统安全,积木报表配置了严格的HTTP防火墙:
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
StrictHttpFirewall firewall = new StrictHttpFirewall();
// 允许必要的特殊字符
firewall.setAllowBackSlash(true);
firewall.setAllowUrlEncodedDoubleSlash(true);
return (web) -> web.httpFirewall(firewall);
}
会话管理策略
系统采用灵活的会话管理配置,支持不同的业务场景:
| 配置项 | 值 | 说明 |
|---|---|---|
sessionCreationPolicy | ALWAYS | 总是创建会话 |
rememberMe | useSecureCookie(true) | 使用安全Cookie |
invalidateHttpSession | true | 登出时销毁会话 |
clearAuthentication | true | 登出时清除认证信息 |
安全过滤器链
积木报表通过过滤器链实现多层次的安全防护:
关键过滤器配置:
.addFilterBefore(new ApiSecurityConfigFilter(), BasicAuthenticationFilter.class)
配置属性说明
支持通过配置文件灵活调整安全策略:
# 是否开放视图页面访问(无需登录)
spring.security.open-view-page=false
# 会话超时时间(秒)
server.servlet.session.timeout=1800
# 安全Cookie配置
server.servlet.session.cookie.http-only=true
server.servlet.session.cookie.secure=true
扩展开发指南
开发者可以通过以下方式扩展安全功能:
- 自定义UserDetailsService:实现用户信息加载逻辑
- 扩展权限验证:重写
access方法实现复杂权限逻辑 - 集成OAuth2:配置OAuth2客户端实现第三方登录
- 添加审计日志:实现认证事件监听记录安全日志
通过以上配置和扩展机制,积木报表提供了强大而灵活的安全框架,能够满足各种企业级应用的安全需求。
API安全过滤与跨域配置详解
在积木报表的安全体系中,API安全过滤与跨域配置是保障系统安全性和可用性的重要组成部分。通过精细化的安全策略配置,积木报表实现了灵活的访问控制和跨域资源共享机制,为开发者提供了安全可靠的数据可视化服务。
API安全过滤器机制
积木报表通过自定义过滤器实现了API访问的安全控制,核心过滤器包括:
ApiSecurityConfigFilter - 外部登录集成过滤器
public class ApiSecurityConfigFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
String loginFrom = req.getHeader("jm_login_from");
if(null != loginFrom && !loginFrom.isEmpty()){
String springSecurityContext = req.getHeader("jm_spring_security_context");
if(null != springSecurityContext && !springSecurityContext.isEmpty()){
SecurityContextImpl securityContext =
JSONObject.parseObject(springSecurityContext, SecurityContextImpl.class);
HttpSession session = req.getSession();
session.setAttribute("loginFrom", loginFrom);
session.setAttribute("SPRING_SECURITY_CONTEXT", securityContext);
}
}
chain.doFilter(request, response);
}
}
该过滤器实现了外部系统登录集成功能,通过特定的HTTP头信息(jm_login_from和jm_spring_security_context)接收外部系统的安全上下文,实现无缝的单点登录集成。
JimuReportSysSwitchFilter - 系统开关过滤器
系统开关过滤器负责处理系统级别的开关控制,包括功能模块的启用/禁用状态管理,确保系统在不同运行环境下的灵活配置。
Spring Security安全配置
积木报表采用Spring Security框架构建完整的安全体系,主要配置如下:
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
// 静态资源放行
.antMatchers("/jmreport/**/cdn/**",
"/jmreport/desreport_/**/*.js",
"/jmreport/desreport_/**/*.css").permitAll()
// API接口权限控制
.antMatchers("/jmreport/excelQueryByTemplate",
"/jmreport/query/report/folder/template").permitAll()
// 分享页面权限
.antMatchers("/jmreport/shareView/**",
"/jmreport/exportPdfStream").permitAll()
// 视图页面自定义访问控制
.antMatchers("/jmreport/view/**")
.access("@viewPageCustomAccess.check(request,authentication)")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login/login.html")
.loginProcessingUrl("/login")
.successHandler(new CustomLoginSuccessHandler())
.permitAll()
.and()
.addFilterBefore(new ApiSecurityConfigFilter(),
BasicAuthenticationFilter.class);
return http.build();
}
}
跨域配置实现
积木报表通过自定义CORS配置实现灵活的跨域资源共享:
@Configuration
public class CustomCorsConfiguration implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // 所有接口
.allowCredentials(true) // 允许发送Cookie
.allowedOriginPatterns("*") // 支持所有域
.allowedMethods(new String[]{"GET", "POST", "PUT", "DELETE"}) // 支持方法
.allowedHeaders("*")
.exposedHeaders("*");
}
}
该配置提供了完整的跨域支持,包括:
- 支持所有来源域的访问
- 允许携带认证信息(Cookie)
- 支持常见的HTTP方法
- 允许所有请求头和响应头
视图页面访问控制
积木报表提供了灵活的视图页面访问控制机制:
@Component("viewPageCustomAccess")
public class ViewPageCustomAccess {
@Value("${spring.security.open-view-page:false}")
boolean openViewPage = false;
public boolean check(HttpServletRequest request, Authentication authentication) {
Object principal = authentication.getPrincipal();
if (null == principal || "anonymousUser".equalsIgnoreCase(principal.toString())) {
// 未登录用户访问控制
if (openViewPage) {
return true; // 配置文件开放访问
}
// 通过特定参数控制访问
String previousPage = request.getParameter("previousPage");
String jmLink = request.getParameter("jmLink");
return (null != previousPage && null != jmLink);
}
return true; // 已登录用户允许访问
}
}
安全配置流程图
配置参数说明
积木报表的安全配置支持以下关键参数:
| 配置项 | 默认值 | 说明 |
|---|---|---|
spring.security.open-view-page | false | 是否开放视图页面给未登录用户 |
CORS allowCredentials | true | 是否允许发送Cookie |
CORS allowedOriginPatterns | * | 允许的源域模式 |
CORS allowedMethods | GET,POST,PUT,DELETE | 允许的HTTP方法 |
最佳实践建议
- 生产环境配置:建议在生产环境中限制CORS的源域,使用具体的域名而非通配符
- 安全头配置:根据需要配置适当的安全头,如X-Frame-Options、Content-Security-Policy等
- 会话管理:根据业务需求调整会话超时时间和并发控制策略
- 监控日志:启用安全相关的访问日志,便于审计和故障排查
通过这套完整的安全配置体系,积木报表既保证了系统的安全性,又提供了灵活的集成和扩展能力,能够满足各种企业级应用场景的安全需求。
自定义Token服务与租户隔离实现
在企业级报表系统中,安全认证和租户隔离是至关重要的功能。积木报表通过灵活的扩展机制,允许开发者自定义Token服务和租户隔离策略,以满足不同企业的安全需求。
Token服务接口设计
积木报表提供了JmReportTokenServiceI接口,定义了完整的认证体系:
public interface JmReportTokenServiceI {
String getToken(HttpServletRequest request);
String getUsername(String token);
String[] getRoles(String token);
String[] getPermissions(String token);
Boolean verifyToken(String token);
HttpHeaders customApiHeader();
String getTenantId();
}
自定义Token服务实现
以下是一个完整的自定义Token服务实现示例:
@Component
public class JimuReportTokenServiceImpl implements JmReportTokenServiceI {
// 从请求中提取Token
@Override
public String getToken(HttpServletRequest request) {
return TokenUtils.getTokenByRequest(request);
}
// 根据Token获取用户名
@Override
public String getUsername(String token) {
return JwtUtil.getUsername(token);
}
// 获取用户角色
@Override
public String[] getRoles(String token) {
return new String[]{"admin","lowdeveloper","dbadeveloper"};
}
// 获取用户权限
@Override
public String[] getPermissions(String token) {
return new String[]{
"drag:datasource:testConnection",
"onl:drag:clear:recovery",
"drag:analysis:sql",
"drag:design:getTotalData"
};
}
// Token验证
@Override
public Boolean verifyToken(String token) {
return TokenUtils.verifyToken(token, sysBaseAPI, redisUtil);
}
}
租户隔离机制
积木报表支持多租户架构,通过getTenantId()方法实现数据隔离:
@Override
public String getTenantId() {
HttpServletRequest request = JimuSpringContextUtils.getHttpServletRequest();
String tenantId = null;
// 从HTTP Header获取租户ID
if (request != null) {
tenantId = request.getHeader("X-Tenant-ID");
if (StringUtils.isEmpty(tenantId)) {
tenantId = request.getParameter("tenantId");
}
}
return tenantId;
}
多租户数据隔离流程
自定义HTTP请求头
通过customApiHeader()方法可以自定义API请求头,用于传递额外的认证信息:
@Override
public HttpHeaders customApiHeader() {
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer " + getCurrentToken());
headers.add("X-Tenant-ID", getCurrentTenantId());
headers.add("X-Request-ID", UUID.randomUUID().toString());
return headers;
}
权限控制矩阵
积木报表内置了细粒度的权限控制,以下是主要的权限指令:
| 权限指令 | 功能描述 | 适用角色 |
|---|---|---|
drag:datasource:testConnection | 数据库连接测试 | 管理员、开发人员 |
onl:drag:clear:recovery | 清空回收站 | 管理员 |
drag:analysis:sql | SQL解析 | 开发人员 |
drag:design:getTotalData | 获取表单数据 | 所有角色 |
onl:drag:page:delete | 删除页面 | 管理员 |
集成企业认证系统
在实际项目中,通常需要与企业现有的认证系统集成:
@Override
public Boolean verifyToken(String token) {
// 调用企业统一认证服务
AuthResponse response = authClient.verifyToken(token);
if (response.isSuccess()) {
// 缓存用户信息到Redis
redisTemplate.opsForValue().set(
"user:" + response.getUserId(),
response.getUserInfo(),
30, TimeUnit.MINUTES
);
return true;
}
return false;
}
租户数据隔离策略
为了实现真正的多租户数据隔离,需要在数据访问层进行过滤:
-- 报表查询时自动添加租户过滤条件
SELECT * FROM report_template
WHERE tenant_id = :tenantId
AND status = 1;
-- 数据源配置按租户隔离
SELECT * FROM datasource_config
WHERE tenant_id = :tenantId;
安全最佳实践
- Token有效期管理:设置合理的Token过期时间,建议不超过24小时
- HTTPS加密传输:所有认证请求必须使用HTTPS协议
- 防止重放攻击:在Token中加入时间戳和随机数
- 租户数据备份:定期备份各租户数据,防止数据丢失
- 访问日志审计:记录所有敏感操作日志,便于安全审计
错误处理机制
完善的错误处理是安全系统的重要组成部分:
try {
String token = getToken(request);
if (!verifyToken(token)) {
throw new AuthenticationException("Token验证失败");
}
String tenantId = getTenantId();
if (StringUtils.isEmpty(tenantId)) {
throw new TenantNotFoundException("租户标识不能为空");
}
// 执行业务逻辑
return processRequest(request, tenantId);
} catch (AuthenticationException e) {
log.warn("认证失败: {}", e.getMessage());
return ResponseEntity.status(401).body("认证失败");
} catch (TenantNotFoundException e) {
log.warn("租户不存在: {}", e.getMessage());
return ResponseEntity.status(400).body("租户标识无效");
}
通过以上实现,积木报表能够为企业提供安全可靠的多租户报表服务,确保每个租户的数据完全隔离,同时保持系统的灵活性和可扩展性。
外部服务集成与日志记录扩展
积木报表作为一个企业级报表工具,其强大的扩展能力体现在与外部服务的无缝集成和灵活的日志记录机制上。通过合理的配置和扩展开发,可以实现与Redis、数据库、消息队列等外部服务的深度集成,同时建立完善的日志监控体系。
Redis集成配置
积木报表提供了开箱即用的Redis配置支持,通过RedisConfig类实现RedisTemplate的自动配置:
@Configuration
@ConditionalOnMissingBean(RedisTemplate.class)
public class RedisConfig {
@Bean
JedisConnectionFactory jedisConnectionFactory() {
return new JedisConnectionFactory();
}
@Bean
public RedisTemplate<String, Object> redisTemplate() {
log.info("---- jimubi Redis Template init----");
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(jedisConnectionFactory());
return template;
}
}
该配置类使用@ConditionalOnMissingBean注解确保在没有自定义RedisTemplate时才生效,避免了配置冲突。Redis集成主要用于:
- 会话管理:存储用户登录状态和权限信息
- 缓存优化:缓存报表模板和数据查询结果
- 分布式锁:防止并发操作导致的数据不一致
外部服务接口扩展
积木报表通过IOnlDragExternalService接口提供了丰富的外部服务扩展点,开发者可以实现自定义的外部服务集成:
字典服务扩展
字典服务是报表系统中常用的功能,积木报表提供了灵活的字典查询机制:
@Override
public Map<String, List<DragDictModel>> getManyDictItems(List<String> codeList,
List<JSONObject> tableDictList) {
Map<String, List<DragDictModel>> manyDragDictItems = new HashMap<>();
// 处理普通字典
if(!CollectionUtils.isEmpty(codeList)){
Map<String, List<JmDictModel>> dictItemsMap = reportDictService.getManyDictItems(codeList);
dictItemsMap.forEach((k,v)->{
List<DragDictModel> dictItems = new ArrayList<>();
v.forEach(dictItem->{
DragDictModel dictModel = new DragDictModel();
BeanUtils.copyProperties(dictItem, dictModel);
dictItems.add(dictModel);
});
manyDragDictItems.put(k, dictItems);
});
}
// 处理表字典
if(!CollectionUtils.isEmpty(tableDictList)){
tableDictList.forEach(item->{
JSONObject object = JSONObject.parseObject(item.toString());
String dictField = object.getString("dictField");
String dictTable = object.getString("dictTable");
String dictText = object.getString("dictText");
String fieldName = object.getString("fieldName");
List<JmDictModel> dictItemsList = reportDictService.queryTableDictItemsByCode(
dictTable, dictText, dictField);
List<DragDictModel> dictItems = new ArrayList<>();
dictItemsList.forEach(dictItem->{
DragDictModel dictModel = new DragDictModel();
BeanUtils.copyProperties(dictItem, dictModel);
dictItems.add(dictModel);
});
manyDragDictItems.put(fieldName, dictItems);
});
}
return manyDragDictItems;
}
日志记录扩展框架
积木报表提供了完善的日志记录扩展机制,支持多种日志类型和操作类型的记录:
| 日志类型 | 操作类型 | 描述 |
|---|---|---|
| 系统日志 | 1-创建 | 记录系统级操作 |
| 业务日志 | 2-修改 | 记录业务数据变更 |
| 安全日志 | 3-删除 | 记录安全相关操作 |
| 性能日志 | 4-查询 | 记录性能指标数据 |
日志记录接口定义
/**
* 添加结构化日志
* @param dragLogDTO 日志数据传输对象
*/
@Override
public void addLog(DragLogDTO dragLogDTO) {
// 实现自定义日志记录逻辑
// 可以集成ELK、Splunk等日志系统
}
/**
* 保存简单日志
* @param logMsg 日志消息
* @param logType 日志类型
* @param operateType 操作类型
*/
@Override
public void addLog(String logMsg, int logType, int operateType) {
// 实现自定义日志记录逻辑
}
自定义日志实现示例
以下是一个完整的自定义日志服务实现示例:
@Service
public class CustomLogServiceImpl implements IOnlDragExternalService {
@Autowired
private LogRepository logRepository;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final String LOG_QUEUE_KEY = "jimureport:log:queue";
@Override
public void addLog(DragLogDTO dragLogDTO) {
// 异步记录日志到Redis队列
redisTemplate.opsForList().rightPush(LOG_QUEUE_KEY, dragLogDTO);
// 批量处理日志到数据库
processLogQueue();
}
@Override
public void addLog(String logMsg, int logType, int operateType) {
DragLogDTO logDTO = new DragLogDTO();
logDTO.setLogContent(logMsg);
logDTO.setLogType(logType);
logDTO.setOperateType(operateType);
logDTO.setCreateTime(new Date());
addLog(logDTO);
}
@Scheduled(fixedDelay = 5000)
private void processLogQueue() {
List<Object> logs = redisTemplate.opsForList().range(LOG_QUEUE_KEY, 0, -1);
if (logs != null && !logs.isEmpty()) {
List<SystemLog> systemLogs = logs.stream()
.filter(log -> log instanceof DragLogDTO)
.map(log -> convertToSystemLog((DragLogDTO) log))
.collect(Collectors.toList());
logRepository.saveAll(systemLogs);
redisTemplate.delete(LOG_QUEUE_KEY);
}
}
private SystemLog convertToSystemLog(DragLogDTO dragLogDTO) {
SystemLog systemLog = new SystemLog();
// 转换逻辑
return systemLog;
}
}
外部服务集成最佳实践
- 连接池配置:合理配置数据库和Redis连接池参数,避免资源耗尽
- 异常处理:实现完善的异常处理机制,确保服务稳定性
- 性能监控:集成APM工具监控外部服务调用性能
- 熔断机制:实现服务熔断,防止级联故障
通过以上扩展机制,积木报表能够灵活集成各种外部服务,构建稳定可靠的企业级报表解决方案。开发者可以根据实际业务需求,实现自定义的外部服务集成和日志记录功能。
总结
积木报表提供了完整而灵活的安全框架和扩展机制,能够满足企业级应用的各种安全需求。从基础的Spring Security集成到复杂的多租户隔离,从API安全过滤到外部服务集成,积木报表都提供了丰富的配置选项和扩展点。通过自定义Token服务、灵活的权限控制策略和完善的日志记录机制,开发者可以构建出安全可靠、易于维护的报表系统。这套安全体系既保证了数据的安全性,又提供了良好的扩展性,是企业级报表项目的理想选择。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



