diff --git a/backend/pom.xml b/backend/pom.xml
index 8b4d20ab82..6e37a7ef98 100644
--- a/backend/pom.xml
+++ b/backend/pom.xml
@@ -111,6 +111,14 @@
shiro-spring-boot-starter
${shiro.version}
+
+ org.springframework.boot
+ spring-boot-starter-data-redis
+
+
+ org.springframework.session
+ spring-session-data-redis
+
org.apache.commons
diff --git a/backend/src/main/java/io/metersphere/commons/utils/RsaKey.java b/backend/src/main/java/io/metersphere/commons/utils/RsaKey.java
index 9da3834c02..c736da9ed1 100644
--- a/backend/src/main/java/io/metersphere/commons/utils/RsaKey.java
+++ b/backend/src/main/java/io/metersphere/commons/utils/RsaKey.java
@@ -3,9 +3,11 @@ package io.metersphere.commons.utils;
import lombok.Getter;
import lombok.Setter;
+import java.io.Serializable;
+
@Setter
@Getter
-public class RsaKey {
+public class RsaKey implements Serializable {
//公钥
private String publicKey;
diff --git a/backend/src/main/java/io/metersphere/commons/utils/RsaUtil.java b/backend/src/main/java/io/metersphere/commons/utils/RsaUtil.java
index 93bfe5d397..539b359acb 100644
--- a/backend/src/main/java/io/metersphere/commons/utils/RsaUtil.java
+++ b/backend/src/main/java/io/metersphere/commons/utils/RsaUtil.java
@@ -16,10 +16,23 @@ public class RsaUtil {
public static final String CHARSET = "UTF-8";
public static final String RSA_ALGORITHM = "RSA";
+ private static RsaKey rsaKey;
/**
* 创建RSA 公钥-私钥
*/
+ public static RsaKey getRsaKey() throws NoSuchAlgorithmException {
+ if (rsaKey == null) {
+ rsaKey = createKeys();
+ }
+ return rsaKey;
+ }
+
+ public static void setRsaKey(RsaKey rsaKey) throws NoSuchAlgorithmException {
+ // 放到缓存里
+ RsaUtil.rsaKey = rsaKey;
+ }
+
public static RsaKey createKeys() throws NoSuchAlgorithmException {
return createKeys(1024);
}
diff --git a/backend/src/main/java/io/metersphere/commons/utils/SessionUtils.java b/backend/src/main/java/io/metersphere/commons/utils/SessionUtils.java
index 0a9064f341..29dea80c4e 100644
--- a/backend/src/main/java/io/metersphere/commons/utils/SessionUtils.java
+++ b/backend/src/main/java/io/metersphere/commons/utils/SessionUtils.java
@@ -1,17 +1,13 @@
package io.metersphere.commons.utils;
import io.metersphere.commons.user.SessionUser;
-import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
-import org.apache.shiro.session.mgt.DefaultSessionManager;
import org.apache.shiro.subject.Subject;
-import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
-import java.util.Collection;
import static io.metersphere.commons.constants.SessionConstants.ATTR_USER;
@@ -39,30 +35,6 @@ public class SessionUtils {
return (String) SecurityUtils.getSubject().getSession().getId();
}
- private static Session getSessionByUsername(String username) {
- DefaultSessionManager sessionManager = CommonBeanFactory.getBean(DefaultSessionManager.class);
- Collection sessions = sessionManager.getSessionDAO().getActiveSessions();
- for (Session session : sessions) {
- if (null != session && StringUtils.equals(String.valueOf(session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY)), username)) {
- return session;
- }
- }
- return null;
- }
-
- /**
- * 踢除用户
- *
- * @param username
- */
- public static void kickOutUser(String username) {
- Session session = getSessionByUsername(username);
- if (session != null) {
- DefaultSessionManager sessionManager = CommonBeanFactory.getBean(DefaultSessionManager.class);
- sessionManager.getSessionDAO().delete(session);
- }
- }
-
//
public static void putUser(SessionUser sessionUser) {
SecurityUtils.getSubject().getSession().setAttribute(ATTR_USER, sessionUser);
diff --git a/backend/src/main/java/io/metersphere/config/RsaConfig.java b/backend/src/main/java/io/metersphere/config/RsaConfig.java
index 945ae94a7e..0409279b2a 100644
--- a/backend/src/main/java/io/metersphere/config/RsaConfig.java
+++ b/backend/src/main/java/io/metersphere/config/RsaConfig.java
@@ -2,15 +2,22 @@ package io.metersphere.config;
import io.metersphere.commons.utils.RsaKey;
import io.metersphere.commons.utils.RsaUtil;
-import org.springframework.context.annotation.Bean;
+import io.metersphere.service.FileService;
import org.springframework.context.annotation.Configuration;
+import org.springframework.context.event.ContextRefreshedEvent;
+import org.springframework.context.event.EventListener;
-import java.security.NoSuchAlgorithmException;
+import javax.annotation.Resource;
@Configuration
public class RsaConfig {
- @Bean
- public RsaKey rsaKey() throws NoSuchAlgorithmException {
- return RsaUtil.createKeys();
+ @Resource
+ private FileService fileService;
+
+ @EventListener
+ public void rsaKey(ContextRefreshedEvent event) throws Exception {
+ RsaKey value = fileService.checkRsaKey();
+ // 保存到缓存中
+ RsaUtil.setRsaKey(value);
}
}
diff --git a/backend/src/main/java/io/metersphere/config/ShiroConfig.java b/backend/src/main/java/io/metersphere/config/ShiroConfig.java
index 972908b6bc..2b357ec013 100644
--- a/backend/src/main/java/io/metersphere/config/ShiroConfig.java
+++ b/backend/src/main/java/io/metersphere/config/ShiroConfig.java
@@ -1,13 +1,16 @@
package io.metersphere.config;
+
import io.metersphere.commons.utils.ShiroUtils;
import io.metersphere.security.ApiKeyFilter;
import io.metersphere.security.CsrfFilter;
import io.metersphere.security.UserModularRealmAuthenticator;
import io.metersphere.security.realm.LdapRealm;
import io.metersphere.security.realm.LocalRealm;
+import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.pam.FirstSuccessfulStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
+import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.SessionManager;
@@ -15,8 +18,8 @@ import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
+import org.apache.shiro.web.session.mgt.ServletContainerSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.EnvironmentAware;
@@ -32,8 +35,8 @@ import javax.servlet.Filter;
import java.util.*;
@Configuration
-@ConditionalOnProperty(prefix = "sso", name = "mode", havingValue = "local", matchIfMissing = true)
public class ShiroConfig implements EnvironmentAware {
+
private Environment env;
@Bean
@@ -69,16 +72,26 @@ public class ShiroConfig implements EnvironmentAware {
return new MemoryConstrainedCacheManager();
}
+ @Bean
+ public SessionManager sessionManager() {
+ Long timeout = env.getProperty("spring.session.timeout", Long.class);
+ String storeType = env.getProperty("spring.session.store-type");
+ if (StringUtils.equals(storeType, "none")) {
+ return ShiroUtils.getSessionManager(timeout, memoryConstrainedCacheManager());
+ }
+ return new ServletContainerSessionManager();
+ }
+
/**
* securityManager 不用直接注入 Realm,可能会导致事务失效
* 解决方法见 handleContextRefresh
* http://www.debugrun.com/a/NKS9EJQ.html
*/
@Bean(name = "securityManager")
- public DefaultWebSecurityManager securityManager(SessionManager sessionManager, MemoryConstrainedCacheManager memoryConstrainedCacheManager) {
+ public DefaultWebSecurityManager securityManager(SessionManager sessionManager, CacheManager cacheManager) {
DefaultWebSecurityManager dwsm = new DefaultWebSecurityManager();
dwsm.setSessionManager(sessionManager);
- dwsm.setCacheManager(memoryConstrainedCacheManager);
+ dwsm.setCacheManager(cacheManager);
dwsm.setAuthenticator(modularRealmAuthenticator());
return dwsm;
}
@@ -123,12 +136,6 @@ public class ShiroConfig implements EnvironmentAware {
return aasa;
}
- @Bean
- public SessionManager sessionManager(MemoryConstrainedCacheManager memoryConstrainedCacheManager) {
- Long sessionTimeout = env.getProperty("session.timeout", Long.class, 43200L); // 默认43200s, 12个小时
- return ShiroUtils.getSessionManager(sessionTimeout, memoryConstrainedCacheManager);
- }
-
/**
* 等到ApplicationContext 加载完成之后 装配shiroRealm
*/
diff --git a/backend/src/main/java/io/metersphere/controller/LoginController.java b/backend/src/main/java/io/metersphere/controller/LoginController.java
index 6481280ff2..efe7b911f7 100644
--- a/backend/src/main/java/io/metersphere/controller/LoginController.java
+++ b/backend/src/main/java/io/metersphere/controller/LoginController.java
@@ -4,6 +4,7 @@ import io.metersphere.commons.constants.OperLogConstants;
import io.metersphere.commons.constants.UserSource;
import io.metersphere.commons.user.SessionUser;
import io.metersphere.commons.utils.RsaKey;
+import io.metersphere.commons.utils.RsaUtil;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.controller.request.LoginRequest;
import io.metersphere.dto.UserDTO;
@@ -19,6 +20,7 @@ import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.io.IOException;
+import java.security.NoSuchAlgorithmException;
@RestController
@RequestMapping
@@ -28,13 +30,15 @@ public class LoginController {
private UserService userService;
@Resource
private BaseDisplayService baseDisplayService;
- @Resource
- private RsaKey rsaKey;
@GetMapping(value = "/isLogin")
- public ResultHolder isLogin() {
+ public ResultHolder isLogin() throws NoSuchAlgorithmException {
+ RsaKey rsaKey = RsaUtil.getRsaKey();
if (SecurityUtils.getSubject().isAuthenticated()) {
UserDTO user = userService.getUserDTO(SessionUtils.getUserId());
+ if (user == null) {
+ return ResultHolder.error(rsaKey.getPublicKey());
+ }
if (StringUtils.isBlank(user.getLanguage())) {
user.setLanguage(LocaleContextHolder.getLocale().toString());
}
@@ -64,7 +68,7 @@ public class LoginController {
}
@GetMapping(value = "/signout")
- @MsAuditLog(module = "auth_title", beforeEvent = "#msClass.getUserId(id)",type = OperLogConstants.LOGIN, title = "登出",msClass = SessionUtils.class)
+ @MsAuditLog(module = "auth_title", beforeEvent = "#msClass.getUserId(id)", type = OperLogConstants.LOGIN, title = "登出", msClass = SessionUtils.class)
public ResultHolder logout() throws Exception {
userService.logout();
SecurityUtils.getSubject().logout();
diff --git a/backend/src/main/java/io/metersphere/controller/UserController.java b/backend/src/main/java/io/metersphere/controller/UserController.java
index a8f9ebb971..e610c47c66 100644
--- a/backend/src/main/java/io/metersphere/controller/UserController.java
+++ b/backend/src/main/java/io/metersphere/controller/UserController.java
@@ -13,7 +13,8 @@ import io.metersphere.controller.request.member.EditPassWordRequest;
import io.metersphere.controller.request.member.QueryMemberRequest;
import io.metersphere.controller.request.member.UserRequest;
import io.metersphere.controller.request.resourcepool.UserBatchProcessRequest;
-import io.metersphere.dto.*;
+import io.metersphere.dto.UserDTO;
+import io.metersphere.dto.UserGroupPermissionDTO;
import io.metersphere.excel.domain.ExcelResponse;
import io.metersphere.i18n.Translator;
import io.metersphere.log.annotation.MsAuditLog;
@@ -21,6 +22,7 @@ import io.metersphere.service.UserService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
+
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -55,8 +57,7 @@ public class UserController {
@MsAuditLog(module = "system_user", type = OperLogConstants.DELETE, beforeEvent = "#msClass.getLogDetails(#userId)", msClass = UserService.class)
public void deleteUser(@PathVariable(value = "userId") String userId) {
userService.deleteUser(userId);
- // 踢掉在线用户
- SessionUtils.kickOutUser(userId);
+ // todo 剔除在线用户
}
@PostMapping("/special/update")
diff --git a/backend/src/main/java/io/metersphere/controller/request/LoginRequest.java b/backend/src/main/java/io/metersphere/controller/request/LoginRequest.java
index 78bd20ff87..1e2f7ae95e 100644
--- a/backend/src/main/java/io/metersphere/controller/request/LoginRequest.java
+++ b/backend/src/main/java/io/metersphere/controller/request/LoginRequest.java
@@ -1,13 +1,10 @@
package io.metersphere.controller.request;
-import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.RsaKey;
import io.metersphere.commons.utils.RsaUtil;
import lombok.Getter;
import lombok.Setter;
-import java.security.NoSuchAlgorithmException;
-
@Getter
@Setter
public class LoginRequest {
@@ -18,7 +15,7 @@ public class LoginRequest {
public String getUsername() {
try {
- RsaKey rsaKey = CommonBeanFactory.getBean(RsaKey.class);
+ RsaKey rsaKey = RsaUtil.getRsaKey();
return RsaUtil.privateDecrypt(username, rsaKey.getPrivateKey());
} catch (Exception e) {
return username;
@@ -27,7 +24,7 @@ public class LoginRequest {
public String getPassword() {
try {
- RsaKey rsaKey = CommonBeanFactory.getBean(RsaKey.class);
+ RsaKey rsaKey = RsaUtil.getRsaKey();
return RsaUtil.privateDecrypt(password, rsaKey.getPrivateKey());
} catch (Exception e) {
return password;
diff --git a/backend/src/main/java/io/metersphere/dto/GroupResourceDTO.java b/backend/src/main/java/io/metersphere/dto/GroupResourceDTO.java
index bbd2222ebd..179f1989ed 100644
--- a/backend/src/main/java/io/metersphere/dto/GroupResourceDTO.java
+++ b/backend/src/main/java/io/metersphere/dto/GroupResourceDTO.java
@@ -4,10 +4,11 @@ import io.metersphere.base.domain.Group;
import io.metersphere.base.domain.UserGroupPermission;
import lombok.Data;
+import java.io.Serializable;
import java.util.List;
@Data
-public class GroupResourceDTO {
+public class GroupResourceDTO implements Serializable {
private GroupResource resource;
private List permissions;
private String type;
diff --git a/backend/src/main/java/io/metersphere/security/CsrfFilter.java b/backend/src/main/java/io/metersphere/security/CsrfFilter.java
index cbd66db63b..9ced1b365d 100644
--- a/backend/src/main/java/io/metersphere/security/CsrfFilter.java
+++ b/backend/src/main/java/io/metersphere/security/CsrfFilter.java
@@ -29,6 +29,10 @@ public class CsrfFilter extends AnonymousFilter {
((HttpServletResponse) response).setHeader(SessionConstants.AUTHENTICATION_STATUS, SessionConstants.AUTHENTICATION_INVALID);
return true;
}
+ // 错误页面不需要 csrf
+ if (WebUtils.toHttp(request).getRequestURI().equals("/error")) {
+ return true;
+ }
// api 过来的请求
if (ApiKeyHandler.isApiKeyCall(WebUtils.toHttp(request))) {
return true;
diff --git a/backend/src/main/java/io/metersphere/service/FileService.java b/backend/src/main/java/io/metersphere/service/FileService.java
index 9e01561a11..e08ce6f637 100644
--- a/backend/src/main/java/io/metersphere/service/FileService.java
+++ b/backend/src/main/java/io/metersphere/service/FileService.java
@@ -6,10 +6,14 @@ import io.metersphere.base.mapper.FileMetadataMapper;
import io.metersphere.base.mapper.TestCaseFileMapper;
import io.metersphere.commons.constants.FileType;
import io.metersphere.commons.exception.MSException;
+import io.metersphere.commons.utils.LogUtil;
+import io.metersphere.commons.utils.RsaKey;
+import io.metersphere.commons.utils.RsaUtil;
import io.metersphere.performance.request.QueryProjectFileRequest;
+import org.apache.commons.lang3.SerializationUtils;
+import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
-import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;
@@ -124,11 +128,11 @@ public class FileService {
public FileMetadata insertFileByFileName(File file, byte[] fileByte, String projectId) {
if (StringUtils.isEmpty(file.getName())) {
return null;
- }else {
+ } else {
FileMetadataExample example = new FileMetadataExample();
example.createCriteria().andProjectIdEqualTo(projectId).andNameEqualTo(file.getName());
List fileMetadatasInDataBase = fileMetadataMapper.selectByExample(example);
- if(CollectionUtils.isEmpty(fileMetadatasInDataBase)){
+ if (CollectionUtils.isEmpty(fileMetadatasInDataBase)) {
final FileMetadata fileMetadata = new FileMetadata();
fileMetadata.setId(UUID.randomUUID().toString());
fileMetadata.setName(file.getName());
@@ -145,7 +149,7 @@ public class FileService {
fileContent.setFile(fileByte);
fileContentMapper.insert(fileContent);
return fileMetadata;
- }else {
+ } else {
FileMetadata fileMetadata = fileMetadatasInDataBase.get(0);
fileMetadata.setName(file.getName());
fileMetadata.setSize(file.length());
@@ -282,4 +286,32 @@ public class FileService {
example.createCriteria().andIdIn(fileIds);
return fileMetadataMapper.selectByExample(example);
}
+
+ public RsaKey checkRsaKey() {
+ String key = "ms.login.rsa.key";
+ FileContent value = getFileContent(key);
+ if (value == null) {
+ try {
+ RsaKey rsaKey = RsaUtil.getRsaKey();
+ byte[] bytes = SerializationUtils.serialize(rsaKey);
+ final FileMetadata fileMetadata = new FileMetadata();
+ fileMetadata.setId(key);
+ fileMetadata.setName(key);
+ fileMetadata.setSize((long) bytes.length);
+ fileMetadata.setCreateTime(System.currentTimeMillis());
+ fileMetadata.setUpdateTime(System.currentTimeMillis());
+ fileMetadata.setType("RSA_KEY");
+ fileMetadataMapper.insert(fileMetadata);
+
+ FileContent fileContent = new FileContent();
+ fileContent.setFileId(fileMetadata.getId());
+ fileContent.setFile(bytes);
+ fileContentMapper.insert(fileContent);
+ return rsaKey;
+ } catch (Exception e) {
+ LogUtil.error(e);
+ }
+ }
+ return SerializationUtils.deserialize(value.getFile());
+ }
}
diff --git a/backend/src/main/java/io/metersphere/service/UserService.java b/backend/src/main/java/io/metersphere/service/UserService.java
index 20056dc263..f64598f007 100644
--- a/backend/src/main/java/io/metersphere/service/UserService.java
+++ b/backend/src/main/java/io/metersphere/service/UserService.java
@@ -378,10 +378,7 @@ public class UserService {
user.setPassword(null);
user.setUpdateTime(System.currentTimeMillis());
userMapper.updateByPrimaryKeySelective(user);
- // 禁用用户之后,剔除在线用户
- if (StringUtils.equals(user.getStatus(), UserStatus.DISABLED)) {
- SessionUtils.kickOutUser(user.getId());
- }
+ // todo 禁用用户之后,剔除在线用户
}
public void switchUserResource(String sign, String sourceId) {
diff --git a/backend/src/main/resources/base.properties b/backend/src/main/resources/base.properties
index 3960b21085..c7c15384c0 100644
--- a/backend/src/main/resources/base.properties
+++ b/backend/src/main/resources/base.properties
@@ -101,6 +101,5 @@ spring.servlet.multipart.max-request-size=500MB
management.server.port=8083
management.endpoints.web.exposure.include=*
#spring.freemarker.checkTemplateLocation=false
-
-
-
+spring.session.timeout=${session.timeout:43200}
+spring.session.store-type=none