RuoYi-Vue-Plus5.X集成积木报表及Token校验

651 阅读3分钟

在 ruoyi-admin 引入 Spring3.0 版本

image.png

RuoYiApplication添加积木扫描目录

@SpringBootApplication(scanBasePackages={"org.dromara","org.jeecg.modules.jmreport" })
public class DromaraApplication {

    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(DromaraApplication.class);
        application.setApplicationStartup(new BufferingApplicationStartup(2048));
        application.run(args);
        System.out.println("(♥◠‿◠)ノ゙  RuoYi-Vue-Plus启动成功   ლ(´ڡ`ლ)゙");
    }

}

SecurityConfig拦截排除

在 application.yml 中的 security 配置中添加 /jmreport/**

image.png

配置启动属性

image.png

配置请求类

为了方便我放在了org.dromara.web.controller.jimu 实现


import cn.dev33.satoken.annotation.SaCheckPermission;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequiredArgsConstructor
@RestController
@RequestMapping("/report/jimu")
public class JimuController {
    @Autowired
    Environment environment;

    @GetMapping("/index")
    @SaCheckPermission("report:jimu:index")
    public String index(){

        return environment.getProperty("jimu-report.url")+"/jmreport/list";
    }

    @GetMapping("/view")
    @SaCheckPermission("report:jimu:view")
    public String view(){
        return environment.getProperty("jimu-report.url")+"/jmreport/view";
    }
}

前端API 请求

import request from '@/utils/request';

//积木报表接口
export const indexUrl = (query?: any) =>
  request({
    url: '/report/jimu/index',
    method: 'get',
  });

export const view = (query?: any) =>
  request({
    url: '/report/jimu/view',
    method: 'get',

  });

前端配置报表设计器 List 页面

此界面对应的是积木报表/jmreport/list

<template>
  <div v-loading="loading" :style="`height:${height}px;`">
    <iframe :src="src" frameborder="no" style="width: 100%; height: 100%" scrolling="auto" />
  </div>
</template>

<script setup>
import { onMounted, ref, watchEffect } from 'vue';
import { useRoute } from 'vue-router';
import { getToken } from '@/utils/auth';
import { indexUrl } from '@/api/report/jimu';

const loading = ref(true);
const height = ref(document.documentElement.clientHeight - 94.5);
const src = ref('');

// 初始化数据
onMounted(() => {
  fetchSrc();
  setTimeout(() => {
    loading.value = false;
  }, 230);

  // 监听窗口大小变化
  const handleResize = () => {
    height.value = document.documentElement.clientHeight - 94.5;
  };

  window.addEventListener('resize', handleResize);
  return () => window.removeEventListener('resize', handleResize);
});

// 获取 iframe 源地址
const fetchSrc = async () => {
  try {
    const response = await indexUrl();
    src.value = `${response}?token=${getToken()}`;
  } catch (error) {
    console.error('Failed to fetch src:', error);
  }
};

// 监听路由变化
const route = useRoute();
watchEffect(() => {
  fetchSrc();
});
</script>

前端配置报表 View 页面

此界面对应的是积木报表/jmreport/view

<template>
    <div v-loading="loading" :style="`height:${height}px;`">
      <iframe :src="src" frameborder="no" style="width: 100%; height: 100%" scrolling="auto" />
    </div>
  </template>

  <script setup>
  import { onMounted, ref, watchEffect } from 'vue';
  import { useRoute } from 'vue-router';
  import { getToken } from '@/utils/auth';
  import { view } from '@/api/report/jimu';

  const loading = ref(true);
  const height = ref(document.documentElement.clientHeight - 94.5);
  const src = ref('');

  // 初始化数据
  onMounted(() => {
    fetchSrc();
    setTimeout(() => {
      loading.value = false;
    }, 230);

    // 监听窗口大小变化
    const handleResize = () => {
      height.value = document.documentElement.clientHeight - 94.5;
    };

    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  });

  // 获取 iframe 源地址
  const fetchSrc = async () => {
    try {
      //获取路由参数reportId
     
      const reportId = useRoute().path.substring(useRoute().path.lastIndexOf("/")+1)
      const response = await view();
      src.value = `${response}/${reportId}?token=${getToken()}`;
    } catch (error) {
      console.error('Failed to fetch src:', error);
    }
  };

  // 监听路由变化
  const route = useRoute();
  watchEffect(() => {
    fetchSrc();
  });
  </script>

前端 菜单配置

image.png

前端配置两个按钮权限

此处两权限必须与后端请求类一致

image.png

添加 Token 校验

实现JimuReportTokenService 类

为了方便我放在了org.dromara.web.controller.jimu 实现

package org.dromara.web.controller.jimu;



import cn.hutool.core.util.ObjectUtil;
import jakarta.servlet.http.HttpServletRequest;
import lombok.AllArgsConstructor;
import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.satoken.utils.LoginHelper;
import org.jeecg.modules.jmreport.api.JmReportTokenServiceI;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.Set;


@Component
@AllArgsConstructor
public class JimuReportTokenService implements JmReportTokenServiceI {

    @Override
    public String getToken(HttpServletRequest request) {
        return getTokenByRequest(request);
    }

    @Override
    public String getToken() {
        return JmReportTokenServiceI.super.getToken();
    }

    @Override
    public String getUsername(String s) {

        LoginUser loginUser =  LoginHelper.getLoginUser();
        return loginUser.getUsername();
    }

    @Override
    public String[] getRoles(String s) {
        return new String[0];
    }

    @Override
    public Boolean verifyToken(String s) {
        //此处不作判断在JmReporListInterceptor和JmReporViewInterceptor拦截器中已校验
        return true;
    }
//    @Override
//    public Boolean verifyToken(String s) {
//        LoginUser loginUser = LoginHelper.getLoginUser(s);
//        if(ObjectUtil.isNotEmpty(loginUser)){
//           if(LoginHelper.isSuperAdmin(loginUser.getUserId())){
//               return true;
//           }else {
//               //获取权限集合
//               Set<String> permissions = loginUser.getMenuPermission();
//               //如果拥有设计器的权限,则无需view权限,也可以通过校验
//               if(permissions != null && permissions.contains("report:jimu:list")) {
//                   return true;
//               } else if (permissions != null && permissions.contains("report:jimu:view")) {
//                   return true;
//               }
//               return false;
//           }
//        }
//        return false;
//    }

    @Override
    public Map<String, Object> getUserInfo(String token) {
        return JmReportTokenServiceI.super.getUserInfo(token);
    }

    @Override
    public HttpHeaders customApiHeader() {
        return JmReportTokenServiceI.super.customApiHeader();
    }

    @Override
    public String getTenantId() {
        return JmReportTokenServiceI.super.getTenantId();
    }


    /**
     * 获取 request 里传递的 token
     *
     * @param request
     * @return
     */
    public static String getTokenByRequest(HttpServletRequest request) {
        String parameter = request.getParameter("token");
        String header = request.getHeader("token");
        if (parameter == null && header == null) {
            parameter = request.getHeader("Authorization");
        }
        return parameter != null ? parameter : header;
    }

}

实现对设计器 List 和视图 View 的监听器

需要在ruoyi-common-web 实现

package org.dromara.common.web.interceptor;


import cn.hutool.json.JSONObject;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.springframework.web.servlet.HandlerInterceptor;

import java.util.Set;

/**
 * 拦截积木报表设计器访问路由,校验权限
 *
 */

public class JmReportListInterceptor implements HandlerInterceptor {


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        response.setContentType("text/html; charset=UTF-8");
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        String token = request.getParameter("token");
        LoginUser loginUser = LoginHelper.getLoginUser(token);

        if (loginUser != null) {
            if(LoginHelper.isSuperAdmin(loginUser.getUserId())){
                return true;
            }else {
                //获取权限集合
                Set<String> permissions = loginUser.getMenuPermission();
                //获取权限集合
               //如果拥有设计器的权限,则无需view权限,也可以通过校验
               if(permissions != null && permissions.contains("report:jimu:list")) {
                   return true;
               }
            }
        }
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("code", 200);
        jsonObject.put("msg", "参数错误或无权访问数据");
        response.getWriter().println(jsonObject);
        return false;

        }
}
package org.dromara.common.web.interceptor;


import cn.hutool.json.JSONObject;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.satoken.utils.LoginHelper;


import org.springframework.web.servlet.HandlerInterceptor;

import java.util.Set;

/**
 * 拦截积木报表查看访问路由,校验权限
 *
 */

public class JmReportViewInterceptor implements HandlerInterceptor {


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        response.setContentType("text/html; charset=UTF-8");
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        String token = request.getParameter("token");
        LoginUser loginUser = LoginHelper.getLoginUser(token);

        if (loginUser != null) {
            if(LoginHelper.isSuperAdmin(loginUser.getUserId())){
                return true;
            }else {
                //获取权限集合
                Set<String> permissions = loginUser.getMenuPermission();
                //获取权限集合
               //如果拥有设计器的权限,则无需view权限,也可以通过校验
              if (permissions != null && permissions.contains("report:jimu:view")) {
                   //需要同时判断对应表单权限
                   //一般是通过报表菜单点击进来的,校验是否有对应报表的权限:report:jimu:view:{reportId}
                   //http../jmreport/view/717968580806651904,则reportId = 717968580806651904
                   String reportId = StringUtils.substringAfterLast(request.getRequestURI(), "/");
                   //report:jimuView 需要与全端的权限保持一致
                   String viewPerm = "report:jimuView:" + reportId;
                   if (permissions != null && permissions.contains(viewPerm)) {
                       return true;
                   }
               }
            }
        }
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("code", 200);
        jsonObject.put("msg", "参数错误或无权访问数据");
        response.getWriter().println(jsonObject);
        return false;

        }
}

在ResourcesConfig 添加监听

registry.addInterceptor(new JmReportViewInterceptor()).addPathPatterns("/jmreport/view/**");
registry.addInterceptor(new JmReportListInterceptor()).addPathPatterns("/jmreport/list/**");

注意ruoyi-common-web 需要引入ruoyi-common-satoken

     <dependency>
            <groupId>org.dromara</groupId>
            <artifactId>ruoyi-common-satoken</artifactId>
        </dependency>

前端菜单管理配置对应视图权限

image.png