Merge pull request #8697 from dataease/pr@dev@perf_login

perf: 前端增加token超时标记以减少shiro后台过期错误日志
This commit is contained in:
fit2cloud-chenyw 2024-03-26 12:41:27 +08:00 committed by GitHub
commit 3d257a4a32
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 66 additions and 24 deletions

View File

@ -26,7 +26,6 @@ import io.dataease.plugins.xpack.ldap.dto.response.ValidateResult;
import io.dataease.plugins.xpack.ldap.service.LdapXpackService;
import io.dataease.plugins.xpack.oidc.service.OidcXpackService;
import io.dataease.service.sys.SysUserService;
import io.dataease.service.system.SystemParameterService;
import io.dataease.websocket.entity.WsMessage;
import io.dataease.websocket.service.WsService;
@ -38,15 +37,14 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
@RestController
public class AuthServer implements AuthApi {
@ -203,11 +201,12 @@ public class AuthServer implements AuthApi {
result.put("defaultPwd", DEFAULT_PWD);
}
}
Long expireTime = System.currentTimeMillis() + JWTUtils.getExpireTime();
TokenInfo tokenInfo = TokenInfo.builder().userId(user.getUserId()).username(username).build();
String token = JWTUtils.sign(tokenInfo, realPwd);
// 记录token操作时间
result.put("token", token);
result.put("expireTime", expireTime);
ServletUtils.setToken(token);
DeLogUtils.save(SysLogConstants.OPERATE_TYPE.LOGIN, SysLogConstants.SOURCE_TYPE.USER, user.getUserId(), null, null, null);
authUserService.unlockAccount(username, ObjectUtils.isEmpty(loginType) ? 0 : loginType);

View File

@ -1,8 +1,8 @@
package io.dataease.auth.util;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.JWTCreator.Builder;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.Verification;
@ -10,7 +10,10 @@ import com.google.gson.Gson;
import io.dataease.auth.entity.TokenInfo;
import io.dataease.auth.entity.TokenInfo.TokenInfoBuilder;
import io.dataease.commons.model.OnlineUserModel;
import io.dataease.commons.utils.*;
import io.dataease.commons.utils.CommonBeanFactory;
import io.dataease.commons.utils.IPUtils;
import io.dataease.commons.utils.ServletUtils;
import io.dataease.commons.utils.TokenCacheUtils;
import io.dataease.plugins.common.exception.DataEaseException;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
@ -77,6 +80,13 @@ public class JWTUtils {
return sign(tokenInfo, secret, true);
}
public static Long getExpireTime() {
if (ObjectUtils.isEmpty(expireTime)) {
expireTime = Objects.requireNonNull(CommonBeanFactory.getBean(Environment.class)).getProperty("dataease.login_timeout", Long.class, 480L);
}
return expireTime * 60000L;
}
private static boolean tokenValid(OnlineUserModel model) {
String token = model.getToken();
// 如果已经加入黑名单 则直接返回无效
@ -84,10 +94,7 @@ public class JWTUtils {
if (invalid) return false;
Long loginTime = model.getLoginTime();
if (ObjectUtils.isEmpty(expireTime)) {
expireTime = CommonBeanFactory.getBean(Environment.class).getProperty("dataease.login_timeout", Long.class, 480L);
}
long expireTimeMillis = expireTime * 60000L;
long expireTimeMillis = getExpireTime();
// 如果当前时间减去登录时间小于超时时间则说明token未过期 返回有效状态
return System.currentTimeMillis() - loginTime < expireTimeMillis;
@ -133,10 +140,7 @@ public class JWTUtils {
DataEaseException.throwException("MultiLoginError1");
}
}
if (ObjectUtils.isEmpty(expireTime)) {
expireTime = CommonBeanFactory.getBean(Environment.class).getProperty("dataease.login_timeout", Long.class, 480L);
}
long expireTimeMillis = expireTime * 60000L;
long expireTimeMillis = getExpireTime();
Date date = new Date(System.currentTimeMillis() + expireTimeMillis);
Algorithm algorithm = Algorithm.HMAC256(secret);
Builder builder = JWT.create()

View File

@ -1,5 +1,6 @@
module.exports = {
TokenKey: 'Authorization',
TokenExpKey: 'de-login-login-exp',
RefreshTokenKey: 'refreshauthorization',
LinkTokenKey: 'LINK-PWD-TOKEN',
title: 'DataEase',

View File

@ -1,5 +1,5 @@
import { login, logout, deLogout, getInfo, getUIinfo, languageApi } from '@/api/user'
import { getToken, setToken, removeToken, setSysUI } from '@/utils/auth'
import { getToken, setToken, removeToken, setSysUI, setTokenExp } from '@/utils/auth'
import { resetRouter } from '@/router'
import { format } from '@/utils/formatUi'
import { getLanguage } from '@/lang/index'
@ -82,6 +82,7 @@ const actions = {
commit('SET_TOKEN', data.token)
commit('SET_LOGIN_MSG', null)
setToken(data.token)
setTokenExp(data.expireTime)
let passwordModified = true
if (Object.prototype.hasOwnProperty.call(data, 'passwordModified')) {
passwordModified = data.passwordModified

View File

@ -2,6 +2,7 @@ import Cookies from 'js-cookie'
import Config from '@/settings'
import store from '@/store'
const TokenKey = Config.TokenKey
const TokenExpKey = Config.TokenExpKey
const IdTokenKey = Config.IdTokenKey
@ -22,11 +23,27 @@ export function getToken() {
export function setToken(token) {
return Cookies.set(TokenKey, token)
}
export function setTokenExp(exp) {
if (exp) {
// return Cookies.set(TokenExpKey, exp)
return Cookies.set(TokenExpKey, new Date().getTime() + 5000)
}
return null
}
export function tokenExp() {
const exp = Cookies.get(TokenExpKey)
if (exp && exp > 3000) {
return new Date().getTime() > (exp - 3000)
}
return false
}
export function removeToken() {
Cookies.remove(casSessionKey)
Cookies.remove(IdTokenKey)
Cookies.remove(AccessTokenKey)
Cookies.remove(TokenExpKey)
return Cookies.remove(TokenKey)
}

View File

@ -1,7 +1,7 @@
import axios from 'axios'
import store from '@/store'
import { $alert, $error } from './message'
import { getToken, getIdToken, setToken } from '@/utils/auth'
import { getToken, getIdToken, setToken, tokenExp } from '@/utils/auth'
import Config from '@/settings'
import i18n from '@/lang'
import { tryShowLoading, tryHideLoading } from './loading'
@ -56,7 +56,6 @@ service.interceptors.request.use(
if (idToken) {
config.headers[Config.IdTokenKey] = idToken
}
if (store.getters.token) {
config.headers[TokenKey] = getToken()
}
@ -74,7 +73,26 @@ service.interceptors.request.use(
config.headers['Accept-Language'] = lang
}
config.loading && tryShowLoading(store.getters.currentPath)
if (config.headers[TokenKey]) {
const logoutApiList = ['/api/auth/deLogout', '/api/auth/logout']
if (tokenExp() && !logoutApiList.includes(config.url)) {
config['expCancel'] = null
config.cancelToken = new CancelToken(function executor(c) {
config['expCancel'] = c
})
const message = i18n.t('login.expires')
$alert(message, () => {
store.dispatch('user/logout').then(() => {
location.reload()
})
}, {
confirmButtonText: i18n.t('login.re_login'),
showClose: false
})
config.expCancel('login.expires')
return config
}
}
config.cancelToken = new CancelToken(function executor(c) {
Vue.prototype.$currentHttpRequestList.set(config.url, c)
})
@ -104,10 +122,12 @@ service.interceptors.response.use(response => {
}
return response.data
}, error => {
const config = error.response && error.response.config || error.config
let config = error.response && error.response.config || error.config
const headers = error.response && error.response.headers || error.response || config && config.headers
config.loading && tryHideLoading(store.getters.currentPath)
config?.loading && tryHideLoading(store.getters.currentPath)
if (!config && !headers && error.code === 'ERR_CANCELED' && error.message === 'login.expires') {
config = { hideMsg: true }
}
let msg = ''
if (error.response) {
@ -119,7 +139,7 @@ service.interceptors.response.use(response => {
if (msg.length > 600) {
msg = msg.slice(0, 600)
}
!config.hideMsg && (!headers['authentication-status']) && !msg?.startsWith('MultiLoginError') && $error(msg)
!config?.hideMsg && (!headers['authentication-status']) && !msg?.startsWith('MultiLoginError') && $error(msg)
return Promise.reject(config.url === '/dataset/table/sqlPreview' ? msg : error)
})
const checkDownError = response => {