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("/test/**", ANON);
|
||||||
filterChainDefinitionMap.put("/index.html", ANON);
|
filterChainDefinitionMap.put("/index.html", ANON);
|
||||||
filterChainDefinitionMap.put("/link.html", ANON);
|
filterChainDefinitionMap.put("/link.html", ANON);
|
||||||
|
|
||||||
|
//验证链接
|
||||||
|
filterChainDefinitionMap.put("/api/link/validate**", ANON);
|
||||||
|
|
||||||
|
|
||||||
filterChainDefinitionMap.put("/api/auth/login", ANON);
|
filterChainDefinitionMap.put("/api/auth/login", ANON);
|
||||||
filterChainDefinitionMap.put("/unauth", ANON);
|
filterChainDefinitionMap.put("/unauth", ANON);
|
||||||
filterChainDefinitionMap.put("/display/**", ANON);
|
filterChainDefinitionMap.put("/display/**", ANON);
|
||||||
|
|||||||
@ -36,9 +36,9 @@ public interface LinkApi {
|
|||||||
|
|
||||||
@ApiOperation("验证访问")
|
@ApiOperation("验证访问")
|
||||||
@PostMapping("/validate")
|
@PostMapping("/validate")
|
||||||
ValidateDto validate(Map<String, String> param);
|
ValidateDto validate(Map<String, String> param) throws Exception;
|
||||||
|
|
||||||
@ApiOperation("验证密码")
|
@ApiOperation("验证密码")
|
||||||
@PostMapping("/validatePwd")
|
@PostMapping("/validatePwd")
|
||||||
boolean validatePwd(PasswordRequest request);
|
boolean validatePwd(PasswordRequest request) throws Exception;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -49,20 +49,16 @@ public class LinkServer implements LinkApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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 link = param.get("link");
|
||||||
String json = null;
|
String json = panelLinkService.decryptParam(link);
|
||||||
try {
|
|
||||||
json = panelLinkService.decryptParam(link);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
Gson gson = new Gson();
|
Gson gson = new Gson();
|
||||||
|
|
||||||
ValidateRequest request = gson.fromJson(json, ValidateRequest.class);
|
ValidateRequest request = gson.fromJson(json, ValidateRequest.class);
|
||||||
ValidateDto dto = new ValidateDto();
|
ValidateDto dto = new ValidateDto();
|
||||||
String resourceId = request.getResourceId();
|
String resourceId = request.getResourceId();
|
||||||
PanelLink one = panelLinkService.findOne(resourceId);
|
PanelLink one = panelLinkService.findOne(resourceId);
|
||||||
|
dto.setResourceId(resourceId);
|
||||||
if (ObjectUtils.isEmpty(one)){
|
if (ObjectUtils.isEmpty(one)){
|
||||||
dto.setValid(false);
|
dto.setValid(false);
|
||||||
return dto;
|
return dto;
|
||||||
@ -74,7 +70,7 @@ public class LinkServer implements LinkApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean validatePwd(@RequestBody PasswordRequest request) {
|
public boolean validatePwd(@RequestBody PasswordRequest request) throws Exception {
|
||||||
return panelLinkService.validatePwd(request);
|
return panelLinkService.validatePwd(request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,4 +10,6 @@ public class ValidateDto {
|
|||||||
private boolean enablePwd;
|
private boolean enablePwd;
|
||||||
|
|
||||||
private boolean passPwd;
|
private boolean passPwd;
|
||||||
|
|
||||||
|
private String resourceId;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,7 +25,7 @@ import java.util.Map;
|
|||||||
@Service
|
@Service
|
||||||
public class PanelLinkService {
|
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;
|
private String baseUrl;
|
||||||
|
|
||||||
@Value("${public-link-salt:DataEaseLinkSalt}")
|
@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();
|
HttpServletRequest request = ServletUtils.request();
|
||||||
String token = request.getHeader("LINK-PWD-TOKEN");
|
String token = request.getHeader("LINK-PWD-TOKEN");
|
||||||
if (StringUtils.isEmpty(token)) return false;
|
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;
|
return verify;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean validatePwd(PasswordRequest request) {
|
public boolean validatePwd(PasswordRequest request) throws Exception {
|
||||||
String password = request.getPassword();
|
String password = decryptParam(request.getPassword());
|
||||||
String resourceId = request.getResourceId();
|
String resourceId = request.getResourceId();
|
||||||
PanelLink one = findOne(resourceId);
|
PanelLink one = findOne(resourceId);
|
||||||
String pwd = one.getPwd();
|
String pwd = decryptParam(one.getPwd());
|
||||||
boolean pass = StringUtils.equals(pwd, password);
|
boolean pass = StringUtils.equals(pwd, password);
|
||||||
if (pass){
|
if (pass){
|
||||||
String token = JWTUtils.signLink(resourceId, password);
|
String token = JWTUtils.signLink(resourceId, password);
|
||||||
|
|||||||
@ -5,6 +5,7 @@ export function validate(data) {
|
|||||||
url: 'api/link/validate',
|
url: 'api/link/validate',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
loading: true,
|
loading: true,
|
||||||
|
hideMsg: true,
|
||||||
data
|
data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import Link from './Link.vue'
|
import Link from './Link.vue'
|
||||||
import router from './link-router'
|
import router from './link-router'
|
||||||
|
import store from '../store'
|
||||||
import '@/styles/index.scss' // global css
|
import '@/styles/index.scss' // global css
|
||||||
import i18n from '../lang' // internationalization
|
import i18n from '../lang' // internationalization
|
||||||
import ElementUI from 'element-ui'
|
import ElementUI from 'element-ui'
|
||||||
@ -11,5 +12,6 @@ Vue.use(ElementUI, {
|
|||||||
})
|
})
|
||||||
new Vue({
|
new Vue({
|
||||||
router,
|
router,
|
||||||
|
store,
|
||||||
render: h => h(Link)
|
render: h => h(Link)
|
||||||
}).$mount('#link')
|
}).$mount('#link')
|
||||||
|
|||||||
@ -17,7 +17,6 @@ const getters = {
|
|||||||
table: state => state.dataset.table,
|
table: state => state.dataset.table,
|
||||||
loadingMap: state => state.request.loadingMap,
|
loadingMap: state => state.request.loadingMap,
|
||||||
currentPath: state => state.permission.currentPath,
|
currentPath: state => state.permission.currentPath,
|
||||||
permissions: state => state.user.permissions,
|
permissions: state => state.user.permissions
|
||||||
linkToken: state => state.link.linkToken
|
|
||||||
}
|
}
|
||||||
export default getters
|
export default getters
|
||||||
|
|||||||
@ -9,7 +9,6 @@ import dataset from './modules/dataset'
|
|||||||
import chart from './modules/chart'
|
import chart from './modules/chart'
|
||||||
import request from './modules/request'
|
import request from './modules/request'
|
||||||
import panel from './modules/panel'
|
import panel from './modules/panel'
|
||||||
import link from './modules/link'
|
|
||||||
import animation from './animation'
|
import animation from './animation'
|
||||||
import compose from './compose'
|
import compose from './compose'
|
||||||
import contextmenu from './contextmenu'
|
import contextmenu from './contextmenu'
|
||||||
@ -112,8 +111,7 @@ const data = {
|
|||||||
dataset,
|
dataset,
|
||||||
chart,
|
chart,
|
||||||
request,
|
request,
|
||||||
panel,
|
panel
|
||||||
link
|
|
||||||
},
|
},
|
||||||
getters
|
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 TokenKey = Config.TokenKey
|
||||||
|
|
||||||
|
const linkTokenKey = Config.LinkTokenKey
|
||||||
|
|
||||||
export function getToken() {
|
export function getToken() {
|
||||||
return Cookies.get(TokenKey)
|
return Cookies.get(TokenKey)
|
||||||
}
|
}
|
||||||
@ -15,3 +17,15 @@ export function removeToken() {
|
|||||||
return Cookies.remove(TokenKey)
|
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 Config from '@/settings'
|
||||||
import i18n from '@/lang'
|
import i18n from '@/lang'
|
||||||
import { tryShowLoading, tryHideLoading } from './loading'
|
import { tryShowLoading, tryHideLoading } from './loading'
|
||||||
|
import { getLinkToken, setLinkToken } from '@/utils/auth'
|
||||||
// import router from '@/router'
|
// import router from '@/router'
|
||||||
|
|
||||||
const TokenKey = Config.TokenKey
|
const TokenKey = Config.TokenKey
|
||||||
@ -29,8 +30,9 @@ service.interceptors.request.use(
|
|||||||
// please modify it according to the actual situation
|
// please modify it according to the actual situation
|
||||||
config.headers[TokenKey] = getToken()
|
config.headers[TokenKey] = getToken()
|
||||||
}
|
}
|
||||||
if (store.getters.linkToken) {
|
let linkToken = null
|
||||||
config.headers[LinkTokenKey] = store.getters.linkToken
|
if ((linkToken = getLinkToken()) !== null) {
|
||||||
|
config.headers[LinkTokenKey] = linkToken
|
||||||
}
|
}
|
||||||
// 增加loading
|
// 增加loading
|
||||||
|
|
||||||
@ -73,9 +75,9 @@ const checkAuth = response => {
|
|||||||
store.dispatch('user/refreshToken', refreshToken)
|
store.dispatch('user/refreshToken', refreshToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.headers[LinkTokenKey]) {
|
if (response.headers[LinkTokenKey.toLocaleLowerCase()]) {
|
||||||
const linkToken = response.headers[LinkTokenKey]
|
const linkToken = response.headers[LinkTokenKey.toLocaleLowerCase()]
|
||||||
store.dispatch('link/setLinkToken', linkToken)
|
setLinkToken(linkToken)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,7 +149,7 @@ service.interceptors.response.use(response => {
|
|||||||
console.log('error: ' + error) // for debug
|
console.log('error: ' + error) // for debug
|
||||||
msg = error.message
|
msg = error.message
|
||||||
}
|
}
|
||||||
$error(msg)
|
!error.config.hideMsg && $error(msg)
|
||||||
return Promise.reject(error)
|
return Promise.reject(error)
|
||||||
})
|
})
|
||||||
export default service
|
export default service
|
||||||
|
|||||||
@ -1,6 +1,37 @@
|
|||||||
<template>
|
<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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -9,11 +40,81 @@ export default {
|
|||||||
name: 'LinkError',
|
name: 'LinkError',
|
||||||
data() {
|
data() {
|
||||||
return {
|
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>
|
</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>
|
<template>
|
||||||
<div>
|
<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-pwd v-if="showIndex===1" :resource-id="resourceId" />
|
||||||
<link-view v-if="showIndex===2" />
|
<link-view v-if="showIndex===2" :resource-id="resourceId" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
@ -17,6 +17,7 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
resourceId: null,
|
||||||
PARAMKEY: 'link',
|
PARAMKEY: 'link',
|
||||||
link: null,
|
link: null,
|
||||||
showIndex: -1
|
showIndex: -1
|
||||||
@ -30,9 +31,10 @@ export default {
|
|||||||
loadInit() {
|
loadInit() {
|
||||||
this.link = getQueryVariable(this.PARAMKEY)
|
this.link = getQueryVariable(this.PARAMKEY)
|
||||||
validate({ link: this.link }).then(res => {
|
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()
|
this.showError()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -43,6 +45,8 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.showView()
|
this.showView()
|
||||||
|
}).catch(() => {
|
||||||
|
this.showError()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
<div class="auth-root-class">
|
<div class="auth-root-class">
|
||||||
<span slot="footer">
|
<span slot="footer">
|
||||||
<el-button size="mini" type="primary" @click="validatePwd">确定</el-button>
|
<el-button size="mini" type="primary" @click="refresh">确定</el-button>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -33,6 +33,8 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { validatePwd } from '@/api/link'
|
import { validatePwd } from '@/api/link'
|
||||||
|
import { encrypt } from '@/utils/rsaEncrypt'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'LinkPwd',
|
name: 'LinkPwd',
|
||||||
props: {
|
props: {
|
||||||
@ -47,10 +49,15 @@ export default {
|
|||||||
msg: null
|
msg: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
// 验证密码是否正确 如果正确 设置请求头部带LINK-PWD-TOKEN=entrypt(pwd)再刷新页面
|
// 验证密码是否正确 如果正确 设置请求头部带LINK-PWD-TOKEN=entrypt(pwd)再刷新页面
|
||||||
refresh() {
|
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) {
|
if (!res.data) {
|
||||||
this.msg = '密码错误'
|
this.msg = '密码错误'
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
我是视图7u页面
|
我是视图页面
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@ -132,7 +132,7 @@
|
|||||||
<el-dialog
|
<el-dialog
|
||||||
:title="linkTitle"
|
:title="linkTitle"
|
||||||
:visible.sync="linkVisible"
|
:visible.sync="linkVisible"
|
||||||
custom-class="de-dialog"
|
width="500px"
|
||||||
@closed="removeLink"
|
@closed="removeLink"
|
||||||
>
|
>
|
||||||
<link-generate v-if="linkVisible" :resource-id="linkResourceId" />
|
<link-generate v-if="linkVisible" :resource-id="linkResourceId" />
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user