forked from github/dataease
Merge branch 'dev' into pr@dev_memory_component
This commit is contained in:
commit
9b39bd7e18
@ -79,12 +79,7 @@ public class F2CDocFilter extends AccessControlFilter {
|
||||
if (StringUtils.isBlank(authorization)) {
|
||||
return false;
|
||||
}
|
||||
if (JWTUtils.loginExpire(authorization)) {
|
||||
return false;
|
||||
}
|
||||
if (JWTUtils.needRefresh(authorization)) {
|
||||
authorization = refreshToken(authorization);
|
||||
}
|
||||
|
||||
TokenInfo tokenInfo = JWTUtils.tokenInfoByToken(authorization);
|
||||
AuthUserService authUserService = CommonBeanFactory.getBean(AuthUserService.class);
|
||||
SysUserEntity user = authUserService.getUserById(tokenInfo.getUserId());
|
||||
@ -96,20 +91,6 @@ public class F2CDocFilter extends AccessControlFilter {
|
||||
return verify;
|
||||
}
|
||||
|
||||
private String refreshToken(String token) throws Exception {
|
||||
TokenInfo tokenInfo = JWTUtils.tokenInfoByToken(token);
|
||||
AuthUserService authUserService = CommonBeanFactory.getBean(AuthUserService.class);
|
||||
SysUserEntity user = authUserService.getUserById(tokenInfo.getUserId());
|
||||
if (user == null) {
|
||||
DataEaseException.throwException(Translator.get("i18n_not_find_user"));
|
||||
}
|
||||
String password = user.getPassword();
|
||||
Algorithm algorithm = Algorithm.HMAC256(password);
|
||||
JWTUtils.verifySign(algorithm, token);
|
||||
String newToken = JWTUtils.sign(tokenInfo, password);
|
||||
return newToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onAccessDenied(ServletRequest req, ServletResponse res) throws Exception {
|
||||
HttpServletResponse response = (HttpServletResponse) res;
|
||||
|
@ -1,24 +1,18 @@
|
||||
package io.dataease.auth.filter;
|
||||
|
||||
import com.auth0.jwt.algorithms.Algorithm;
|
||||
import io.dataease.auth.entity.ASKToken;
|
||||
import io.dataease.auth.entity.JWTToken;
|
||||
import io.dataease.auth.entity.SysUserEntity;
|
||||
import io.dataease.auth.entity.TokenInfo;
|
||||
|
||||
import io.dataease.auth.handler.ApiKeyHandler;
|
||||
import io.dataease.auth.service.AuthUserService;
|
||||
import io.dataease.auth.util.JWTUtils;
|
||||
import io.dataease.commons.utils.CommonBeanFactory;
|
||||
|
||||
import io.dataease.commons.utils.LogUtil;
|
||||
import io.dataease.commons.utils.TokenCacheUtils;
|
||||
import io.dataease.exception.DataEaseException;
|
||||
import io.dataease.i18n.Translator;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.shiro.authc.AuthenticationException;
|
||||
import org.apache.shiro.subject.Subject;
|
||||
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
|
||||
@ -30,7 +24,6 @@ import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
public class JWTFilter extends BasicHttpAuthenticationFilter {
|
||||
|
||||
private Logger LOGGER = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
public final static String expireMessage = "Login token is expire.";
|
||||
|
||||
@ -69,14 +62,7 @@ public class JWTFilter extends BasicHttpAuthenticationFilter {
|
||||
if (TokenCacheUtils.invalid(authorization)) {
|
||||
throw new AuthenticationException(expireMessage);
|
||||
}
|
||||
// 当没有出现登录超时 且需要刷新token 则执行刷新token
|
||||
if (JWTUtils.loginExpire(authorization)) {
|
||||
TokenCacheUtils.remove(authorization);
|
||||
throw new AuthenticationException(expireMessage);
|
||||
}
|
||||
if (JWTUtils.needRefresh(authorization)) {
|
||||
authorization = refreshToken(request, response);
|
||||
}
|
||||
|
||||
JWTToken token = new JWTToken(authorization);
|
||||
Subject subject = getSubject(request, response);
|
||||
// 提交给realm进行登入,如果错误他会抛出异常并被捕获
|
||||
@ -110,28 +96,6 @@ public class JWTFilter extends BasicHttpAuthenticationFilter {
|
||||
}
|
||||
|
||||
|
||||
private String refreshToken(ServletRequest request, ServletResponse response) throws Exception {
|
||||
// 获取AccessToken(Shiro中getAuthzHeader方法已经实现)
|
||||
String token = this.getAuthzHeader(request);
|
||||
// 获取当前Token的帐号信息
|
||||
TokenInfo tokenInfo = JWTUtils.tokenInfoByToken(token);
|
||||
AuthUserService authUserService = CommonBeanFactory.getBean(AuthUserService.class);
|
||||
SysUserEntity user = authUserService.getUserById(tokenInfo.getUserId());
|
||||
if (user == null) {
|
||||
DataEaseException.throwException(Translator.get("i18n_not_find_user"));
|
||||
}
|
||||
String password = user.getPassword();
|
||||
Algorithm algorithm = Algorithm.HMAC256(password);
|
||||
JWTUtils.verifySign(algorithm, token);
|
||||
String newToken = JWTUtils.sign(tokenInfo, password);
|
||||
// 设置响应的Header头新Token
|
||||
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
|
||||
httpServletResponse.addHeader("Access-Control-Expose-Headers", "RefreshAuthorization");
|
||||
httpServletResponse.setHeader("RefreshAuthorization", newToken);
|
||||
return newToken;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 对跨域提供支持
|
||||
*/
|
||||
|
@ -4,7 +4,6 @@ import com.auth0.jwt.JWT;
|
||||
import com.auth0.jwt.JWTVerifier;
|
||||
import com.auth0.jwt.JWTCreator.Builder;
|
||||
import com.auth0.jwt.algorithms.Algorithm;
|
||||
import com.auth0.jwt.exceptions.JWTDecodeException;
|
||||
import com.auth0.jwt.interfaces.DecodedJWT;
|
||||
import com.auth0.jwt.interfaces.Verification;
|
||||
import io.dataease.auth.entity.TokenInfo;
|
||||
@ -19,10 +18,8 @@ import java.util.Date;
|
||||
|
||||
public class JWTUtils {
|
||||
|
||||
// token过期时间1min (过期会自动刷新续命 目的是避免一直都是同一个token )
|
||||
private static final long EXPIRE_TIME = 1 * 60 * 1000;
|
||||
// 登录间隔时间10min 超过这个时间强制重新登录
|
||||
private static long Login_Interval;
|
||||
|
||||
private static Long expireTime;
|
||||
|
||||
/**
|
||||
* 校验token是否正确
|
||||
@ -65,62 +62,24 @@ public class JWTUtils {
|
||||
return tokenInfoBuilder.build();
|
||||
}
|
||||
|
||||
public static boolean needRefresh(String token) {
|
||||
Date exp = JWTUtils.getExp(token);
|
||||
Long advanceTime = 5000L;
|
||||
return (new Date().getTime() + advanceTime) >= exp.getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前token是否登录超时
|
||||
*
|
||||
* @param token
|
||||
* @return
|
||||
*/
|
||||
public static boolean loginExpire(String token) {
|
||||
if (Login_Interval == 0) {
|
||||
// 默认超时时间是8h
|
||||
Long minute = CommonBeanFactory.getBean(Environment.class).getProperty("dataease.login_timeout", Long.class,
|
||||
8 * 60L);
|
||||
// 分钟换算成毫秒
|
||||
Login_Interval = minute * 1000 * 60;
|
||||
}
|
||||
Long lastOperateTime = tokenLastOperateTime(token);
|
||||
boolean isExpire = true;
|
||||
if (lastOperateTime != null) {
|
||||
Long now = System.currentTimeMillis();
|
||||
isExpire = now - lastOperateTime > Login_Interval;
|
||||
}
|
||||
return isExpire;
|
||||
}
|
||||
|
||||
public static Date getExp(String token) {
|
||||
try {
|
||||
DecodedJWT jwt = JWT.decode(token);
|
||||
return jwt.getClaim("exp").asDate();
|
||||
} catch (JWTDecodeException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成签名,5min后过期
|
||||
*
|
||||
* @param tokenInfo 用户信息
|
||||
* @param secret 用户的密码
|
||||
* @return 加密的token
|
||||
*/
|
||||
public static String sign(TokenInfo tokenInfo, String secret) {
|
||||
try {
|
||||
Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
|
||||
if (ObjectUtils.isEmpty(expireTime)) {
|
||||
expireTime = CommonBeanFactory.getBean(Environment.class).getProperty("dataease.login_timeout", Long.class, 480L);
|
||||
}
|
||||
long expireTimeMillis = expireTime * 60000L;
|
||||
Date date = new Date(System.currentTimeMillis() + expireTimeMillis);
|
||||
Algorithm algorithm = Algorithm.HMAC256(secret);
|
||||
Builder builder = JWT.create()
|
||||
.withClaim("username", tokenInfo.getUsername())
|
||||
.withClaim("userId", tokenInfo.getUserId());
|
||||
String sign = builder.withExpiresAt(date).sign(algorithm);
|
||||
return sign;
|
||||
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
@ -143,7 +102,6 @@ public class JWTUtils {
|
||||
} else {
|
||||
verifier = JWT.require(algorithm).withClaim("resourceId", resourceId).withClaim("userId", userId).build();
|
||||
}
|
||||
|
||||
try {
|
||||
verifier.verify(token);
|
||||
return true;
|
||||
@ -152,16 +110,5 @@ public class JWTUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前token上次操作时间
|
||||
*
|
||||
* @param token
|
||||
* @return
|
||||
*/
|
||||
public static Long tokenLastOperateTime(String token) {
|
||||
DecodedJWT jwt = JWT.decode(token);
|
||||
Date expiresAt = jwt.getExpiresAt();
|
||||
return expiresAt.getTime();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -50,8 +50,9 @@ public class TokenCacheUtils {
|
||||
}
|
||||
|
||||
Long time = expTime * 60;
|
||||
CacheUtils.put(KEY, token, userId, time.intValue(), null);
|
||||
|
||||
Double v = time * 0.6;
|
||||
CacheUtils.put(KEY, token, userId, time.intValue(), v.intValue());
|
||||
CacheUtils.flush(KEY);
|
||||
}
|
||||
|
||||
public static void remove(String token) {
|
||||
|
@ -0,0 +1,24 @@
|
||||
package io.dataease.listener;
|
||||
|
||||
import io.dataease.commons.utils.LogUtil;
|
||||
import net.sf.ehcache.CacheManager;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.event.ContextClosedEvent;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class ApplicationCloseEventListener implements ApplicationListener<ContextClosedEvent> {
|
||||
|
||||
@Autowired(required = false)
|
||||
CacheManager cacheManager;
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ContextClosedEvent event) {
|
||||
|
||||
if (ObjectUtils.isNotEmpty(cacheManager))
|
||||
cacheManager.shutdown();
|
||||
LogUtil.info("DataEase is stopping");
|
||||
}
|
||||
}
|
@ -66,6 +66,12 @@ public class CacheUtils {
|
||||
return cache(cacheName).remove(key);
|
||||
}
|
||||
|
||||
public static void flush(String cacheName) {
|
||||
CacheManager manager = getCacheManager();
|
||||
if (manager instanceof RedisCacheManager) return;
|
||||
cache(cacheName).flush();
|
||||
}
|
||||
|
||||
public static void removeAll(String cacheName) {
|
||||
if (getCacheManager() instanceof RedisCacheManager) {
|
||||
org.springframework.cache.Cache cache = getCacheManager().getCache(cacheName);
|
||||
|
@ -272,12 +272,16 @@
|
||||
|
||||
<cache
|
||||
name="sys_token_store"
|
||||
eternal="true"
|
||||
maxElementsInMemory="1"
|
||||
maxElementsOnDisk="0"
|
||||
eternal="false"
|
||||
maxElementsInMemory="5000"
|
||||
maxElementsOnDisk="50000"
|
||||
overflowToDisk="true"
|
||||
diskPersistent="true"
|
||||
/>
|
||||
timeToIdleSeconds="28800"
|
||||
timeToLiveSeconds="28800"
|
||||
memoryStoreEvictionPolicy="LRU"
|
||||
diskPersistent="true">
|
||||
<BootstrapCacheLoaderFactory class="net.sf.ehcache.store.DiskStoreBootstrapCacheLoaderFactory" properties="bootstrapAsynchronously=true" />
|
||||
</cache>
|
||||
|
||||
|
||||
|
||||
|
@ -88,7 +88,8 @@
|
||||
"vuedraggable": "^2.24.3",
|
||||
"vuex": "3.1.0",
|
||||
"webpack": "^4.46.0",
|
||||
"xlsx": "^0.17.0"
|
||||
"xlsx": "^0.17.0",
|
||||
"xss": "^1.0.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.4.0-0",
|
||||
|
@ -37,6 +37,7 @@ import 'tinymce/plugins/nonbreaking'
|
||||
import 'tinymce/plugins/pagebreak'
|
||||
import { mapState } from 'vuex'
|
||||
import Vue from 'vue'
|
||||
import xssCheck from 'xss'
|
||||
|
||||
export default {
|
||||
name: 'DeRichText',
|
||||
@ -77,7 +78,7 @@ export default {
|
||||
canEdit: false,
|
||||
// 初始化配置
|
||||
tinymceId: 'tinymce-' + this.element.id,
|
||||
myValue: this.propValue,
|
||||
myValue: xssCheck(this.propValue),
|
||||
init: {
|
||||
selector: '#tinymce-' + this.element.id,
|
||||
toolbar_items_size: 'small',
|
||||
|
@ -38,6 +38,7 @@ import 'tinymce/plugins/pagebreak'
|
||||
import { mapState } from 'vuex'
|
||||
import bus from '@/utils/bus'
|
||||
import { uuid } from 'vue-uuid'
|
||||
import xssCheck from 'xss'
|
||||
|
||||
export default {
|
||||
name: 'DeRichTextView',
|
||||
@ -152,7 +153,7 @@ export default {
|
||||
viewInit() {
|
||||
bus.$on('fieldSelect-' + this.element.propValue.viewId, this.fieldSelect)
|
||||
tinymce.init({})
|
||||
this.myValue = this.assignment(this.element.propValue.textValue)
|
||||
this.myValue = xssCheck(this.assignment(this.element.propValue.textValue))
|
||||
bus.$on('initCurFields-' + this.element.id, this.initCurFieldsChange)
|
||||
this.$nextTick(() => {
|
||||
this.initReady = true
|
||||
|
@ -759,7 +759,7 @@ export default {
|
||||
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
|
||||
requestInfo.pageSize = this.currentPage.pageSize
|
||||
requestInfo.pageSize = this.currentPage.pageSize === parseInt(attrSize.tablePageSize) ? this.currentPage.pageSize : parseInt(attrSize.tablePageSize)
|
||||
}
|
||||
}
|
||||
if (this.isFirstLoad) {
|
||||
|
@ -64,7 +64,7 @@
|
||||
:enable-scroll="false"
|
||||
:chart="chartTable"
|
||||
:show-summary="false"
|
||||
class="table-class"
|
||||
class="table-class-dialog"
|
||||
/>
|
||||
</de-main-container>
|
||||
</de-container>
|
||||
@ -342,8 +342,9 @@ export default {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.table-class {
|
||||
.table-class-dialog {
|
||||
height: 100%;
|
||||
overflow-y: auto !important;
|
||||
}
|
||||
|
||||
.canvas-class {
|
||||
|
@ -18,7 +18,7 @@
|
||||
@mousedown="handleMousedown"
|
||||
@blur="handleBlur"
|
||||
@input="handleInput"
|
||||
v-html="element.propValue"
|
||||
v-html="$xss(element.propValue)"
|
||||
/>
|
||||
<div
|
||||
v-if="!canEdit"
|
||||
@ -28,7 +28,7 @@
|
||||
@mousedown="handleMousedown"
|
||||
@blur="handleBlur"
|
||||
@input="handleInput"
|
||||
v-html="element.propValue"
|
||||
v-html="$xss(element.propValue)"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
@ -37,7 +37,7 @@
|
||||
>
|
||||
<div
|
||||
:style="{ verticalAlign: element.style.verticalAlign }"
|
||||
v-html="textInfo"
|
||||
v-html="$xss(textInfo)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@ -80,7 +80,7 @@ export default {
|
||||
},
|
||||
textInfo() {
|
||||
if (this.element && this.element.hyperlinks && this.element.hyperlinks.enable) {
|
||||
return "<a title='" + this.element.hyperlinks.content + "' target='" + this.element.hyperlinks.openMode + "' href='" + this.element.hyperlinks.content + "'>" + this.element.propValue + '</a>'
|
||||
return '<a title=\'' + this.element.hyperlinks.content + '\' target=\'' + this.element.hyperlinks.openMode + '\' href=\'' + this.element.hyperlinks.content + '\'>' + this.element.propValue + '</a>'
|
||||
} else {
|
||||
return this.element.propValue
|
||||
}
|
||||
|
@ -411,6 +411,10 @@ export default {
|
||||
const _this = this
|
||||
_this.$nextTick(() => {
|
||||
try {
|
||||
const targetRef = _this.$refs['canvasTabRef-' + _this.activeTabName]
|
||||
if (targetRef) {
|
||||
targetRef[0].restore()
|
||||
}
|
||||
_this.$refs[this.activeTabName][0].resizeChart()
|
||||
} catch (e) {
|
||||
// ignore
|
||||
|
@ -43,6 +43,12 @@ import 'video.js/dist/video-js.css'
|
||||
// 控制标签宽高成比例的指令
|
||||
import proportion from 'vue-proportion-directive'
|
||||
|
||||
import xss from 'xss'
|
||||
// 定义全局XSS解决方法
|
||||
Object.defineProperty(Vue.prototype, '$xss', {
|
||||
value: xss
|
||||
})
|
||||
|
||||
Vue.config.productionTip = false
|
||||
Vue.use(VueClipboard)
|
||||
Vue.use(widgets)
|
||||
|
@ -57,7 +57,7 @@ const routeBefore = (callBack) => {
|
||||
callBack()
|
||||
}
|
||||
}
|
||||
router.beforeEach(async(to, from, next) => routeBefore(() => {
|
||||
router.beforeEach(async (to, from, next) => routeBefore(() => {
|
||||
// start progress bar
|
||||
NProgress.start()
|
||||
const mobileIgnores = ['/delink', '/de-auto-login']
|
||||
@ -69,6 +69,9 @@ router.beforeEach(async(to, from, next) => routeBefore(() => {
|
||||
if (hasToken) {
|
||||
urlSuffix += ('?detoken=' + hasToken)
|
||||
}
|
||||
localStorage.removeItem('user-info')
|
||||
localStorage.removeItem('userId')
|
||||
localStorage.removeItem('Authorization')
|
||||
window.location.href = window.origin + urlSuffix
|
||||
NProgress.done()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user