mirror of
https://github.com/jeecgboot/JimuReport.git
synced 2025-10-15 15:30:23 +00:00
升级springboot3、权限采用sa-token
This commit is contained in:
10
README.md
10
README.md
@@ -40,17 +40,17 @@ v2.1.3 | 2025-09-05
|
|||||||
|
|
||||||
快速集成积木报表
|
快速集成积木报表
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
> 快速集成到自己项目中,支持SpringBoot脚手架项目,springboot2依赖采用jdk8编译,支持jdk8、jdk17、jdk21+。
|
> 快速集成到自己项目中,支持SpringBoot3脚手架项目(要求jdk17+),springboot2版本要求jdk17+
|
||||||
|
|
||||||
#### 第一步:引入积木报表依赖
|
#### 第一步:引入积木报表依赖
|
||||||
|
|
||||||
|
|
||||||
- springboot2
|
- springboot3
|
||||||
|
|
||||||
```
|
```
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.jeecgframework.jimureport</groupId>
|
<groupId>org.jeecgframework.jimureport</groupId>
|
||||||
<artifactId>jimureport-spring-boot-starter</artifactId>
|
<artifactId>jimureport-spring-boot3-starter</artifactId>
|
||||||
<version>2.1.3</version>
|
<version>2.1.3</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- mongo、redis和文件数据集支持包,按需引入 -->
|
<!-- mongo、redis和文件数据集支持包,按需引入 -->
|
||||||
@@ -67,12 +67,12 @@ v2.1.3 | 2025-09-05
|
|||||||
</dependency>
|
</dependency>
|
||||||
```
|
```
|
||||||
|
|
||||||
- springboot3
|
- springboot2
|
||||||
|
|
||||||
```
|
```
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.jeecgframework.jimureport</groupId>
|
<groupId>org.jeecgframework.jimureport</groupId>
|
||||||
<artifactId>jimureport-spring-boot3-starter-fastjson2</artifactId>
|
<artifactId>jimureport-spring-boot-starter</artifactId>
|
||||||
<version>2.1.3</version>
|
<version>2.1.3</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- mongo、redis和文件数据集支持包,按需引入 -->
|
<!-- mongo、redis和文件数据集支持包,按需引入 -->
|
||||||
|
@@ -8,6 +8,14 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
环境要求
|
||||||
|
-----------------------------------
|
||||||
|
|
||||||
|
- 要求jdk17+(本项目springboot3架构)
|
||||||
|
- 要求mysql5.7+ 手工执行db/jimureport.mysql5.7.create.sql,会自动创建库jimureport
|
||||||
|
- 要求redis
|
||||||
|
- 项目配置:src/main/resources/application-dev.yml
|
||||||
|
|
||||||
|
|
||||||
使用步骤
|
使用步骤
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
|
@@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-parent</artifactId>
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
<version>2.7.18</version>
|
<version>3.5.5</version>
|
||||||
<relativePath/>
|
<relativePath/>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
@@ -45,12 +45,12 @@
|
|||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<java.version>1.8</java.version>
|
<java.version>17</java.version>
|
||||||
<minio.version>8.0.3</minio.version>
|
<minio.version>8.0.3</minio.version>
|
||||||
<!-- DB驱动 -->
|
<!-- DB驱动 -->
|
||||||
<mysql-connector-java.version>8.0.27</mysql-connector-java.version>
|
<mysql-connector-java.version>8.0.27</mysql-connector-java.version>
|
||||||
<!-- SQL解析引擎 -->
|
<!-- SQL解析引擎 -->
|
||||||
<jsqlparser.version>4.6</jsqlparser.version>
|
<jsqlparser.version>4.9</jsqlparser.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@
|
|||||||
<!-- 积木报表 -->
|
<!-- 积木报表 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.jeecgframework.jimureport</groupId>
|
<groupId>org.jeecgframework.jimureport</groupId>
|
||||||
<artifactId>jimureport-spring-boot-starter</artifactId>
|
<artifactId>jimureport-spring-boot3-starter</artifactId>
|
||||||
<version>2.1.3</version>
|
<version>2.1.3</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- mongo、redis和文件数据集支持包,按需引入 -->
|
<!-- mongo、redis和文件数据集支持包,按需引入 -->
|
||||||
@@ -82,8 +82,8 @@
|
|||||||
<!-- 积木BI大屏 -->
|
<!-- 积木BI大屏 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.jeecgframework.jimureport</groupId>
|
<groupId>org.jeecgframework.jimureport</groupId>
|
||||||
<artifactId>jimubi-spring-boot-starter</artifactId>
|
<artifactId>jimubi-spring-boot3-starter</artifactId>
|
||||||
<version>2.1.3</version>
|
<version>2.1.3.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- jsqlparser-->
|
<!-- jsqlparser-->
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -92,7 +92,6 @@
|
|||||||
<version>${jsqlparser.version}</version>
|
<version>${jsqlparser.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
<!-- SpringBoot-->
|
<!-- SpringBoot-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
@@ -103,10 +102,22 @@
|
|||||||
<artifactId>spring-boot-starter-freemarker</artifactId>
|
<artifactId>spring-boot-starter-freemarker</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!--spring security-->
|
<!-- Sa-Token 权限认证,在线文档:https://sa-token.cc -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>cn.dev33</groupId>
|
||||||
<artifactId>spring-boot-starter-security</artifactId>
|
<artifactId>sa-token-spring-boot3-starter</artifactId>
|
||||||
|
<version>1.44.0</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- Sa-Token整合 Redis (使用jackson序列化方式) -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.dev33</groupId>
|
||||||
|
<artifactId>sa-token-redis-jackson</artifactId>
|
||||||
|
<version>1.44.0</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- 提供Redis连接池 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-pool2</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- minio oss-->
|
<!-- minio oss-->
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
package com.jeecg.modules;
|
package com.jeecg;
|
||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
|
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
|
||||||
import org.springframework.context.ConfigurableApplicationContext;
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
@@ -10,8 +9,7 @@ import org.springframework.core.env.Environment;
|
|||||||
/**
|
/**
|
||||||
* 积木报表独立服务启动类
|
* 积木报表独立服务启动类
|
||||||
*/
|
*/
|
||||||
@SpringBootApplication(scanBasePackages = {"org.jeecg", "com.jeecg"})
|
@SpringBootApplication(scanBasePackages = {"org.jeecg", "com.jeecg"}, exclude = {MongoAutoConfiguration.class})
|
||||||
@EnableAutoConfiguration(exclude={MongoAutoConfiguration.class})
|
|
||||||
public class JimuReportApplication {
|
public class JimuReportApplication {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
@@ -24,6 +22,7 @@ public class JimuReportApplication {
|
|||||||
// 默认编码不是UTF-8设置为UTF-8
|
// 默认编码不是UTF-8设置为UTF-8
|
||||||
System.setProperty("file.encoding", "UTF-8");
|
System.setProperty("file.encoding", "UTF-8");
|
||||||
}
|
}
|
||||||
|
|
||||||
System.out.println("\n----------------------------------------------------------\n\t" +
|
System.out.println("\n----------------------------------------------------------\n\t" +
|
||||||
"JimuReport 积木报表平台 is running! Access URL:\n\t" +
|
"JimuReport 积木报表平台 is running! Access URL:\n\t" +
|
||||||
"报表工作台: \t\thttp://localhost:" + port + path + "/jmreport/list\n\t" +
|
"报表工作台: \t\thttp://localhost:" + port + path + "/jmreport/list\n\t" +
|
@@ -1,35 +0,0 @@
|
|||||||
package com.jeecg.modules.jmreport.config;
|
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSONObject;
|
|
||||||
import org.jeecg.modules.jmreport.common.util.OkConvertUtils;
|
|
||||||
import org.springframework.security.core.context.SecurityContextImpl;
|
|
||||||
|
|
||||||
import javax.servlet.*;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpSession;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Description: api访问权限过滤器
|
|
||||||
*
|
|
||||||
* @author: wangshuai
|
|
||||||
* @date: 2024/9/25 下午6:22
|
|
||||||
*/
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,18 +0,0 @@
|
|||||||
package com.jeecg.modules.jmreport.config;
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
|
||||||
|
|
||||||
@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("*");
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,53 +0,0 @@
|
|||||||
package com.jeecg.modules.jmreport.config;
|
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.security.core.Authentication;
|
|
||||||
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
|
|
||||||
import org.springframework.security.web.savedrequest.SavedRequest;
|
|
||||||
|
|
||||||
import javax.servlet.ServletException;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
import javax.servlet.http.HttpSession;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.URI;
|
|
||||||
import java.net.URISyntaxException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 自定义springsecurity登录成功处理
|
|
||||||
* [TV360X-1884]jimureport-example集成简单的 spring security,设置登录账号密码
|
|
||||||
* @author chenrui
|
|
||||||
* @date 2024/8/2 16:26
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
public class CustomLoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
|
|
||||||
Authentication authentication) throws IOException, ServletException {
|
|
||||||
HttpSession session = request.getSession();
|
|
||||||
session.setAttribute("loginFrom", "jimu_example");
|
|
||||||
|
|
||||||
SavedRequest savedRequest = (SavedRequest) session.getAttribute("SPRING_SECURITY_SAVED_REQUEST");
|
|
||||||
String redirectUrl = savedRequest != null ? savedRequest.getRedirectUrl() : null;
|
|
||||||
|
|
||||||
if (redirectUrl != null) {
|
|
||||||
try {
|
|
||||||
URI uri = new URI(redirectUrl);
|
|
||||||
String path = uri.getPath(); // 提取路径部分
|
|
||||||
|
|
||||||
if (path != null && path.startsWith("/jmreport")) {
|
|
||||||
// 路径符合要求,执行默认跳转
|
|
||||||
super.onAuthenticationSuccess(request, response, authentication);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch (URISyntaxException e) {
|
|
||||||
// URL解析失败,打印日志后继续执行默认跳转
|
|
||||||
log.error(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 不符合 /jmreport 开头,或 redirectUrl 为 null 或异常
|
|
||||||
response.sendRedirect("/");
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,61 +0,0 @@
|
|||||||
package com.jeecg.modules.jmreport.config;
|
|
||||||
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import javax.servlet.*;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpSession;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 积木报表子系统切换处理类
|
|
||||||
* @author chenrui
|
|
||||||
* @date 2024/10/31 11:33
|
|
||||||
*/
|
|
||||||
@Component("jimuReportSwitchSysHandler")
|
|
||||||
public class JimuReportSysSwitchFilter implements Filter {
|
|
||||||
|
|
||||||
// 包含积木报表
|
|
||||||
boolean includeJimuReport = false;
|
|
||||||
// 包含积木仪表盘
|
|
||||||
boolean includeJimuDrag = false;
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void init(FilterConfig filterConfig) throws ServletException {
|
|
||||||
Filter.super.init(filterConfig);
|
|
||||||
// 判断是否包含积木报表
|
|
||||||
try {
|
|
||||||
Class.forName("org.jeecg.modules.jmreport.config.JmReportBaseConfig");
|
|
||||||
includeJimuReport = true;
|
|
||||||
} catch (ClassNotFoundException ignored) {
|
|
||||||
}
|
|
||||||
// 判断是否包含积木仪表盘
|
|
||||||
try {
|
|
||||||
Class.forName("org.jeecg.modules.drag.service.IOnlDragCompService");
|
|
||||||
includeJimuDrag = true;
|
|
||||||
} catch (ClassNotFoundException ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
|
||||||
if (request instanceof HttpServletRequest) {
|
|
||||||
HttpServletRequest httpRequest = (HttpServletRequest) request;
|
|
||||||
HttpSession session = httpRequest.getSession();
|
|
||||||
if(includeJimuReport) {
|
|
||||||
session.setAttribute("switchJimuReport", "true");
|
|
||||||
}else{
|
|
||||||
session.setAttribute("switchJimuReport", "false");
|
|
||||||
}
|
|
||||||
if(includeJimuDrag) {
|
|
||||||
session.setAttribute("switchJimuDrag", "true");
|
|
||||||
}else {
|
|
||||||
session.setAttribute("switchJimuDrag", "false");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
chain.doFilter(request, response);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@@ -1,111 +0,0 @@
|
|||||||
package com.jeecg.modules.jmreport.config;
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
|
||||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
|
|
||||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
|
||||||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
|
|
||||||
import org.springframework.security.web.firewall.StrictHttpFirewall;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* spring security 配置
|
|
||||||
* [TV360X-1884]jimureport-example集成简单的 spring security,设置登录账号密码
|
|
||||||
* @Author chenrui
|
|
||||||
* @Date 2024-07-23
|
|
||||||
*/
|
|
||||||
@Configuration
|
|
||||||
@EnableWebSecurity
|
|
||||||
public class SpringSecurityConfig {
|
|
||||||
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
|
||||||
http.csrf().disable()
|
|
||||||
.authorizeRequests()
|
|
||||||
.antMatchers("/login/**").permitAll()
|
|
||||||
// 放过静态资源
|
|
||||||
.antMatchers("/jmreport/**/cdn/**",
|
|
||||||
"/jmreport/desreport_/**/*.js",
|
|
||||||
"/jmreport/desreport_/**/*.css",
|
|
||||||
"/jmreport/desreport_/**/*.ico",
|
|
||||||
"/jmreport/desreport_/**/*.png").permitAll()
|
|
||||||
// 放过静态资源-仪表盘
|
|
||||||
.antMatchers("/drag/lib/**/*.css",
|
|
||||||
"/drag/lib/**/*.js",
|
|
||||||
"/drag/lib/**/*.png",
|
|
||||||
"/drag/**/*.ico").permitAll()
|
|
||||||
// 不需要登录的接口
|
|
||||||
.antMatchers("/jmreport/excelQueryByTemplate",
|
|
||||||
"/jmreport/query/report/folder/template",
|
|
||||||
"/jmreport/img/**",
|
|
||||||
"/jmreport/download/image",
|
|
||||||
"/jmreport/verificationToken",
|
|
||||||
"/jmreport/link/queryByIds",
|
|
||||||
"/jmreport/test/getUserMsg",
|
|
||||||
"/jmreport/test/getOrder",
|
|
||||||
"/jimureport/test/**",
|
|
||||||
"/jmreport/auto/export/download/**",
|
|
||||||
"/jmreport/auto/export",
|
|
||||||
"/jmreport/qurestSql",
|
|
||||||
"/jmreport/qurestApi").permitAll()
|
|
||||||
// 分享页面
|
|
||||||
.antMatchers("/jmreport/shareView/**",
|
|
||||||
"/jmreport/exportPdfStream",
|
|
||||||
"/jmreport/exportAllExcelStream",
|
|
||||||
"/jmreport/checkParam/**",
|
|
||||||
"/jmreport/share/verification",
|
|
||||||
"/jmreport/getQueryInfo",
|
|
||||||
"/jmreport/show",
|
|
||||||
"/jmreport/form/submit",
|
|
||||||
"/jmreport/form/repeat/check/**",
|
|
||||||
"/jmreport/exportReport",
|
|
||||||
"/jmreport/dictCodeSearch",
|
|
||||||
"/jmreport/query/multiple/initValue",
|
|
||||||
"/jmreport/addViewCount/**").permitAll()
|
|
||||||
// 仪表盘分享页面
|
|
||||||
.antMatchers("/jimubi/share/view/**",
|
|
||||||
"/drag/share/view/**",
|
|
||||||
"/drag/page/queryById",
|
|
||||||
"/drag/page/addVisitsNumber",
|
|
||||||
"/drag/page/queryTemplateList",
|
|
||||||
"/drag/onlDragDatasetHead/getAllChartData",
|
|
||||||
"/drag/onlDragDatasetHead/getTotalData",
|
|
||||||
"/drag/onlDragDatasetHead/getDictByCodes",
|
|
||||||
"/drag/mock/json/**",
|
|
||||||
"/drag/getImageBase64/**").permitAll()
|
|
||||||
// view页面
|
|
||||||
.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)
|
|
||||||
.logout()
|
|
||||||
.invalidateHttpSession(true)
|
|
||||||
.clearAuthentication(true).permitAll();
|
|
||||||
// 开放iframe访问限制
|
|
||||||
http.headers().frameOptions().disable();
|
|
||||||
// 禁用默认的 no-cache
|
|
||||||
http.headers().cacheControl().disable();
|
|
||||||
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.ALWAYS);
|
|
||||||
http.rememberMe().useSecureCookie(true);
|
|
||||||
|
|
||||||
return http.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public WebSecurityCustomizer webSecurityCustomizer() {
|
|
||||||
StrictHttpFirewall firewall = new StrictHttpFirewall();
|
|
||||||
// 允许反斜杠
|
|
||||||
firewall.setAllowBackSlash(true);
|
|
||||||
// 允许双反斜杠
|
|
||||||
firewall.setAllowUrlEncodedDoubleSlash(true);
|
|
||||||
return (web) -> web.httpFirewall(firewall);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,43 +0,0 @@
|
|||||||
package com.jeecg.modules.jmreport.config;
|
|
||||||
|
|
||||||
import org.jeecg.modules.jmreport.common.util.OkConvertUtils;
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.security.core.Authentication;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 自定义view页面access处理
|
|
||||||
* for: [TV360X-2206] 目前这个版本必须登录后才能看报表,如何设置不登录也能查看报表 #2919
|
|
||||||
* @author chenrui
|
|
||||||
* @date 2024/8/23 14:28
|
|
||||||
*/
|
|
||||||
@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 || principal.toString().isEmpty() || "anonymousUser".equalsIgnoreCase(principal.toString())) {
|
|
||||||
// 未登录
|
|
||||||
if (openViewPage) {
|
|
||||||
// 配置文件设置了开放view页面
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
HttpServletRequest httpRequest = (HttpServletRequest) request;
|
|
||||||
String previousPage = httpRequest.getParameter("previousPage");
|
|
||||||
String jmLink = httpRequest.getParameter("jmLink");
|
|
||||||
if (null != previousPage && !previousPage.isEmpty()
|
|
||||||
&& null != jmLink && !jmLink.isEmpty()) {
|
|
||||||
// 参数中有previousPage和jmLink
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@@ -1,21 +0,0 @@
|
|||||||
package com.jeecg.modules.jmreport.controller;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.stereotype.Controller;
|
|
||||||
import org.springframework.ui.Model;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 积木报表-设置默认首页跳转
|
|
||||||
*/
|
|
||||||
@Controller
|
|
||||||
public class IndexController {
|
|
||||||
private Logger logger = LoggerFactory.getLogger(IndexController.class);
|
|
||||||
|
|
||||||
@GetMapping("/")
|
|
||||||
public String index(Model model) {
|
|
||||||
model.addAttribute("name", "jimureport");
|
|
||||||
return "jmreport/list"; // 视图重定向 - 跳转
|
|
||||||
}
|
|
||||||
}
|
|
@@ -0,0 +1,89 @@
|
|||||||
|
package com.jeecg.modules.jmreport.controller;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
|
import com.jeecg.modules.jmreport.satoken.config.SecurityConfig;
|
||||||
|
import com.jeecg.modules.jmreport.satoken.util.AjaxRequestUtils;
|
||||||
|
import jakarta.servlet.http.HttpSession;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.ui.Model;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 积木报表-设置默认首页跳转
|
||||||
|
*/
|
||||||
|
@Controller
|
||||||
|
public class LoginController {
|
||||||
|
private Logger logger = LoggerFactory.getLogger(LoginController.class);
|
||||||
|
public final static String LOGIN_PAGE = "/login/login.html";
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
SecurityConfig securityConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录请求
|
||||||
|
*
|
||||||
|
* @param username
|
||||||
|
* @param password
|
||||||
|
* @param req
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@GetMapping("/doLogin")
|
||||||
|
public String login(@RequestParam String username, @RequestParam String password, jakarta.servlet.http.HttpServletRequest req) {
|
||||||
|
logger.info("登录请求,用户名:" + username + ",密码:" + password);
|
||||||
|
// 此处通过yml配置文件获取登录账号和密码,实际项目中请从数据库读取进行验证
|
||||||
|
if (securityConfig.getUser().getName().equals(username) && securityConfig.getUser().getPassword().equals(password)) {
|
||||||
|
StpUtil.login(securityConfig.getUser().getName());
|
||||||
|
logger.info("登录成功,当前会话tokeName={}, tokenValue={}", StpUtil.getTokenName(), StpUtil.getTokenValue());
|
||||||
|
|
||||||
|
// 设置登录来源,方便退出登录时区分
|
||||||
|
AjaxRequestUtils.setLoginSessionInfo();
|
||||||
|
return "jmreport/list";
|
||||||
|
}else{
|
||||||
|
logger.error("登录失败,用户名或密码错误");
|
||||||
|
// 返回密码错误提示,需进行URL编码
|
||||||
|
return "redirect:" + LOGIN_PAGE + "?error=1";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 首页跳转
|
||||||
|
*
|
||||||
|
* @param model
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@GetMapping("/")
|
||||||
|
public String index(Model model) {
|
||||||
|
model.addAttribute("name", "jimureport");
|
||||||
|
return "jmreport/list"; // 视图重定向 - 跳转
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询登录状态
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@RequestMapping("/isLogin")
|
||||||
|
public String isLogin() {
|
||||||
|
logger.info("查询登录状态:{}", StpUtil.getTokenInfo());
|
||||||
|
return "当前会话是否登录:" + StpUtil.isLogin();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退出登录
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@RequestMapping("/logout")
|
||||||
|
public String logout() {
|
||||||
|
StpUtil.logout();
|
||||||
|
return "redirect:" + LOGIN_PAGE;
|
||||||
|
}
|
||||||
|
}
|
@@ -30,9 +30,8 @@ import java.util.Map;
|
|||||||
@Component
|
@Component
|
||||||
public class JimuDragExternalServiceImpl implements IOnlDragExternalService {
|
public class JimuDragExternalServiceImpl implements IOnlDragExternalService {
|
||||||
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
@Lazy
|
@Lazy
|
||||||
|
@Autowired
|
||||||
private IJimuReportDictService reportDictService;
|
private IJimuReportDictService reportDictService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -1,21 +1,31 @@
|
|||||||
package com.jeecg.modules.jmreport.extend;
|
package com.jeecg.modules.jmreport.extend;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.context.SaHolder;
|
||||||
|
import cn.dev33.satoken.exception.NotLoginException;
|
||||||
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
|
import com.jeecg.modules.jmreport.satoken.config.SecurityConfig;
|
||||||
|
import com.jeecg.modules.jmreport.satoken.util.AjaxRequestUtils;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.jeecg.modules.jmreport.api.JmReportTokenServiceI;
|
import org.jeecg.modules.jmreport.api.JmReportTokenServiceI;
|
||||||
import org.jeecg.modules.jmreport.common.constant.JmConst;
|
import org.jeecg.modules.jmreport.common.constant.JmConst;
|
||||||
|
import org.jeecg.modules.jmreport.common.expetion.JimuReportException;
|
||||||
import org.jeecg.modules.jmreport.common.util.JimuSpringContextUtils;
|
import org.jeecg.modules.jmreport.common.util.JimuSpringContextUtils;
|
||||||
import org.jeecg.modules.jmreport.common.util.OkConvertUtils;
|
import org.jeecg.modules.jmreport.common.util.OkConvertUtils;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 自定义积木报表鉴权(如果不进行自定义,则所有请求不做权限控制)
|
* 自定义积木报表鉴权(如果不进行自定义,则所有请求不做权限控制)
|
||||||
* 1.自定义获取登录token
|
* 1.自定义获取登录token
|
||||||
* 2.自定义获取登录用户
|
* 2.自定义获取登录用户
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
@Component
|
@Component
|
||||||
public class JimuReportTokenServiceImpl implements JmReportTokenServiceI {
|
public class JimuReportTokenServiceImpl implements JmReportTokenServiceI {
|
||||||
|
@Autowired
|
||||||
|
SecurityConfig securityConfig;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通过请求获取Token
|
* 通过请求获取Token
|
||||||
@@ -24,9 +34,15 @@ public class JimuReportTokenServiceImpl implements JmReportTokenServiceI {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String getToken(HttpServletRequest request) {
|
public String getToken(HttpServletRequest request) {
|
||||||
//System.out.println("---------call---------getToken-----------------------");
|
String token = StpUtil.getTokenValue();
|
||||||
//return TokenUtils.getTokenByRequest(request);
|
log.debug("------SA--TOKEN-----RequestPath={} ,GET Token = {}", SaHolder.getRequest().getRequestPath(), token);
|
||||||
return "123456";
|
if(StringUtils.isEmpty(token)){
|
||||||
|
token = request.getParameter("token");
|
||||||
|
// 将URL上的token设置到SaToken上下文,方便后续操作
|
||||||
|
log.info("------SA--Init--TOKEN-----RequestPath={} ,从URL参数获取Token = {}", SaHolder.getRequest().getRequestPath(), token);
|
||||||
|
StpUtil.setTokenValue(token);
|
||||||
|
}
|
||||||
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -36,8 +52,9 @@ public class JimuReportTokenServiceImpl implements JmReportTokenServiceI {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String getUsername(String token) {
|
public String getUsername(String token) {
|
||||||
// return JwtUtil.getUsername(token);
|
String username = StpUtil.getLoginIdAsString();
|
||||||
return "admin";
|
log.debug("------SA--TOKEN-----RequestPath={} ,Token={} , LoginId={}", SaHolder.getRequest().getRequestPath(), token, username);
|
||||||
|
return username;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -75,25 +92,46 @@ public class JimuReportTokenServiceImpl implements JmReportTokenServiceI {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Boolean verifyToken(String token) {
|
public Boolean verifyToken(String token) {
|
||||||
//System.out.println("---------verify-----Token---------------");
|
try {
|
||||||
//return TokenUtils.verifyToken(token, sysBaseAPI, redisUtil);
|
if(securityConfig.getEnable()!=null && !securityConfig.getEnable()){
|
||||||
|
// 如果security.enable=false,则不进行登录校验
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
StpUtil.checkLogin();
|
||||||
|
log.debug("--SaToken verifyToken-成功!RequestPath={},Token = {}", SaHolder.getRequest().getRequestPath(), token);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("Token校验失败: token = {},error:{}", token, e.getMessage());
|
||||||
|
|
||||||
|
if(e instanceof NotLoginException){
|
||||||
|
// 跳转登录页面
|
||||||
|
try {
|
||||||
|
if(!AjaxRequestUtils.isAjaxRequest(JimuSpringContextUtils.getHttpServletRequest())){
|
||||||
|
JimuSpringContextUtils.getHttpServletResponse().sendRedirect("/login/login.html");
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}else{
|
||||||
|
throw new JimuReportException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* 自定义请求头
|
// * 自定义请求头
|
||||||
* @return
|
// * @return
|
||||||
*/
|
// */
|
||||||
@Override
|
// @Override
|
||||||
public HttpHeaders customApiHeader() {
|
// public HttpHeaders customApiHeader() {
|
||||||
HttpHeaders header = new HttpHeaders();
|
// HttpHeaders header = new HttpHeaders();
|
||||||
header.add("custom-header1", "Please set a custom value 1");
|
// header.add("custom-header1", "Please set a custom value 1");
|
||||||
header.add("token", "token value 2");
|
// header.add("token", "token value 2");
|
||||||
return header;
|
// return header;
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 自定义获取租户
|
* 自定义租户
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@@ -110,7 +148,6 @@ public class JimuReportTokenServiceImpl implements JmReportTokenServiceI {
|
|||||||
headerTenantId = request.getParameter(JmConst.TENANT_ID);
|
headerTenantId = request.getParameter(JmConst.TENANT_ID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//return headerTenantId;
|
return headerTenantId;
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -0,0 +1,85 @@
|
|||||||
|
package com.jeecg.modules.jmreport.satoken;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.context.model.SaRequest;
|
||||||
|
import cn.dev33.satoken.session.SaSession;
|
||||||
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
|
import com.jeecg.modules.jmreport.controller.LoginController;
|
||||||
|
import com.jeecg.modules.jmreport.satoken.util.AjaxRequestUtils;
|
||||||
|
import jakarta.servlet.http.HttpSession;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.context.SaHolder;
|
||||||
|
import cn.dev33.satoken.filter.SaServletFilter;
|
||||||
|
import cn.dev33.satoken.interceptor.SaInterceptor;
|
||||||
|
import cn.dev33.satoken.util.SaResult;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [Sa-Token 权限认证] 配置类
|
||||||
|
*
|
||||||
|
* @author click33
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class SaTokenConfigure implements WebMvcConfigurer {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册 Sa-Token 拦截器打开注解鉴权功能
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void addInterceptors(InterceptorRegistry registry) {
|
||||||
|
// 注册 Sa-Token 拦截器打开注解鉴权功能
|
||||||
|
registry.addInterceptor(new SaInterceptor()).addPathPatterns("/**");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册 [Sa-Token 全局过滤器]
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public SaServletFilter getSaServletFilter() {
|
||||||
|
return new SaServletFilter()
|
||||||
|
|
||||||
|
// 指定 [拦截路由] 与 [放行路由]
|
||||||
|
.addInclude("/**")
|
||||||
|
.addExclude("/favicon.ico")
|
||||||
|
.addExclude("/login/**")
|
||||||
|
.addExclude("/doLogin")
|
||||||
|
|
||||||
|
|
||||||
|
// 认证函数: 每次请求执行
|
||||||
|
.setAuth(obj -> {
|
||||||
|
// 设置登录来源,方便退出登录时区分
|
||||||
|
AjaxRequestUtils.setLoginSessionInfo();
|
||||||
|
|
||||||
|
// System.out.println("---------- sa全局认证,path = " + SaHolder.getRequest().getRequestPath());
|
||||||
|
// System.out.println("---------- sa全局认证,token = " + StpUtil.getTokenValue());
|
||||||
|
})
|
||||||
|
|
||||||
|
// 异常处理函数:每次认证函数发生异常时执行此函数
|
||||||
|
.setError(e -> {
|
||||||
|
System.out.println("---------- sa全局异常,path = " + SaHolder.getRequest().getRequestPath());
|
||||||
|
System.out.println("---------- sa全局认证,token = " + StpUtil.getTokenValue());
|
||||||
|
e.printStackTrace();
|
||||||
|
return SaResult.error(e.getMessage());
|
||||||
|
})
|
||||||
|
|
||||||
|
// 前置函数:在每次认证函数之前执行(BeforeAuth 不受 includeList 与 excludeList 的限制,所有请求都会进入)
|
||||||
|
.setBeforeAuth(r -> {
|
||||||
|
// ---------- 设置一些安全响应头 ----------
|
||||||
|
SaHolder.getResponse()
|
||||||
|
// 服务器名称
|
||||||
|
.setServer("sa-server")
|
||||||
|
// 是否可以在iframe显示视图: DENY=不可以 | SAMEORIGIN=同域下可以 | ALLOW-FROM uri=指定域名下可以
|
||||||
|
.setHeader("X-Frame-Options", "SAMEORIGIN")
|
||||||
|
// 是否启用浏览器默认XSS防护: 0=禁用 | 1=启用 | 1; mode=block 启用, 并在检查到XSS攻击时,停止渲染页面
|
||||||
|
.setHeader("X-XSS-Protection", "1; mode=block")
|
||||||
|
// 禁用浏览器内容嗅探
|
||||||
|
.setHeader("X-Content-Type-Options", "nosniff")
|
||||||
|
;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,44 @@
|
|||||||
|
package com.jeecg.modules.jmreport.satoken;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.stp.StpInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义权限验证接口扩展
|
||||||
|
*/
|
||||||
|
@Component // 打开此注解,保证此类被springboot扫描,即可完成sa-token的自定义权限验证扩展
|
||||||
|
public class StpInterfaceImpl implements StpInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回一个账号所拥有的权限码集合
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<String> getPermissionList(Object loginId, String loginType) {
|
||||||
|
// 本list仅做模拟,实际项目中要根据具体业务逻辑来查询权限
|
||||||
|
List<String> list = new ArrayList<String>();
|
||||||
|
list.add("101");
|
||||||
|
list.add("user-add");
|
||||||
|
list.add("user-delete");
|
||||||
|
list.add("user-update");
|
||||||
|
list.add("user-get");
|
||||||
|
list.add("article-get");
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回一个账号所拥有的角色标识集合
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<String> getRoleList(Object loginId, String loginType) {
|
||||||
|
// 本list仅做模拟,实际项目中要根据具体业务逻辑来查询角色
|
||||||
|
List<String> list = new ArrayList<String>();
|
||||||
|
list.add("admin");
|
||||||
|
list.add("super-admin");
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
package com.jeecg.modules.jmreport.config;
|
package com.jeecg.modules.jmreport.satoken.config;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||||
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||||
@@ -42,12 +42,10 @@ public class RedisConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Jackson2JsonRedisSerializer<Object> jacksonSerializer() {
|
private Jackson2JsonRedisSerializer<Object> jacksonSerializer() {
|
||||||
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
|
|
||||||
ObjectMapper objectMapper = new ObjectMapper();
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
|
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
|
||||||
// 使用新的替代方法,避免已废弃的enableDefaultTyping
|
|
||||||
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
|
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
|
||||||
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
|
// 直接在构造器中传入 objectMapper
|
||||||
return jackson2JsonRedisSerializer;
|
return new Jackson2JsonRedisSerializer<>(objectMapper, Object.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -0,0 +1,40 @@
|
|||||||
|
package com.jeecg.modules.jmreport.satoken.config;
|
||||||
|
|
||||||
|
import com.jeecg.modules.jmreport.satoken.config.vo.User;
|
||||||
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Role;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载项目配置
|
||||||
|
*
|
||||||
|
* @author: jeecg-boot
|
||||||
|
*/
|
||||||
|
@Component("securityConfig")
|
||||||
|
@ConfigurationProperties(prefix = "spring.security")
|
||||||
|
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||||
|
public class SecurityConfig {
|
||||||
|
private Boolean enable = true;
|
||||||
|
/**
|
||||||
|
* 登录账号和密码
|
||||||
|
*/
|
||||||
|
private User user;
|
||||||
|
|
||||||
|
public User getUser() {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUser(User user) {
|
||||||
|
this.user = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getEnable() {
|
||||||
|
return enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnable(Boolean enable) {
|
||||||
|
this.enable = enable;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,20 @@
|
|||||||
|
package com.jeecg.modules.jmreport.satoken.config.vo;
|
||||||
|
|
||||||
|
public class User {
|
||||||
|
private String name;
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@@ -0,0 +1,162 @@
|
|||||||
|
//package com.jeecg.modules.jmreport.satoken.exception;
|
||||||
|
//
|
||||||
|
//import java.io.Serializable;
|
||||||
|
//import java.util.List;
|
||||||
|
//
|
||||||
|
//
|
||||||
|
///**
|
||||||
|
// * ajax请求返回Json格式数据的封装
|
||||||
|
// */
|
||||||
|
//public class AjaxJson implements Serializable{
|
||||||
|
//
|
||||||
|
// private static final long serialVersionUID = 1L; // 序列化版本号
|
||||||
|
//
|
||||||
|
// public static final int CODE_SUCCESS = 200; // 成功状态码
|
||||||
|
// public static final int CODE_ERROR = 500; // 错误状态码
|
||||||
|
// public static final int CODE_WARNING = 501; // 警告状态码
|
||||||
|
// public static final int CODE_NOT_JUR = 403; // 无权限状态码
|
||||||
|
// public static final int CODE_NOT_LOGIN = 401; // 未登录状态码
|
||||||
|
// public static final int CODE_INVALID_REQUEST = 400; // 无效请求状态码
|
||||||
|
//
|
||||||
|
// public int code; // 状态码
|
||||||
|
// public String msg; // 描述信息
|
||||||
|
// public Object data; // 携带对象
|
||||||
|
// public Long dataCount; // 数据总数,用于分页
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * 返回code
|
||||||
|
// * @return
|
||||||
|
// */
|
||||||
|
// public int getCode() {
|
||||||
|
// return this.code;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * 给msg赋值,连缀风格
|
||||||
|
// */
|
||||||
|
// public AjaxJson setMsg(String msg) {
|
||||||
|
// this.msg = msg;
|
||||||
|
// return this;
|
||||||
|
// }
|
||||||
|
// public String getMsg() {
|
||||||
|
// return this.msg;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * 给data赋值,连缀风格
|
||||||
|
// */
|
||||||
|
// public AjaxJson setData(Object data) {
|
||||||
|
// this.data = data;
|
||||||
|
// return this;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * 将data还原为指定类型并返回
|
||||||
|
// */
|
||||||
|
// @SuppressWarnings("unchecked")
|
||||||
|
// public <T> T getData(Class<T> cs) {
|
||||||
|
// return (T) data;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // ============================ 构建 ==================================
|
||||||
|
//
|
||||||
|
// public AjaxJson(int code, String msg, Object data, Long dataCount) {
|
||||||
|
// this.code = code;
|
||||||
|
// this.msg = msg;
|
||||||
|
// this.data = data;
|
||||||
|
// this.dataCount = dataCount;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 返回成功
|
||||||
|
// public static AjaxJson getSuccess() {
|
||||||
|
// return new AjaxJson(CODE_SUCCESS, "ok", null, null);
|
||||||
|
// }
|
||||||
|
// public static AjaxJson getSuccess(String msg) {
|
||||||
|
// return new AjaxJson(CODE_SUCCESS, msg, null, null);
|
||||||
|
// }
|
||||||
|
// public static AjaxJson getSuccess(String msg, Object data) {
|
||||||
|
// return new AjaxJson(CODE_SUCCESS, msg, data, null);
|
||||||
|
// }
|
||||||
|
// public static AjaxJson getSuccessData(Object data) {
|
||||||
|
// return new AjaxJson(CODE_SUCCESS, "ok", data, null);
|
||||||
|
// }
|
||||||
|
// public static AjaxJson getSuccessArray(Object... data) {
|
||||||
|
// return new AjaxJson(CODE_SUCCESS, "ok", data, null);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 返回失败
|
||||||
|
// public static AjaxJson getError() {
|
||||||
|
// return new AjaxJson(CODE_ERROR, "error", null, null);
|
||||||
|
// }
|
||||||
|
// public static AjaxJson getError(String msg) {
|
||||||
|
// return new AjaxJson(CODE_ERROR, msg, null, null);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 返回警告
|
||||||
|
// public static AjaxJson getWarning() {
|
||||||
|
// return new AjaxJson(CODE_ERROR, "warning", null, null);
|
||||||
|
// }
|
||||||
|
// public static AjaxJson getWarning(String msg) {
|
||||||
|
// return new AjaxJson(CODE_WARNING, msg, null, null);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 返回未登录
|
||||||
|
// public static AjaxJson getNotLogin() {
|
||||||
|
// return new AjaxJson(CODE_NOT_LOGIN, "未登录,请登录后再次访问", null, null);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 返回没有权限的
|
||||||
|
// public static AjaxJson getNotJur(String msg) {
|
||||||
|
// return new AjaxJson(CODE_NOT_JUR, msg, null, null);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 返回一个自定义状态码的
|
||||||
|
// public static AjaxJson get(int code, String msg){
|
||||||
|
// return new AjaxJson(code, msg, null, null);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 返回分页和数据的
|
||||||
|
// public static AjaxJson getPageData(Long dataCount, Object data){
|
||||||
|
// return new AjaxJson(CODE_SUCCESS, "ok", data, dataCount);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 返回,根据受影响行数的(大于0=ok,小于0=error)
|
||||||
|
// public static AjaxJson getByLine(int line){
|
||||||
|
// if(line > 0){
|
||||||
|
// return getSuccess("ok", line);
|
||||||
|
// }
|
||||||
|
// return getError("error").setData(line);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 返回,根据布尔值来确定最终结果的 (true=ok,false=error)
|
||||||
|
// public static AjaxJson getByBoolean(boolean b){
|
||||||
|
// return b ? getSuccess("ok") : getError("error");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /* (non-Javadoc)
|
||||||
|
// * @see java.lang.Object#toString()
|
||||||
|
// */
|
||||||
|
// @SuppressWarnings("rawtypes")
|
||||||
|
// @Override
|
||||||
|
// public String toString() {
|
||||||
|
// String data_string = null;
|
||||||
|
// if(data == null){
|
||||||
|
//
|
||||||
|
// } else if(data instanceof List){
|
||||||
|
// data_string = "List(length=" + ((List)data).size() + ")";
|
||||||
|
// } else {
|
||||||
|
// data_string = data.toString();
|
||||||
|
// }
|
||||||
|
// return "{"
|
||||||
|
// + "\"code\": " + this.getCode()
|
||||||
|
// + ", \"msg\": \"" + this.getMsg() + "\""
|
||||||
|
// + ", \"data\": " + data_string
|
||||||
|
// + ", \"dataCount\": " + dataCount
|
||||||
|
// + "}";
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//}
|
@@ -0,0 +1,53 @@
|
|||||||
|
//package com.jeecg.modules.jmreport.satoken.exception;
|
||||||
|
//
|
||||||
|
//import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
|
//import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
|
//import cn.dev33.satoken.exception.DisableServiceException;
|
||||||
|
//import cn.dev33.satoken.exception.NotLoginException;
|
||||||
|
//import cn.dev33.satoken.exception.NotPermissionException;
|
||||||
|
//import cn.dev33.satoken.exception.NotRoleException;
|
||||||
|
//import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
//import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
//
|
||||||
|
///**
|
||||||
|
// * 全局异常处理
|
||||||
|
// */
|
||||||
|
//@RestControllerAdvice
|
||||||
|
//public class GlobalException {
|
||||||
|
//
|
||||||
|
// // 全局异常拦截(拦截项目中的所有异常)
|
||||||
|
// @ExceptionHandler
|
||||||
|
// public AjaxJson handlerException(Exception e, HttpServletRequest request, HttpServletResponse response)
|
||||||
|
// throws Exception {
|
||||||
|
//
|
||||||
|
// // 打印堆栈,以供调试
|
||||||
|
// System.out.println("全局异常---------------");
|
||||||
|
// //e.printStackTrace();
|
||||||
|
//
|
||||||
|
// // 不同异常返回不同状态码
|
||||||
|
// AjaxJson aj = null;
|
||||||
|
// if (e instanceof NotLoginException) { // 如果是未登录异常
|
||||||
|
// NotLoginException ee = (NotLoginException) e;
|
||||||
|
// aj = AjaxJson.getNotLogin().setMsg(ee.getMessage());
|
||||||
|
// }
|
||||||
|
// else if(e instanceof NotRoleException) { // 如果是角色异常
|
||||||
|
// NotRoleException ee = (NotRoleException) e;
|
||||||
|
// aj = AjaxJson.getNotJur("无此角色:" + ee.getRole());
|
||||||
|
// }
|
||||||
|
// else if(e instanceof NotPermissionException) { // 如果是权限异常
|
||||||
|
// NotPermissionException ee = (NotPermissionException) e;
|
||||||
|
// aj = AjaxJson.getNotJur("无此权限:" + ee.getPermission());
|
||||||
|
// }
|
||||||
|
// else if(e instanceof DisableServiceException) { // 如果是被封禁异常
|
||||||
|
// DisableServiceException ee = (DisableServiceException) e;
|
||||||
|
// aj = AjaxJson.getNotJur("当前账号 " + ee.getService() + " 服务已被封禁 (level=" + ee.getLevel() + "):" + ee.getDisableTime() + "秒后解封");
|
||||||
|
// }
|
||||||
|
// else { // 普通异常, 输出:500 + 异常信息
|
||||||
|
// aj = AjaxJson.getError(e.getMessage());
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 返回给前端
|
||||||
|
// return aj;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//}
|
@@ -0,0 +1,38 @@
|
|||||||
|
//package com.jeecg.modules.jmreport.satoken.exception;
|
||||||
|
//
|
||||||
|
//import cn.dev33.satoken.exception.NotLoginException;
|
||||||
|
//import cn.dev33.satoken.util.SaResult;
|
||||||
|
//import com.jeecg.modules.jmreport.satoken.util.AjaxRequestUtils;
|
||||||
|
//import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
//import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
//import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
|
//import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
|
//
|
||||||
|
//import java.io.IOException;
|
||||||
|
//import java.net.URLEncoder;
|
||||||
|
//
|
||||||
|
//@RestControllerAdvice
|
||||||
|
//public class GlobalExceptionHandler {
|
||||||
|
//
|
||||||
|
// // 捕获未登录异常
|
||||||
|
// @ExceptionHandler(NotLoginException.class)
|
||||||
|
// public SaResult handlerNotLoginException(NotLoginException e,
|
||||||
|
// HttpServletRequest request,
|
||||||
|
// HttpServletResponse response) {
|
||||||
|
//
|
||||||
|
// // AJAX 请求返回 JSON
|
||||||
|
// if (AjaxRequestUtils.isAjaxRequest(request)) {
|
||||||
|
// return SaResult.error("未登录,请先登录").setCode(401);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 普通请求重定向到登录页
|
||||||
|
// try {
|
||||||
|
// response.sendRedirect("/login/login.html?redirect=" + URLEncoder.encode(request.getRequestURI(), "UTF-8"));
|
||||||
|
// } catch (IOException ex) {
|
||||||
|
// ex.printStackTrace();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return SaResult.error("未登录");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//}
|
@@ -0,0 +1,79 @@
|
|||||||
|
package com.jeecg.modules.jmreport.satoken.util;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.context.SaHolder;
|
||||||
|
import cn.dev33.satoken.session.SaSession;
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import jakarta.servlet.http.HttpSession;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class AjaxRequestUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否为 AJAX 请求
|
||||||
|
*/
|
||||||
|
public static boolean isAjaxRequest(HttpServletRequest request) {
|
||||||
|
// 1. 检查 X-Requested-With 头部
|
||||||
|
String xRequestedWith = request.getHeader("X-Requested-With");
|
||||||
|
if ("XMLHttpRequest".equalsIgnoreCase(xRequestedWith)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 检查 Accept 头部
|
||||||
|
String accept = request.getHeader("Accept");
|
||||||
|
if (accept != null && accept.contains("application/json")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 检查 Content-Type 头部(对于 POST 请求)
|
||||||
|
String contentType = request.getHeader("Content-Type");
|
||||||
|
if (contentType != null && contentType.contains("application/json")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 检查请求参数(某些框架会添加特定参数)
|
||||||
|
String ajaxParam = request.getParameter("_ajax");
|
||||||
|
if ("true".equals(ajaxParam)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据请求类型返回相应响应
|
||||||
|
*/
|
||||||
|
public static void writeResponse(HttpServletRequest request,
|
||||||
|
HttpServletResponse response,
|
||||||
|
Object data) throws IOException {
|
||||||
|
|
||||||
|
if (isAjaxRequest(request)) {
|
||||||
|
response.setContentType("application/json;charset=UTF-8");
|
||||||
|
response.getWriter().write(JSON.toJSONString(data));
|
||||||
|
} else {
|
||||||
|
// 普通请求处理
|
||||||
|
request.setAttribute("data", data);
|
||||||
|
// 这里可以转发到相应的 JSP 页面
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置 Sa-Token 会话的登录来源和拖拽开关
|
||||||
|
*/
|
||||||
|
// 登录来源-积木报表示例
|
||||||
|
public final static String LOGIN_FROM_MODEL_NEED_LOGOUT = "jimu_example";
|
||||||
|
|
||||||
|
public static void setLoginSessionInfo() {
|
||||||
|
HttpSession originalSession = ((HttpServletRequest) SaHolder.getRequest().getSource()).getSession();
|
||||||
|
if (originalSession!=null && originalSession.getAttribute("loginFrom") == null) {
|
||||||
|
log.info("设置登录来源,BI与报表切换开关,注入个性化session信息。");
|
||||||
|
originalSession.setAttribute("loginFrom", LOGIN_FROM_MODEL_NEED_LOGOUT);
|
||||||
|
originalSession.setAttribute("switchJimuDrag", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -7,9 +7,17 @@ spring:
|
|||||||
username: root
|
username: root
|
||||||
password: root
|
password: root
|
||||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
|
#Redis配置
|
||||||
|
data:
|
||||||
|
redis:
|
||||||
|
database: 0
|
||||||
|
host: 127.0.0.1
|
||||||
|
port: 6379
|
||||||
|
password:
|
||||||
|
# 登录认证配置
|
||||||
security:
|
security:
|
||||||
#是否放开预览页面不需要登录
|
#是否开启登录认证
|
||||||
open-view-page: true
|
enable: true
|
||||||
#登录账号和密码
|
#登录账号和密码
|
||||||
user:
|
user:
|
||||||
name: "admin"
|
name: "admin"
|
||||||
|
@@ -7,10 +7,16 @@ spring:
|
|||||||
username: root
|
username: root
|
||||||
password: root
|
password: root
|
||||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
|
#Redis配置
|
||||||
|
data:
|
||||||
|
redis:
|
||||||
|
database: 0
|
||||||
|
host: 127.0.0.1
|
||||||
|
port: 6379
|
||||||
|
password:
|
||||||
|
# 登录认证配置
|
||||||
security:
|
security:
|
||||||
#是否放开预览页面不需要登录
|
#是否开启登录认证
|
||||||
open-view-page: true
|
|
||||||
#登录账号和密码
|
|
||||||
user:
|
user:
|
||||||
name: "admin"
|
name: "admin"
|
||||||
password: "123456"
|
password: "123456"
|
||||||
|
@@ -1,3 +1,20 @@
|
|||||||
spring:
|
spring:
|
||||||
profiles:
|
profiles:
|
||||||
active: dev
|
active: dev
|
||||||
|
|
||||||
|
############## Sa-Token 配置 (文档: https://sa-token.cc) ##############
|
||||||
|
sa-token:
|
||||||
|
# token 名称(同时也是 cookie 名称)
|
||||||
|
token-name: X-Access-Token
|
||||||
|
# token 有效期(单位:秒) 默认30天,-1 代表永久有效
|
||||||
|
timeout: 2592000
|
||||||
|
# token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结
|
||||||
|
active-timeout: -1
|
||||||
|
# 是否允许同一账号多地同时登录 (为 true 时允许一起登录, 为 false 时新登录挤掉旧登录)
|
||||||
|
is-concurrent: true
|
||||||
|
# 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token)
|
||||||
|
is-share: false
|
||||||
|
# token 风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik)
|
||||||
|
token-style: uuid
|
||||||
|
# 是否输出操作日志
|
||||||
|
is-log: false
|
@@ -280,7 +280,7 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<form class="form-signin" method="post" action="/login">
|
<form class="form-signin" method="get" action="/doLogin">
|
||||||
<h2 class="form-signin-heading">欢迎使用积木报表</h2>
|
<h2 class="form-signin-heading">欢迎使用积木报表</h2>
|
||||||
<p>
|
<p>
|
||||||
<label for="username" class="sr-only">Username</label>
|
<label for="username" class="sr-only">Username</label>
|
||||||
|
Reference in New Issue
Block a user