From 6aa8f9ae61128e8b7ceb55f0343310da66a77690 Mon Sep 17 00:00:00 2001
From: JEECG <445654970@qq.com>
Date: Fri, 26 Sep 2025 12:18:30 +0800
Subject: [PATCH] =?UTF-8?q?=E5=8D=87=E7=BA=A7springboot3=E3=80=81=E6=9D=83?=
=?UTF-8?q?=E9=99=90=E9=87=87=E7=94=A8sa-token?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 10 +-
jimureport-example/README.md | 8 +
jimureport-example/pom.xml | 33 ++--
.../{modules => }/JimuReportApplication.java | 7 +-
.../config/ApiSecurityConfigFilter.java | 35 ----
.../config/CustomCorsConfiguration.java | 18 --
.../config/CustomLoginSuccessHandler.java | 53 ------
.../config/JimuReportSysSwitchFilter.java | 61 -------
.../jmreport/config/SpringSecurityConfig.java | 111 ------------
.../jmreport/config/ViewPageCustomAccess.java | 43 -----
.../jmreport/controller/IndexController.java | 21 ---
.../jmreport/controller/LoginController.java | 89 ++++++++++
.../extend/JimuDragExternalServiceImpl.java | 3 +-
.../extend/JimuReportTokenServiceImpl.java | 87 +++++++---
.../jmreport/satoken/SaTokenConfigure.java | 85 +++++++++
.../jmreport/satoken/StpInterfaceImpl.java | 44 +++++
.../{ => satoken}/config/RedisConfig.java | 8 +-
.../satoken/config/SecurityConfig.java | 40 +++++
.../jmreport/satoken/config/vo/User.java | 20 +++
.../jmreport/satoken/exception/AjaxJson.java | 162 ++++++++++++++++++
.../satoken/exception/GlobalException.java | 53 ++++++
.../exception/GlobalExceptionHandler.java | 38 ++++
.../satoken/util/AjaxRequestUtils.java | 79 +++++++++
.../src/main/resources/application-dev.yml | 12 +-
.../src/main/resources/application-prod.yml | 12 +-
.../src/main/resources/application.yml | 19 +-
.../main/resources/static/login/login.html | 2 +-
27 files changed, 752 insertions(+), 401 deletions(-)
rename jimureport-example/src/main/java/com/jeecg/{modules => }/JimuReportApplication.java (90%)
delete mode 100644 jimureport-example/src/main/java/com/jeecg/modules/jmreport/config/ApiSecurityConfigFilter.java
delete mode 100644 jimureport-example/src/main/java/com/jeecg/modules/jmreport/config/CustomCorsConfiguration.java
delete mode 100644 jimureport-example/src/main/java/com/jeecg/modules/jmreport/config/CustomLoginSuccessHandler.java
delete mode 100644 jimureport-example/src/main/java/com/jeecg/modules/jmreport/config/JimuReportSysSwitchFilter.java
delete mode 100644 jimureport-example/src/main/java/com/jeecg/modules/jmreport/config/SpringSecurityConfig.java
delete mode 100644 jimureport-example/src/main/java/com/jeecg/modules/jmreport/config/ViewPageCustomAccess.java
delete mode 100644 jimureport-example/src/main/java/com/jeecg/modules/jmreport/controller/IndexController.java
create mode 100644 jimureport-example/src/main/java/com/jeecg/modules/jmreport/controller/LoginController.java
create mode 100644 jimureport-example/src/main/java/com/jeecg/modules/jmreport/satoken/SaTokenConfigure.java
create mode 100644 jimureport-example/src/main/java/com/jeecg/modules/jmreport/satoken/StpInterfaceImpl.java
rename jimureport-example/src/main/java/com/jeecg/modules/jmreport/{ => satoken}/config/RedisConfig.java (86%)
create mode 100644 jimureport-example/src/main/java/com/jeecg/modules/jmreport/satoken/config/SecurityConfig.java
create mode 100644 jimureport-example/src/main/java/com/jeecg/modules/jmreport/satoken/config/vo/User.java
create mode 100644 jimureport-example/src/main/java/com/jeecg/modules/jmreport/satoken/exception/AjaxJson.java
create mode 100644 jimureport-example/src/main/java/com/jeecg/modules/jmreport/satoken/exception/GlobalException.java
create mode 100644 jimureport-example/src/main/java/com/jeecg/modules/jmreport/satoken/exception/GlobalExceptionHandler.java
create mode 100644 jimureport-example/src/main/java/com/jeecg/modules/jmreport/satoken/util/AjaxRequestUtils.java
diff --git a/README.md b/README.md
index 5db1c38..7595039 100644
--- a/README.md
+++ b/README.md
@@ -40,17 +40,17 @@ v2.1.3 | 2025-09-05
快速集成积木报表
-----------------------------------
-> 快速集成到自己项目中,支持SpringBoot脚手架项目,springboot2依赖采用jdk8编译,支持jdk8、jdk17、jdk21+。
+> 快速集成到自己项目中,支持SpringBoot3脚手架项目(要求jdk17+),springboot2版本要求jdk17+
#### 第一步:引入积木报表依赖
-- springboot2
+- springboot3
```
org.jeecgframework.jimureport
- jimureport-spring-boot-starter
+ jimureport-spring-boot3-starter
2.1.3
@@ -67,12 +67,12 @@ v2.1.3 | 2025-09-05
```
-- springboot3
+- springboot2
```
org.jeecgframework.jimureport
- jimureport-spring-boot3-starter-fastjson2
+ jimureport-spring-boot-starter
2.1.3
diff --git a/jimureport-example/README.md b/jimureport-example/README.md
index 38cdee2..3443e97 100644
--- a/jimureport-example/README.md
+++ b/jimureport-example/README.md
@@ -8,6 +8,14 @@
+环境要求
+-----------------------------------
+
+- 要求jdk17+(本项目springboot3架构)
+- 要求mysql5.7+ 手工执行db/jimureport.mysql5.7.create.sql,会自动创建库jimureport
+- 要求redis
+- 项目配置:src/main/resources/application-dev.yml
+
使用步骤
-----------------------------------
diff --git a/jimureport-example/pom.xml b/jimureport-example/pom.xml
index 1c7d218..957e3a2 100644
--- a/jimureport-example/pom.xml
+++ b/jimureport-example/pom.xml
@@ -5,7 +5,7 @@
org.springframework.boot
spring-boot-starter-parent
- 2.7.18
+ 3.5.5
@@ -45,12 +45,12 @@
- 1.8
+ 17
8.0.3
8.0.27
- 4.6
+ 4.9
@@ -64,7 +64,7 @@
org.jeecgframework.jimureport
- jimureport-spring-boot-starter
+ jimureport-spring-boot3-starter
2.1.3
@@ -82,17 +82,16 @@
org.jeecgframework.jimureport
- jimubi-spring-boot-starter
- 2.1.3
+ jimubi-spring-boot3-starter
+ 2.1.3.1
-
+
com.github.jsqlparser
jsqlparser
${jsqlparser.version}
-
org.springframework.boot
@@ -103,10 +102,22 @@
spring-boot-starter-freemarker
-
+
- org.springframework.boot
- spring-boot-starter-security
+ cn.dev33
+ sa-token-spring-boot3-starter
+ 1.44.0
+
+
+
+ cn.dev33
+ sa-token-redis-jackson
+ 1.44.0
+
+
+
+ org.apache.commons
+ commons-pool2
diff --git a/jimureport-example/src/main/java/com/jeecg/modules/JimuReportApplication.java b/jimureport-example/src/main/java/com/jeecg/JimuReportApplication.java
similarity index 90%
rename from jimureport-example/src/main/java/com/jeecg/modules/JimuReportApplication.java
rename to jimureport-example/src/main/java/com/jeecg/JimuReportApplication.java
index 4c5dd44..3b25e1b 100644
--- a/jimureport-example/src/main/java/com/jeecg/modules/JimuReportApplication.java
+++ b/jimureport-example/src/main/java/com/jeecg/JimuReportApplication.java
@@ -1,7 +1,6 @@
-package com.jeecg.modules;
+package com.jeecg;
import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;
@@ -10,8 +9,7 @@ import org.springframework.core.env.Environment;
/**
* 积木报表独立服务启动类
*/
-@SpringBootApplication(scanBasePackages = {"org.jeecg", "com.jeecg"})
-@EnableAutoConfiguration(exclude={MongoAutoConfiguration.class})
+@SpringBootApplication(scanBasePackages = {"org.jeecg", "com.jeecg"}, exclude = {MongoAutoConfiguration.class})
public class JimuReportApplication {
public static void main(String[] args) {
@@ -24,6 +22,7 @@ public class JimuReportApplication {
// 默认编码不是UTF-8设置为UTF-8
System.setProperty("file.encoding", "UTF-8");
}
+
System.out.println("\n----------------------------------------------------------\n\t" +
"JimuReport 积木报表平台 is running! Access URL:\n\t" +
"报表工作台: \t\thttp://localhost:" + port + path + "/jmreport/list\n\t" +
diff --git a/jimureport-example/src/main/java/com/jeecg/modules/jmreport/config/ApiSecurityConfigFilter.java b/jimureport-example/src/main/java/com/jeecg/modules/jmreport/config/ApiSecurityConfigFilter.java
deleted file mode 100644
index e0351de..0000000
--- a/jimureport-example/src/main/java/com/jeecg/modules/jmreport/config/ApiSecurityConfigFilter.java
+++ /dev/null
@@ -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);
- }
-}
diff --git a/jimureport-example/src/main/java/com/jeecg/modules/jmreport/config/CustomCorsConfiguration.java b/jimureport-example/src/main/java/com/jeecg/modules/jmreport/config/CustomCorsConfiguration.java
deleted file mode 100644
index 8a3e365..0000000
--- a/jimureport-example/src/main/java/com/jeecg/modules/jmreport/config/CustomCorsConfiguration.java
+++ /dev/null
@@ -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("*");
- }
-}
\ No newline at end of file
diff --git a/jimureport-example/src/main/java/com/jeecg/modules/jmreport/config/CustomLoginSuccessHandler.java b/jimureport-example/src/main/java/com/jeecg/modules/jmreport/config/CustomLoginSuccessHandler.java
deleted file mode 100644
index 47c1092..0000000
--- a/jimureport-example/src/main/java/com/jeecg/modules/jmreport/config/CustomLoginSuccessHandler.java
+++ /dev/null
@@ -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("/");
- }
-}
\ No newline at end of file
diff --git a/jimureport-example/src/main/java/com/jeecg/modules/jmreport/config/JimuReportSysSwitchFilter.java b/jimureport-example/src/main/java/com/jeecg/modules/jmreport/config/JimuReportSysSwitchFilter.java
deleted file mode 100644
index 56d6da9..0000000
--- a/jimureport-example/src/main/java/com/jeecg/modules/jmreport/config/JimuReportSysSwitchFilter.java
+++ /dev/null
@@ -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);
- }
-
-
-}
diff --git a/jimureport-example/src/main/java/com/jeecg/modules/jmreport/config/SpringSecurityConfig.java b/jimureport-example/src/main/java/com/jeecg/modules/jmreport/config/SpringSecurityConfig.java
deleted file mode 100644
index 9c450a4..0000000
--- a/jimureport-example/src/main/java/com/jeecg/modules/jmreport/config/SpringSecurityConfig.java
+++ /dev/null
@@ -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);
- }
-}
diff --git a/jimureport-example/src/main/java/com/jeecg/modules/jmreport/config/ViewPageCustomAccess.java b/jimureport-example/src/main/java/com/jeecg/modules/jmreport/config/ViewPageCustomAccess.java
deleted file mode 100644
index 3ef7ac8..0000000
--- a/jimureport-example/src/main/java/com/jeecg/modules/jmreport/config/ViewPageCustomAccess.java
+++ /dev/null
@@ -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;
- }
-}
-
diff --git a/jimureport-example/src/main/java/com/jeecg/modules/jmreport/controller/IndexController.java b/jimureport-example/src/main/java/com/jeecg/modules/jmreport/controller/IndexController.java
deleted file mode 100644
index bf24234..0000000
--- a/jimureport-example/src/main/java/com/jeecg/modules/jmreport/controller/IndexController.java
+++ /dev/null
@@ -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"; // 视图重定向 - 跳转
- }
-}
\ No newline at end of file
diff --git a/jimureport-example/src/main/java/com/jeecg/modules/jmreport/controller/LoginController.java b/jimureport-example/src/main/java/com/jeecg/modules/jmreport/controller/LoginController.java
new file mode 100644
index 0000000..38c9c43
--- /dev/null
+++ b/jimureport-example/src/main/java/com/jeecg/modules/jmreport/controller/LoginController.java
@@ -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;
+ }
+}
\ No newline at end of file
diff --git a/jimureport-example/src/main/java/com/jeecg/modules/jmreport/extend/JimuDragExternalServiceImpl.java b/jimureport-example/src/main/java/com/jeecg/modules/jmreport/extend/JimuDragExternalServiceImpl.java
index d9a5029..d623476 100644
--- a/jimureport-example/src/main/java/com/jeecg/modules/jmreport/extend/JimuDragExternalServiceImpl.java
+++ b/jimureport-example/src/main/java/com/jeecg/modules/jmreport/extend/JimuDragExternalServiceImpl.java
@@ -30,9 +30,8 @@ import java.util.Map;
@Component
public class JimuDragExternalServiceImpl implements IOnlDragExternalService {
-
- @Autowired
@Lazy
+ @Autowired
private IJimuReportDictService reportDictService;
/**
diff --git a/jimureport-example/src/main/java/com/jeecg/modules/jmreport/extend/JimuReportTokenServiceImpl.java b/jimureport-example/src/main/java/com/jeecg/modules/jmreport/extend/JimuReportTokenServiceImpl.java
index 9f0420e..cf5258f 100644
--- a/jimureport-example/src/main/java/com/jeecg/modules/jmreport/extend/JimuReportTokenServiceImpl.java
+++ b/jimureport-example/src/main/java/com/jeecg/modules/jmreport/extend/JimuReportTokenServiceImpl.java
@@ -1,22 +1,32 @@
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.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.OkConvertUtils;
-import org.springframework.http.HttpHeaders;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
-import javax.servlet.http.HttpServletRequest;
-
/**
* 自定义积木报表鉴权(如果不进行自定义,则所有请求不做权限控制)
* 1.自定义获取登录token
* 2.自定义获取登录用户
*/
+@Slf4j
@Component
public class JimuReportTokenServiceImpl implements JmReportTokenServiceI {
-
+ @Autowired
+ SecurityConfig securityConfig;
+
/**
* 通过请求获取Token
* @param request
@@ -24,9 +34,15 @@ public class JimuReportTokenServiceImpl implements JmReportTokenServiceI {
*/
@Override
public String getToken(HttpServletRequest request) {
- //System.out.println("---------call---------getToken-----------------------");
- //return TokenUtils.getTokenByRequest(request);
- return "123456";
+ String token = StpUtil.getTokenValue();
+ log.debug("------SA--TOKEN-----RequestPath={} ,GET Token = {}", SaHolder.getRequest().getRequestPath(), token);
+ 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
public String getUsername(String token) {
- // return JwtUtil.getUsername(token);
- return "admin";
+ String username = StpUtil.getLoginIdAsString();
+ log.debug("------SA--TOKEN-----RequestPath={} ,Token={} , LoginId={}", SaHolder.getRequest().getRequestPath(), token, username);
+ return username;
}
/**
@@ -75,25 +92,46 @@ public class JimuReportTokenServiceImpl implements JmReportTokenServiceI {
*/
@Override
public Boolean verifyToken(String token) {
- //System.out.println("---------verify-----Token---------------");
- //return TokenUtils.verifyToken(token, sysBaseAPI, redisUtil);
+ try {
+ 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
- */
- @Override
- public HttpHeaders customApiHeader() {
- HttpHeaders header = new HttpHeaders();
- header.add("custom-header1", "Please set a custom value 1");
- header.add("token", "token value 2");
- return header;
- }
+// /**
+// * 自定义请求头
+// * @return
+// */
+// @Override
+// public HttpHeaders customApiHeader() {
+// HttpHeaders header = new HttpHeaders();
+// header.add("custom-header1", "Please set a custom value 1");
+// header.add("token", "token value 2");
+// return header;
+// }
/**
- * 自定义获取租户
+ * 自定义租户
*
* @return
*/
@@ -110,7 +148,6 @@ public class JimuReportTokenServiceImpl implements JmReportTokenServiceI {
headerTenantId = request.getParameter(JmConst.TENANT_ID);
}
}
- //return headerTenantId;
- return null;
+ return headerTenantId;
}
}
\ No newline at end of file
diff --git a/jimureport-example/src/main/java/com/jeecg/modules/jmreport/satoken/SaTokenConfigure.java b/jimureport-example/src/main/java/com/jeecg/modules/jmreport/satoken/SaTokenConfigure.java
new file mode 100644
index 0000000..8aa6210
--- /dev/null
+++ b/jimureport-example/src/main/java/com/jeecg/modules/jmreport/satoken/SaTokenConfigure.java
@@ -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")
+ ;
+ });
+ }
+
+}
\ No newline at end of file
diff --git a/jimureport-example/src/main/java/com/jeecg/modules/jmreport/satoken/StpInterfaceImpl.java b/jimureport-example/src/main/java/com/jeecg/modules/jmreport/satoken/StpInterfaceImpl.java
new file mode 100644
index 0000000..aebdc4f
--- /dev/null
+++ b/jimureport-example/src/main/java/com/jeecg/modules/jmreport/satoken/StpInterfaceImpl.java
@@ -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 getPermissionList(Object loginId, String loginType) {
+ // 本list仅做模拟,实际项目中要根据具体业务逻辑来查询权限
+ List list = new ArrayList();
+ 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 getRoleList(Object loginId, String loginType) {
+ // 本list仅做模拟,实际项目中要根据具体业务逻辑来查询角色
+ List list = new ArrayList();
+ list.add("admin");
+ list.add("super-admin");
+ return list;
+ }
+
+}
\ No newline at end of file
diff --git a/jimureport-example/src/main/java/com/jeecg/modules/jmreport/config/RedisConfig.java b/jimureport-example/src/main/java/com/jeecg/modules/jmreport/satoken/config/RedisConfig.java
similarity index 86%
rename from jimureport-example/src/main/java/com/jeecg/modules/jmreport/config/RedisConfig.java
rename to jimureport-example/src/main/java/com/jeecg/modules/jmreport/satoken/config/RedisConfig.java
index 46860a6..17362f2 100644
--- a/jimureport-example/src/main/java/com/jeecg/modules/jmreport/config/RedisConfig.java
+++ b/jimureport-example/src/main/java/com/jeecg/modules/jmreport/satoken/config/RedisConfig.java
@@ -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.PropertyAccessor;
@@ -42,12 +42,10 @@ public class RedisConfig {
}
private Jackson2JsonRedisSerializer