mirror of
https://github.com/jeecgboot/JimuReport.git
synced 2025-10-13 22:10:27 +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>
|
||||
<groupId>org.jeecgframework.jimureport</groupId>
|
||||
<artifactId>jimureport-spring-boot-starter</artifactId>
|
||||
<artifactId>jimureport-spring-boot3-starter</artifactId>
|
||||
<version>2.1.3</version>
|
||||
</dependency>
|
||||
<!-- mongo、redis和文件数据集支持包,按需引入 -->
|
||||
@@ -67,12 +67,12 @@ v2.1.3 | 2025-09-05
|
||||
</dependency>
|
||||
```
|
||||
|
||||
- springboot3
|
||||
- springboot2
|
||||
|
||||
```
|
||||
<dependency>
|
||||
<groupId>org.jeecgframework.jimureport</groupId>
|
||||
<artifactId>jimureport-spring-boot3-starter-fastjson2</artifactId>
|
||||
<artifactId>jimureport-spring-boot-starter</artifactId>
|
||||
<version>2.1.3</version>
|
||||
</dependency>
|
||||
<!-- 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>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.7.18</version>
|
||||
<version>3.5.5</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
@@ -45,12 +45,12 @@
|
||||
</repositories>
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<java.version>17</java.version>
|
||||
<minio.version>8.0.3</minio.version>
|
||||
<!-- DB驱动 -->
|
||||
<mysql-connector-java.version>8.0.27</mysql-connector-java.version>
|
||||
<!-- SQL解析引擎 -->
|
||||
<jsqlparser.version>4.6</jsqlparser.version>
|
||||
<jsqlparser.version>4.9</jsqlparser.version>
|
||||
</properties>
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
<!-- 积木报表 -->
|
||||
<dependency>
|
||||
<groupId>org.jeecgframework.jimureport</groupId>
|
||||
<artifactId>jimureport-spring-boot-starter</artifactId>
|
||||
<artifactId>jimureport-spring-boot3-starter</artifactId>
|
||||
<version>2.1.3</version>
|
||||
</dependency>
|
||||
<!-- mongo、redis和文件数据集支持包,按需引入 -->
|
||||
@@ -82,17 +82,16 @@
|
||||
<!-- 积木BI大屏 -->
|
||||
<dependency>
|
||||
<groupId>org.jeecgframework.jimureport</groupId>
|
||||
<artifactId>jimubi-spring-boot-starter</artifactId>
|
||||
<version>2.1.3</version>
|
||||
<artifactId>jimubi-spring-boot3-starter</artifactId>
|
||||
<version>2.1.3.1</version>
|
||||
</dependency>
|
||||
<!-- jsqlparser -->
|
||||
<!-- jsqlparser-->
|
||||
<dependency>
|
||||
<groupId>com.github.jsqlparser</groupId>
|
||||
<artifactId>jsqlparser</artifactId>
|
||||
<version>${jsqlparser.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- SpringBoot-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
@@ -103,10 +102,22 @@
|
||||
<artifactId>spring-boot-starter-freemarker</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--spring security-->
|
||||
<!-- Sa-Token 权限认证,在线文档:https://sa-token.cc -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<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>
|
||||
|
||||
<!-- minio oss-->
|
||||
|
@@ -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" +
|
@@ -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
|
||||
public class JimuDragExternalServiceImpl implements IOnlDragExternalService {
|
||||
|
||||
|
||||
@Autowired
|
||||
@Lazy
|
||||
@Autowired
|
||||
private IJimuReportDictService reportDictService;
|
||||
|
||||
/**
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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.PropertyAccessor;
|
||||
@@ -42,12 +42,10 @@ public class RedisConfig {
|
||||
}
|
||||
|
||||
private Jackson2JsonRedisSerializer<Object> jacksonSerializer() {
|
||||
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
|
||||
// 使用新的替代方法,避免已废弃的enableDefaultTyping
|
||||
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
|
||||
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
|
||||
return jackson2JsonRedisSerializer;
|
||||
// 直接在构造器中传入 objectMapper
|
||||
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
|
||||
password: root
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
#Redis配置
|
||||
data:
|
||||
redis:
|
||||
database: 0
|
||||
host: 127.0.0.1
|
||||
port: 6379
|
||||
password:
|
||||
# 登录认证配置
|
||||
security:
|
||||
#是否放开预览页面不需要登录
|
||||
open-view-page: true
|
||||
#是否开启登录认证
|
||||
enable: true
|
||||
#登录账号和密码
|
||||
user:
|
||||
name: "admin"
|
||||
|
@@ -7,10 +7,16 @@ spring:
|
||||
username: root
|
||||
password: root
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
#Redis配置
|
||||
data:
|
||||
redis:
|
||||
database: 0
|
||||
host: 127.0.0.1
|
||||
port: 6379
|
||||
password:
|
||||
# 登录认证配置
|
||||
security:
|
||||
#是否放开预览页面不需要登录
|
||||
open-view-page: true
|
||||
#登录账号和密码
|
||||
#是否开启登录认证
|
||||
user:
|
||||
name: "admin"
|
||||
password: "123456"
|
||||
|
@@ -1,3 +1,20 @@
|
||||
spring:
|
||||
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>
|
||||
<body>
|
||||
<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>
|
||||
<p>
|
||||
<label for="username" class="sr-only">Username</label>
|
||||
|
Reference in New Issue
Block a user