feat: 支持共享 session
This commit is contained in:
parent
f3dbe66b63
commit
fd76c1d8d4
|
@ -111,6 +111,14 @@
|
|||
<artifactId>shiro-spring-boot-starter</artifactId>
|
||||
<version>${shiro.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.session</groupId>
|
||||
<artifactId>spring-session-data-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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<Session> 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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<GroupPermission> permissions;
|
||||
private String type;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<FileMetadata> 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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue