diff --git a/backend/src/main/java/io/dataease/auth/filter/F2CDocFilter.java b/backend/src/main/java/io/dataease/auth/filter/F2CDocFilter.java index 022180c4b3..78ff0a82f2 100644 --- a/backend/src/main/java/io/dataease/auth/filter/F2CDocFilter.java +++ b/backend/src/main/java/io/dataease/auth/filter/F2CDocFilter.java @@ -1,26 +1,99 @@ package io.dataease.auth.filter; -import org.apache.shiro.web.filter.authc.AnonymousFilter; +import cn.hutool.core.util.ArrayUtil; +import io.dataease.auth.entity.SysUserEntity; +import io.dataease.auth.entity.TokenInfo; +import io.dataease.auth.service.AuthUserService; +import io.dataease.auth.util.JWTUtils; +import io.dataease.commons.license.DefaultLicenseService; +import io.dataease.commons.license.F2CLicenseResponse; +import io.dataease.commons.utils.CommonBeanFactory; +import io.dataease.commons.utils.LogUtil; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.shiro.web.filter.AccessControlFilter; -import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; +import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; -import java.io.IOException; +import javax.servlet.http.HttpServletResponse; +import java.util.Arrays; + +import static io.dataease.commons.license.F2CLicenseResponse.Status; + +public class F2CDocFilter extends AccessControlFilter { + + private static final String RESULT_URI_KEY = "result_uri_key"; + private static final String NOLIC_PAGE = "nolic.html"; + private static final String NO_LOGIN_PAGE = "/nologin.html"; + private static final String DEFAULT_FAILED_PAGE = "/"; -public class F2CDocFilter extends AnonymousFilter { @Override - protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) { - HttpServletRequest req = (HttpServletRequest) request; - String path = "/deApi"; + protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception { + HttpServletRequest request = (HttpServletRequest) servletRequest; try { - req.getRequestDispatcher(path).forward(req, response); - } catch (ServletException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); + DefaultLicenseService defaultLicenseService = CommonBeanFactory.getBean(DefaultLicenseService.class); + F2CLicenseResponse f2CLicenseResponse = defaultLicenseService.validateLicense(); + Status status = f2CLicenseResponse.getStatus(); + if (status != Status.valid) { + request.setAttribute(RESULT_URI_KEY, NOLIC_PAGE); + return false; + } + } catch (Exception e) { + request.setAttribute(RESULT_URI_KEY, NOLIC_PAGE); + LogUtil.error(e.getMessage(), e); + return false; } + + try { + Boolean isLogin = validateLogin(request); + if (!isLogin) { + request.setAttribute(RESULT_URI_KEY, NO_LOGIN_PAGE); + return false; + } + } catch (Exception e) { + request.setAttribute(RESULT_URI_KEY, NO_LOGIN_PAGE); + LogUtil.error(e.getMessage(), e); + return false; + } + return true; } + + private Boolean validateLogin(HttpServletRequest request) throws Exception{ + String authorization = request.getHeader("Authorization"); + if (StringUtils.isBlank(authorization)) { + Cookie[] cookies = request.getCookies(); + if (ArrayUtil.isNotEmpty(cookies)) { + Cookie cookie = Arrays.stream(cookies).filter(item -> StringUtils.equals(item.getName(), "Authorization")).findFirst().orElse(null); + if (ObjectUtils.isNotEmpty(cookie) && StringUtils.isNotBlank(cookie.getValue())) { + authorization = cookie.getValue(); + } + } + } + if (StringUtils.isBlank(authorization)) { + return false; + } + TokenInfo tokenInfo = JWTUtils.tokenInfoByToken(authorization); + AuthUserService authUserService = CommonBeanFactory.getBean(AuthUserService.class); + SysUserEntity user = authUserService.getUserById(tokenInfo.getUserId()); + if (user == null) { + return false; + } + String password = user.getPassword(); + boolean verify = JWTUtils.verify(authorization, tokenInfo, password); + return verify; + } + + @Override + protected boolean onAccessDenied(ServletRequest req, ServletResponse res) throws Exception { + HttpServletResponse response = (HttpServletResponse) res; + HttpServletRequest request = (HttpServletRequest) req; + Object attribute = request.getAttribute(RESULT_URI_KEY); + String path = ObjectUtils.isNotEmpty(attribute) ? attribute.toString() : DEFAULT_FAILED_PAGE; + request.getRequestDispatcher(path).forward(request, response); + return false; + } } diff --git a/backend/src/main/java/io/dataease/auth/filter/JWTFilter.java b/backend/src/main/java/io/dataease/auth/filter/JWTFilter.java index a68673c3c1..2d60576759 100644 --- a/backend/src/main/java/io/dataease/auth/filter/JWTFilter.java +++ b/backend/src/main/java/io/dataease/auth/filter/JWTFilter.java @@ -1,5 +1,6 @@ package io.dataease.auth.filter; +import cn.hutool.core.util.URLUtil; import com.auth0.jwt.algorithms.Algorithm; import io.dataease.auth.entity.ASKToken; import io.dataease.auth.entity.JWTToken; @@ -23,8 +24,10 @@ import org.springframework.web.bind.annotation.RequestMethod; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; +import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import java.nio.charset.Charset; public class JWTFilter extends BasicHttpAuthenticationFilter { @@ -158,4 +161,18 @@ public class JWTFilter extends BasicHttpAuthenticationFilter { httpServletResponse.setHeader("authentication-status", "login_expire"); } + @Override + protected boolean onAccessDenied(ServletRequest req, ServletResponse res, Object mappedValue) throws Exception { + HttpServletResponse response = (HttpServletResponse) res; + HttpServletRequest request = (HttpServletRequest) req; + String requestURI = request.getRequestURI(); + String msg = requestURI + " has been denied"; + String encode = URLUtil.encode(msg, Charset.forName("UTF-8")); + Cookie cookie_error = new Cookie("onAccessDeniedMsg", encode); + cookie_error.setPath("/"); + response.addCookie(cookie_error); + response.sendRedirect("/"); + return false; + } + } diff --git a/backend/src/main/java/io/dataease/auth/service/impl/ShiroServiceImpl.java b/backend/src/main/java/io/dataease/auth/service/impl/ShiroServiceImpl.java index 989e16f350..d6b7c60896 100644 --- a/backend/src/main/java/io/dataease/auth/service/impl/ShiroServiceImpl.java +++ b/backend/src/main/java/io/dataease/auth/service/impl/ShiroServiceImpl.java @@ -11,6 +11,7 @@ import java.util.Map; public class ShiroServiceImpl implements ShiroService { private final static String ANON = "anon"; + private final static String DOC = "doc"; @Override public Map loadFilterChainDefinitionMap() { @@ -20,15 +21,18 @@ public class ShiroServiceImpl implements ShiroService { // ---------------------------------------------------------- // 放行Swagger2页面,需要放行这些 - filterChainDefinitionMap.put("/doc.html**", "doc"); - filterChainDefinitionMap.put("/deApi**", ANON); + filterChainDefinitionMap.put("/doc.html**", DOC); + filterChainDefinitionMap.put("/deApi**", DOC); filterChainDefinitionMap.put("/swagger-ui.html", ANON); filterChainDefinitionMap.put("/swagger-ui/**", ANON); filterChainDefinitionMap.put("/swagger/**", ANON); filterChainDefinitionMap.put("/webjars/**", ANON); - filterChainDefinitionMap.put("/swagger-resources/**", ANON); - filterChainDefinitionMap.put("/v2/**", ANON); - filterChainDefinitionMap.put("/v3/**", ANON); + filterChainDefinitionMap.put("/swagger-resources/**", DOC); + filterChainDefinitionMap.put("/v2/**", DOC); + filterChainDefinitionMap.put("/v3/**", DOC); + + filterChainDefinitionMap.put("/**.gif", ANON); + filterChainDefinitionMap.put("/**.png", ANON); filterChainDefinitionMap.put("/static/**", ANON); filterChainDefinitionMap.put("/css/**", ANON); diff --git a/backend/src/main/java/io/dataease/controller/IndexController.java b/backend/src/main/java/io/dataease/controller/IndexController.java index aacee431cd..857ab7eadc 100644 --- a/backend/src/main/java/io/dataease/controller/IndexController.java +++ b/backend/src/main/java/io/dataease/controller/IndexController.java @@ -2,17 +2,16 @@ package io.dataease.controller; import io.dataease.commons.exception.DEException; import io.dataease.commons.license.DefaultLicenseService; -import io.dataease.commons.license.F2CLicenseResponse; import io.dataease.commons.utils.CodingUtil; import io.dataease.commons.utils.LogUtil; import io.dataease.commons.utils.ServletUtils; import io.dataease.service.panel.PanelLinkService; import org.apache.commons.lang3.StringUtils; -import org.springframework.http.HttpRequest; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; + import javax.annotation.Resource; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; @@ -42,13 +41,7 @@ public class IndexController { @GetMapping("/deApi") public String deApi() { - F2CLicenseResponse f2CLicenseResponse = defaultLicenseService.validateLicense(); - switch (f2CLicenseResponse.getStatus()) { - case valid: - return "doc.html"; - default: - return "nolic.html"; - } + return "doc.html"; } @GetMapping("/link/{index}") @@ -64,8 +57,8 @@ public class IndexController { // TODO 增加仪表板外部参数 HttpServletRequest request = ServletUtils.request(); String attachParams = request.getParameter("attachParams"); - if(StringUtils.isNotEmpty(attachParams)){ - url = url+"&attachParams="+attachParams; + if (StringUtils.isNotEmpty(attachParams)) { + url = url + "&attachParams=" + attachParams; } response.sendRedirect(url); } catch (IOException e) { diff --git a/backend/src/main/java/io/dataease/service/chart/ChartViewService.java b/backend/src/main/java/io/dataease/service/chart/ChartViewService.java index 34b5601182..32d6f0f62d 100644 --- a/backend/src/main/java/io/dataease/service/chart/ChartViewService.java +++ b/backend/src/main/java/io/dataease/service/chart/ChartViewService.java @@ -908,7 +908,7 @@ public class ChartViewService { if (StringUtils.equalsIgnoreCase(table.getType(), DatasetType.DB.name())) { datasourceRequest.setTable(dataTableInfoDTO.getTable()); if (StringUtils.equalsAnyIgnoreCase(view.getType(), "text", "gauge", "liquid")) { - datasourceRequest.setQuery(qp.getSQLSummary(dataTableInfoDTO.getTable(), yAxis, fieldCustomFilter, rowPermissionsTree, extFilterList, view, ds)); + querySql = qp.getSQLSummary(dataTableInfoDTO.getTable(), yAxis, fieldCustomFilter, rowPermissionsTree, extFilterList, view, ds); } else if (StringUtils.containsIgnoreCase(view.getType(), "stack")) { querySql = qp.getSQLStack(dataTableInfoDTO.getTable(), xAxis, yAxis, fieldCustomFilter, rowPermissionsTree, extFilterList, extStack, ds, view); } else if (StringUtils.containsIgnoreCase(view.getType(), "scatter")) { diff --git a/frontend/public/dynamic.gif b/frontend/public/dynamic.gif new file mode 100644 index 0000000000..d70762819a Binary files /dev/null and b/frontend/public/dynamic.gif differ diff --git a/frontend/public/lic.png b/frontend/public/lic.png new file mode 100644 index 0000000000..27f6c77607 Binary files /dev/null and b/frontend/public/lic.png differ diff --git a/frontend/public/nolic.html b/frontend/public/nolic.html index fc6f71162b..f7bb622070 100644 --- a/frontend/public/nolic.html +++ b/frontend/public/nolic.html @@ -1,13 +1,46 @@ + DataEase + + + -
缺少许可
+ +
+ 缺少许可 +
- + + + + \ No newline at end of file diff --git a/frontend/public/nologin.html b/frontend/public/nologin.html new file mode 100644 index 0000000000..d173f43855 --- /dev/null +++ b/frontend/public/nologin.html @@ -0,0 +1,50 @@ + + + + + + + + DataEase + + + + + + +
+ 请先登录,即将跳转! +
+ + + + + + \ No newline at end of file diff --git a/frontend/src/components/canvas/components/editor/Preview.vue b/frontend/src/components/canvas/components/editor/Preview.vue index e4fc09e275..0381b94033 100644 --- a/frontend/src/components/canvas/components/editor/Preview.vue +++ b/frontend/src/components/canvas/components/editor/Preview.vue @@ -272,15 +272,19 @@ export default { width: '100%' } if (this.canvasStyleData.openCommonStyle && this.isMainCanvas()) { - if (this.canvasStyleData.panel.backgroundType === 'image' && this.canvasStyleData.panel.imageUrl) { + const styleInfo = this.terminal === 'mobile' && this.canvasStyleData.panel.mobileSetting && this.canvasStyleData.panel.mobileSetting.customSetting + ? this.canvasStyleData.panel.mobileSetting : this.canvasStyleData.panel + if (styleInfo.backgroundType === 'image' && typeof (styleInfo.imageUrl) === 'string') { style = { - background: `url(${imgUrlTrans(this.canvasStyleData.panel.imageUrl)}) no-repeat`, - ...style + background: `url(${imgUrlTrans(styleInfo.imageUrl)}) no-repeat` } - } else if (this.canvasStyleData.panel.backgroundType === 'color') { + } else if (styleInfo.backgroundType === 'color') { style = { - background: this.canvasStyleData.panel.color, - ...style + background: styleInfo.color + } + } else { + style = { + background: '#f7f8fa' } } } diff --git a/frontend/src/components/canvas/customComponent/UserView.vue b/frontend/src/components/canvas/customComponent/UserView.vue index f6a0cd6c92..cfedd58b54 100644 --- a/frontend/src/components/canvas/customComponent/UserView.vue +++ b/frontend/src/components/canvas/customComponent/UserView.vue @@ -640,7 +640,7 @@ export default { }, viewInCache(param) { this.view = param.view - if (this.view.customAttr) { + if (this.view && this.view.customAttr) { this.currentPage.pageSize = parseInt(JSON.parse(this.view.customAttr).size.tablePageSize) } param.viewId && param.viewId === this.element.propValue.viewId && this.getDataEdit(param) @@ -703,7 +703,7 @@ export default { requestInfo.proxy = { userId: this.panelInfo.proxy } } // table-info明细表增加分页 - if (this.view.customAttr) { + if (this.view && this.view.customAttr) { const attrSize = JSON.parse(this.view.customAttr).size if (this.chart.type === 'table-info' && this.view.datasetMode === 0 && (!attrSize.tablePageMode || attrSize.tablePageMode === 'page')) { requestInfo.goPage = this.currentPage.page @@ -1162,7 +1162,7 @@ export default { queryFrom: 'panel' } // table-info明细表增加分页 - if (this.view.customAttr) { + if (this.view && this.view.customAttr) { const attrSize = JSON.parse(this.view.customAttr).size if (this.chart.type === 'table-info' && this.view.datasetMode === 0 && (!attrSize.tablePageMode || attrSize.tablePageMode === 'page')) { requestInfo.goPage = this.currentPage.page diff --git a/frontend/src/components/canvas/utils/utils.js b/frontend/src/components/canvas/utils/utils.js index a64da813e9..c81866fc7b 100644 --- a/frontend/src/components/canvas/utils/utils.js +++ b/frontend/src/components/canvas/utils/utils.js @@ -7,7 +7,7 @@ import { import { ApplicationContext } from '@/utils/ApplicationContext' import { uuid } from 'vue-uuid' import store from '@/store' -import { AIDED_DESIGN, PANEL_CHART_INFO, TAB_COMMON_STYLE } from '@/views/panel/panel' +import { AIDED_DESIGN, MOBILE_SETTING, PANEL_CHART_INFO, TAB_COMMON_STYLE } from '@/views/panel/panel' import html2canvas from 'html2canvasde' export function deepCopy(target) { @@ -86,6 +86,7 @@ export function panelDataPrepare(componentData, componentStyle, callback) { componentStyle.chartInfo.tabStyle = (componentStyle.chartInfo.tabStyle || deepCopy(TAB_COMMON_STYLE)) componentStyle.themeId = (componentStyle.themeId || 'NO_THEME') componentStyle.panel.themeColor = (componentStyle.panel.themeColor || 'light') + componentStyle.panel.mobileSetting = (componentStyle.panel.mobileSetting || MOBILE_SETTING) componentData.forEach((item, index) => { if (item.component && item.component === 'de-date') { const widget = ApplicationContext.getService(item.serviceName) diff --git a/frontend/src/lang/en.js b/frontend/src/lang/en.js index 4143324f52..9068ffd9bb 100644 --- a/frontend/src/lang/en.js +++ b/frontend/src/lang/en.js @@ -1865,7 +1865,12 @@ export default { sure_bt: 'Confirm' }, panel: { + down: 'Down', + + mobile_style_setting: 'Style setting', + mobile_style_setting_tips: 'Customize the mobile background', + board: 'Border', text: 'Text', board_background: 'Background', diff --git a/frontend/src/lang/tw.js b/frontend/src/lang/tw.js index 0dbd3cf9a4..2814a8f010 100644 --- a/frontend/src/lang/tw.js +++ b/frontend/src/lang/tw.js @@ -1865,7 +1865,12 @@ export default { sure_bt: '確定' }, panel: { + down: '下載', + + mobile_style_setting: '樣式設置', + mobile_style_setting_tips: '自定義移動端背景', + board: '邊框', text: '文字', board_background: '背景', diff --git a/frontend/src/lang/zh.js b/frontend/src/lang/zh.js index 5171a54add..b078c35d21 100644 --- a/frontend/src/lang/zh.js +++ b/frontend/src/lang/zh.js @@ -1865,7 +1865,12 @@ export default { sure_bt: '确定' }, panel: { + down: '下载', + + mobile_style_setting: '样式设置', + mobile_style_setting_tips: '自定义移动端背景', + board: '边框', text: '文字', board_background: '背景', diff --git a/frontend/src/views/panel/edit/ComponentWait.vue b/frontend/src/views/panel/edit/ComponentWait.vue index 3434a4b07a..7740c865e5 100644 --- a/frontend/src/views/panel/edit/ComponentWait.vue +++ b/frontend/src/views/panel/edit/ComponentWait.vue @@ -1,9 +1,22 @@ diff --git a/frontend/src/views/panel/edit/index.vue b/frontend/src/views/panel/edit/index.vue index 5aa71b3586..91ac5dfb85 100644 --- a/frontend/src/views/panel/edit/index.vue +++ b/frontend/src/views/panel/edit/index.vue @@ -251,7 +251,6 @@ @@ -680,13 +679,15 @@ export default { mobileCanvasStyle() { let style if (this.canvasStyleData.openCommonStyle) { - if (this.canvasStyleData.panel.backgroundType === 'image' && typeof (this.canvasStyleData.panel.imageUrl) === 'string') { + const styleInfo = this.canvasStyleData.panel.mobileSetting && this.canvasStyleData.panel.mobileSetting.customSetting + ? this.canvasStyleData.panel.mobileSetting : this.canvasStyleData.panel + if (styleInfo.backgroundType === 'image' && typeof (styleInfo.imageUrl) === 'string') { style = { - background: `url(${imgUrlTrans(this.canvasStyleData.panel.imageUrl)}) no-repeat` + background: `url(${imgUrlTrans(styleInfo.imageUrl)}) no-repeat` } - } else if (this.canvasStyleData.panel.backgroundType === 'color') { + } else if (styleInfo.backgroundType === 'color') { style = { - background: this.canvasStyleData.panel.color + background: styleInfo.color } } else { style = { @@ -1537,7 +1538,7 @@ export default { .this_mobile_canvas_wait_cell { background-size: 100% 100% !important; - border: 2px solid #9ea6b2 + border: 1px solid #9ea6b2 } .canvas_main_content { diff --git a/frontend/src/views/panel/panel.js b/frontend/src/views/panel/panel.js index 55e25fa733..f4cf0e7bc8 100644 --- a/frontend/src/views/panel/panel.js +++ b/frontend/src/views/panel/panel.js @@ -29,7 +29,15 @@ export const FILTER_COMMON_STYLE_DARK = { innerBgColor: '#131E42' } +export const MOBILE_SETTING = { + customSetting: false, + color: '#ffffff', + imageUrl: null, + backgroundType: 'image' +} + export const DEFAULT_PANEL_STYLE = { + mobileSetting: MOBILE_SETTING, themeColor: 'light', color: '#ffffff', imageUrl: null, diff --git a/frontend/src/views/panel/subjectSetting/panelStyle/MobileBackgroundSelector.vue b/frontend/src/views/panel/subjectSetting/panelStyle/MobileBackgroundSelector.vue new file mode 100644 index 0000000000..1c32a570f9 --- /dev/null +++ b/frontend/src/views/panel/subjectSetting/panelStyle/MobileBackgroundSelector.vue @@ -0,0 +1,268 @@ + + + + +