forked from github/dataease
feat: 完善公共链接分享
This commit is contained in:
parent
2f3af28bd3
commit
12a226643c
@ -39,6 +39,11 @@ public class ShiroServiceImpl implements ShiroService {
|
||||
filterChainDefinitionMap.put("/test/**", ANON);
|
||||
filterChainDefinitionMap.put("/index.html", ANON);
|
||||
filterChainDefinitionMap.put("/link.html", ANON);
|
||||
|
||||
//验证链接
|
||||
filterChainDefinitionMap.put("/api/link/validate**", ANON);
|
||||
|
||||
|
||||
filterChainDefinitionMap.put("/api/auth/login", ANON);
|
||||
filterChainDefinitionMap.put("/unauth", ANON);
|
||||
filterChainDefinitionMap.put("/display/**", ANON);
|
||||
|
@ -36,9 +36,9 @@ public interface LinkApi {
|
||||
|
||||
@ApiOperation("验证访问")
|
||||
@PostMapping("/validate")
|
||||
ValidateDto validate(Map<String, String> param);
|
||||
ValidateDto validate(Map<String, String> param) throws Exception;
|
||||
|
||||
@ApiOperation("验证密码")
|
||||
@PostMapping("/validatePwd")
|
||||
boolean validatePwd(PasswordRequest request);
|
||||
boolean validatePwd(PasswordRequest request) throws Exception;
|
||||
}
|
||||
|
@ -49,20 +49,16 @@ public class LinkServer implements LinkApi {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValidateDto validate(@RequestBody Map<String, String> param) {
|
||||
public ValidateDto validate(@RequestBody Map<String, String> param) throws Exception{
|
||||
String link = param.get("link");
|
||||
String json = null;
|
||||
try {
|
||||
json = panelLinkService.decryptParam(link);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
String json = panelLinkService.decryptParam(link);
|
||||
Gson gson = new Gson();
|
||||
|
||||
ValidateRequest request = gson.fromJson(json, ValidateRequest.class);
|
||||
ValidateDto dto = new ValidateDto();
|
||||
String resourceId = request.getResourceId();
|
||||
PanelLink one = panelLinkService.findOne(resourceId);
|
||||
dto.setResourceId(resourceId);
|
||||
if (ObjectUtils.isEmpty(one)){
|
||||
dto.setValid(false);
|
||||
return dto;
|
||||
@ -74,7 +70,7 @@ public class LinkServer implements LinkApi {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validatePwd(@RequestBody PasswordRequest request) {
|
||||
public boolean validatePwd(@RequestBody PasswordRequest request) throws Exception {
|
||||
return panelLinkService.validatePwd(request);
|
||||
}
|
||||
}
|
||||
|
@ -10,4 +10,6 @@ public class ValidateDto {
|
||||
private boolean enablePwd;
|
||||
|
||||
private boolean passPwd;
|
||||
|
||||
private String resourceId;
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ import java.util.Map;
|
||||
@Service
|
||||
public class PanelLinkService {
|
||||
|
||||
@Value("${public-link-url:http://localhost:8081/link?link=}")
|
||||
@Value("${public-link-url:http://localhost:9528/link.html?link=}")
|
||||
private String baseUrl;
|
||||
|
||||
@Value("${public-link-salt:DataEaseLinkSalt}")
|
||||
@ -113,19 +113,19 @@ public class PanelLinkService {
|
||||
}
|
||||
|
||||
// 验证请求头部携带的信息 如果正确说明通过密码验证 否则没有通过
|
||||
public Boolean validateHeads(PanelLink panelLink){
|
||||
public Boolean validateHeads(PanelLink panelLink) throws Exception{
|
||||
HttpServletRequest request = ServletUtils.request();
|
||||
String token = request.getHeader("LINK-PWD-TOKEN");
|
||||
if (StringUtils.isEmpty(token)) return false;
|
||||
boolean verify = JWTUtils.verifyLink(token, panelLink.getResourceId(), panelLink.getPwd());
|
||||
boolean verify = JWTUtils.verifyLink(token, panelLink.getResourceId(), decryptParam(panelLink.getPwd()));
|
||||
return verify;
|
||||
}
|
||||
|
||||
public boolean validatePwd(PasswordRequest request) {
|
||||
String password = request.getPassword();
|
||||
public boolean validatePwd(PasswordRequest request) throws Exception {
|
||||
String password = decryptParam(request.getPassword());
|
||||
String resourceId = request.getResourceId();
|
||||
PanelLink one = findOne(resourceId);
|
||||
String pwd = one.getPwd();
|
||||
String pwd = decryptParam(one.getPwd());
|
||||
boolean pass = StringUtils.equals(pwd, password);
|
||||
if (pass){
|
||||
String token = JWTUtils.signLink(resourceId, password);
|
||||
|
@ -5,6 +5,7 @@ export function validate(data) {
|
||||
url: 'api/link/validate',
|
||||
method: 'post',
|
||||
loading: true,
|
||||
hideMsg: true,
|
||||
data
|
||||
})
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import Vue from 'vue'
|
||||
import Link from './Link.vue'
|
||||
import router from './link-router'
|
||||
import store from '../store'
|
||||
import '@/styles/index.scss' // global css
|
||||
import i18n from '../lang' // internationalization
|
||||
import ElementUI from 'element-ui'
|
||||
@ -11,5 +12,6 @@ Vue.use(ElementUI, {
|
||||
})
|
||||
new Vue({
|
||||
router,
|
||||
store,
|
||||
render: h => h(Link)
|
||||
}).$mount('#link')
|
||||
|
@ -17,7 +17,6 @@ const getters = {
|
||||
table: state => state.dataset.table,
|
||||
loadingMap: state => state.request.loadingMap,
|
||||
currentPath: state => state.permission.currentPath,
|
||||
permissions: state => state.user.permissions,
|
||||
linkToken: state => state.link.linkToken
|
||||
permissions: state => state.user.permissions
|
||||
}
|
||||
export default getters
|
||||
|
@ -9,7 +9,6 @@ import dataset from './modules/dataset'
|
||||
import chart from './modules/chart'
|
||||
import request from './modules/request'
|
||||
import panel from './modules/panel'
|
||||
import link from './modules/link'
|
||||
import animation from './animation'
|
||||
import compose from './compose'
|
||||
import contextmenu from './contextmenu'
|
||||
@ -112,8 +111,7 @@ const data = {
|
||||
dataset,
|
||||
chart,
|
||||
request,
|
||||
panel,
|
||||
link
|
||||
panel
|
||||
},
|
||||
getters
|
||||
}
|
||||
|
@ -1,24 +0,0 @@
|
||||
|
||||
const state = {
|
||||
linkToken: null
|
||||
}
|
||||
|
||||
const mutations = {
|
||||
SET_LINK_TOKEN: (state, value) => {
|
||||
state.linkToken = value
|
||||
}
|
||||
}
|
||||
|
||||
const actions = {
|
||||
setLinkToken({ commit }, data) {
|
||||
commit('SET_LINK_TOKEN', data)
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state,
|
||||
mutations,
|
||||
actions
|
||||
}
|
||||
|
@ -3,6 +3,8 @@ import Config from '@/settings'
|
||||
|
||||
const TokenKey = Config.TokenKey
|
||||
|
||||
const linkTokenKey = Config.LinkTokenKey
|
||||
|
||||
export function getToken() {
|
||||
return Cookies.get(TokenKey)
|
||||
}
|
||||
@ -15,3 +17,15 @@ export function removeToken() {
|
||||
return Cookies.remove(TokenKey)
|
||||
}
|
||||
|
||||
export function getLinkToken() {
|
||||
return Cookies.get(linkTokenKey)
|
||||
}
|
||||
|
||||
export function setLinkToken(token) {
|
||||
return Cookies.set(linkTokenKey, token)
|
||||
}
|
||||
|
||||
export function removeLinkToken() {
|
||||
return Cookies.remove(linkTokenKey)
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ import { getToken } from '@/utils/auth'
|
||||
import Config from '@/settings'
|
||||
import i18n from '@/lang'
|
||||
import { tryShowLoading, tryHideLoading } from './loading'
|
||||
import { getLinkToken, setLinkToken } from '@/utils/auth'
|
||||
// import router from '@/router'
|
||||
|
||||
const TokenKey = Config.TokenKey
|
||||
@ -29,8 +30,9 @@ service.interceptors.request.use(
|
||||
// please modify it according to the actual situation
|
||||
config.headers[TokenKey] = getToken()
|
||||
}
|
||||
if (store.getters.linkToken) {
|
||||
config.headers[LinkTokenKey] = store.getters.linkToken
|
||||
let linkToken = null
|
||||
if ((linkToken = getLinkToken()) !== null) {
|
||||
config.headers[LinkTokenKey] = linkToken
|
||||
}
|
||||
// 增加loading
|
||||
|
||||
@ -73,9 +75,9 @@ const checkAuth = response => {
|
||||
store.dispatch('user/refreshToken', refreshToken)
|
||||
}
|
||||
|
||||
if (response.headers[LinkTokenKey]) {
|
||||
const linkToken = response.headers[LinkTokenKey]
|
||||
store.dispatch('link/setLinkToken', linkToken)
|
||||
if (response.headers[LinkTokenKey.toLocaleLowerCase()]) {
|
||||
const linkToken = response.headers[LinkTokenKey.toLocaleLowerCase()]
|
||||
setLinkToken(linkToken)
|
||||
}
|
||||
}
|
||||
|
||||
@ -147,7 +149,7 @@ service.interceptors.response.use(response => {
|
||||
console.log('error: ' + error) // for debug
|
||||
msg = error.message
|
||||
}
|
||||
$error(msg)
|
||||
!error.config.hideMsg && $error(msg)
|
||||
return Promise.reject(error)
|
||||
})
|
||||
export default service
|
||||
|
@ -1,6 +1,37 @@
|
||||
<template>
|
||||
<div>
|
||||
我是错误页面
|
||||
<div class="pwd-body">
|
||||
|
||||
<div class="countdown">
|
||||
<svg
|
||||
:width="size"
|
||||
:height="size"
|
||||
>
|
||||
<circle
|
||||
fill="transparent"
|
||||
:stroke-width="stroke"
|
||||
stroke="#270B58"
|
||||
:r="radius"
|
||||
:cx="circleOffset"
|
||||
:cy="circleOffset"
|
||||
/>
|
||||
<circle
|
||||
class="circle"
|
||||
fill="transparent"
|
||||
:stroke-width="stroke"
|
||||
stroke="#F36F21"
|
||||
:r="radius"
|
||||
:cx="circleOffset"
|
||||
:cy="circleOffset"
|
||||
:stroke-dasharray="circumference"
|
||||
:stroke-dashoffset="progress"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
</svg>
|
||||
<span>{{ countdown }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<h1>链接无效 即将跳转登录页面</h1>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -9,11 +40,81 @@ export default {
|
||||
name: 'LinkError',
|
||||
data() {
|
||||
return {
|
||||
|
||||
size: 200,
|
||||
stroke: 30,
|
||||
percentage: 100,
|
||||
timer: null,
|
||||
seconds: 5
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
computed: {
|
||||
radius() {
|
||||
return (this.size / 2) - (this.stroke / 2)
|
||||
},
|
||||
circleOffset() {
|
||||
return this.size / 2
|
||||
},
|
||||
circumference() {
|
||||
return this.radius * 2 * Math.PI
|
||||
},
|
||||
progress() {
|
||||
return this.circumference - this.circumference * this.percentage / 100
|
||||
},
|
||||
countdown() {
|
||||
return Math.ceil(this.seconds * this.percentage / 100)
|
||||
}
|
||||
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
this.animate()
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
animate() {
|
||||
this.timer = setInterval(() => {
|
||||
this.percentage -= 1 / 10
|
||||
|
||||
if (this.percentage <= 0) {
|
||||
clearInterval(this.timer)
|
||||
this.percentage = 100
|
||||
this.$store.dispatch('user/logout')
|
||||
window.location.href = '/login'
|
||||
}
|
||||
}, this.seconds * 1000 / 100 / 10)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.circle {
|
||||
transform: rotate(-90deg);
|
||||
transform-origin: 50% 50%;;
|
||||
}
|
||||
|
||||
.countdown {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
margin-top: 15%;
|
||||
}
|
||||
|
||||
span {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translateX(-50%) translateY(-50%);
|
||||
font-size: 180px;
|
||||
font-family: monospace;
|
||||
color: #F36F21;
|
||||
}
|
||||
|
||||
.pwd-body {
|
||||
display: block;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
text-align: center;
|
||||
background-color: #F7F8FA;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div>
|
||||
<link-error v-if="showIndex===0" />
|
||||
<link-error v-if="showIndex===0" :resource-id="resourceId" />
|
||||
<link-pwd v-if="showIndex===1" :resource-id="resourceId" />
|
||||
<link-view v-if="showIndex===2" />
|
||||
<link-view v-if="showIndex===2" :resource-id="resourceId" />
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
@ -17,6 +17,7 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
resourceId: null,
|
||||
PARAMKEY: 'link',
|
||||
link: null,
|
||||
showIndex: -1
|
||||
@ -30,9 +31,10 @@ export default {
|
||||
loadInit() {
|
||||
this.link = getQueryVariable(this.PARAMKEY)
|
||||
validate({ link: this.link }).then(res => {
|
||||
const { valid, enablePwd, passPwd } = res.data
|
||||
const { resourceId, valid, enablePwd, passPwd } = res.data
|
||||
this.resourceId = resourceId
|
||||
// 如果链接无效 直接显示无效页面
|
||||
if (!valid) {
|
||||
if (!valid || !resourceId) {
|
||||
this.showError()
|
||||
return
|
||||
}
|
||||
@ -43,6 +45,8 @@ export default {
|
||||
}
|
||||
|
||||
this.showView()
|
||||
}).catch(() => {
|
||||
this.showError()
|
||||
})
|
||||
},
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
|
||||
<div class="auth-root-class">
|
||||
<span slot="footer">
|
||||
<el-button size="mini" type="primary" @click="validatePwd">确定</el-button>
|
||||
<el-button size="mini" type="primary" @click="refresh">确定</el-button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -33,6 +33,8 @@
|
||||
|
||||
<script>
|
||||
import { validatePwd } from '@/api/link'
|
||||
import { encrypt } from '@/utils/rsaEncrypt'
|
||||
|
||||
export default {
|
||||
name: 'LinkPwd',
|
||||
props: {
|
||||
@ -47,10 +49,15 @@ export default {
|
||||
msg: null
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 验证密码是否正确 如果正确 设置请求头部带LINK-PWD-TOKEN=entrypt(pwd)再刷新页面
|
||||
refresh() {
|
||||
validatePwd({ password: this.pwd, resourceId: this.resourceId }).then(res => {
|
||||
const param = {
|
||||
password: encrypt(this.pwd),
|
||||
resourceId: this.resourceId
|
||||
}
|
||||
validatePwd(param).then(res => {
|
||||
if (!res.data) {
|
||||
this.msg = '密码错误'
|
||||
} else {
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
我是视图7u页面
|
||||
我是视图页面
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -132,7 +132,7 @@
|
||||
<el-dialog
|
||||
:title="linkTitle"
|
||||
:visible.sync="linkVisible"
|
||||
custom-class="de-dialog"
|
||||
width="500px"
|
||||
@closed="removeLink"
|
||||
>
|
||||
<link-generate v-if="linkVisible" :resource-id="linkResourceId" />
|
||||
|
Loading…
Reference in New Issue
Block a user