diff --git a/backend/pom.xml b/backend/pom.xml index bdd4e17897..3ce4f9ac77 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -272,6 +272,24 @@ runtime + + + org.pac4j + pac4j-cas + 3.0.2 + + + io.buji + buji-pac4j + 4.0.0 + + + shiro-web + org.apache.shiro + + + + diff --git a/backend/src/main/java/io/metersphere/commons/constants/OssMode.java b/backend/src/main/java/io/metersphere/commons/constants/OssMode.java new file mode 100644 index 0000000000..aeb142a55b --- /dev/null +++ b/backend/src/main/java/io/metersphere/commons/constants/OssMode.java @@ -0,0 +1,5 @@ +package io.metersphere.commons.constants; + +public enum OssMode { + CAS,LOCAL +} diff --git a/backend/src/main/java/io/metersphere/commons/utils/ShiroUtils.java b/backend/src/main/java/io/metersphere/commons/utils/ShiroUtils.java new file mode 100644 index 0000000000..2cf98d199a --- /dev/null +++ b/backend/src/main/java/io/metersphere/commons/utils/ShiroUtils.java @@ -0,0 +1,53 @@ +package io.metersphere.commons.utils; + +import org.apache.shiro.cache.CacheManager; +import org.apache.shiro.session.mgt.SessionManager; +import org.apache.shiro.web.servlet.Cookie; +import org.apache.shiro.web.servlet.SimpleCookie; +import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; + +import java.util.Map; + +public class ShiroUtils { + + public static void loadBaseFilterChain(Map filterChainDefinitionMap){ + + filterChainDefinitionMap.put("/resource/**", "anon"); + filterChainDefinitionMap.put("/signin", "anon"); + filterChainDefinitionMap.put("/ldap/signin", "anon"); + filterChainDefinitionMap.put("/ldap/open", "anon"); + filterChainDefinitionMap.put("/isLogin", "anon"); + filterChainDefinitionMap.put("/css/**", "anon"); + filterChainDefinitionMap.put("/js/**", "anon"); + filterChainDefinitionMap.put("/img/**", "anon"); + filterChainDefinitionMap.put("/fonts/**", "anon"); + + // for swagger + filterChainDefinitionMap.put("/swagger-ui.html", "anon"); + filterChainDefinitionMap.put("/swagger-ui/**", "anon"); + filterChainDefinitionMap.put("/v3/api-docs/**", "anon"); + + filterChainDefinitionMap.put("/403", "anon"); + filterChainDefinitionMap.put("/anonymous/**", "anon"); + } + + public static Cookie getSessionIdCookie(){ + SimpleCookie sessionIdCookie = new SimpleCookie(); + sessionIdCookie.setPath("/"); + sessionIdCookie.setName("MS_SESSION_ID"); + return sessionIdCookie; + } + + public static SessionManager getSessionManager(Long sessionTimeout, CacheManager cacheManager){ + DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); + sessionManager.setSessionIdUrlRewritingEnabled(false); + sessionManager.setDeleteInvalidSessions(true); + sessionManager.setSessionValidationSchedulerEnabled(true); + sessionManager.setSessionIdCookie(ShiroUtils.getSessionIdCookie()); + sessionManager.setGlobalSessionTimeout(sessionTimeout * 1000);// 超时时间ms + sessionManager.setCacheManager(cacheManager); + + //sessionManager.setSessionIdCookieEnabled(true); + return sessionManager; + } +} diff --git a/backend/src/main/java/io/metersphere/config/ShiroConfig.java b/backend/src/main/java/io/metersphere/config/ShiroConfig.java index a5b65170cf..a6dc567cef 100644 --- a/backend/src/main/java/io/metersphere/config/ShiroConfig.java +++ b/backend/src/main/java/io/metersphere/config/ShiroConfig.java @@ -1,5 +1,6 @@ package io.metersphere.config; +import io.metersphere.commons.utils.ShiroUtils; import io.metersphere.security.ApiKeyFilter; import io.metersphere.security.LoginFilter; import io.metersphere.security.ShiroDBRealm; @@ -9,9 +10,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.servlet.SimpleCookie; -import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; 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; @@ -29,6 +29,7 @@ import java.util.Map; import java.util.Objects; @Configuration +@ConditionalOnProperty(prefix="oss",name = "mode", havingValue = "local", matchIfMissing = true) public class ShiroConfig implements EnvironmentAware { private Environment env; @@ -42,26 +43,8 @@ public class ShiroConfig implements EnvironmentAware { shiroFilterFactoryBean.setSuccessUrl("/"); shiroFilterFactoryBean.getFilters().put("apikey", new ApiKeyFilter()); - Map filterChainDefinitionMap = shiroFilterFactoryBean.getFilterChainDefinitionMap(); - filterChainDefinitionMap.put("/resource/**", "anon"); - filterChainDefinitionMap.put("/", "anon"); - filterChainDefinitionMap.put("/signin", "anon"); - filterChainDefinitionMap.put("/ldap/signin", "anon"); - filterChainDefinitionMap.put("/ldap/open", "anon"); - filterChainDefinitionMap.put("/isLogin", "anon"); - filterChainDefinitionMap.put("/css/**", "anon"); - filterChainDefinitionMap.put("/js/**", "anon"); - filterChainDefinitionMap.put("/img/**", "anon"); - filterChainDefinitionMap.put("/fonts/**", "anon"); - - // for swagger - filterChainDefinitionMap.put("/swagger-ui.html", "anon"); - filterChainDefinitionMap.put("/swagger-ui/**", "anon"); - filterChainDefinitionMap.put("/v3/api-docs/**", "anon"); - - filterChainDefinitionMap.put("/403", "anon"); - filterChainDefinitionMap.put("/anonymous/**", "anon"); + ShiroUtils.loadBaseFilterChain(filterChainDefinitionMap); filterChainDefinitionMap.put("/**", "apikey, authc"); return shiroFilterFactoryBean; } @@ -120,18 +103,7 @@ public class ShiroConfig implements EnvironmentAware { @Bean public SessionManager sessionManager(MemoryConstrainedCacheManager memoryConstrainedCacheManager) { Long sessionTimeout = env.getProperty("session.timeout", Long.class, 1800L); // 默认1800s, 半个小时 - - DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); - sessionManager.setSessionIdUrlRewritingEnabled(false); - sessionManager.setGlobalSessionTimeout(sessionTimeout * 1000);// 超时时间ms - sessionManager.setDeleteInvalidSessions(true); - sessionManager.setSessionValidationSchedulerEnabled(true); - SimpleCookie sessionIdCookie = new SimpleCookie(); - sessionManager.setSessionIdCookie(sessionIdCookie); - sessionIdCookie.setPath("/"); - sessionIdCookie.setName("MS_SESSION_ID"); - sessionManager.setCacheManager(memoryConstrainedCacheManager); - return sessionManager; + return ShiroUtils.getSessionManager(sessionTimeout, memoryConstrainedCacheManager); } /** diff --git a/backend/src/main/java/io/metersphere/controller/IndexController.java b/backend/src/main/java/io/metersphere/controller/IndexController.java index 868b253dd3..8bbd67e76d 100644 --- a/backend/src/main/java/io/metersphere/controller/IndexController.java +++ b/backend/src/main/java/io/metersphere/controller/IndexController.java @@ -1,6 +1,7 @@ package io.metersphere.controller; import io.metersphere.commons.utils.SessionUtils; +import org.apache.shiro.SecurityUtils; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -22,4 +23,15 @@ public class IndexController { return "redirect:/"; } } + + @GetMapping(value = "/oss/login") + public String ossLogin() { + return "redirect:/"; + } + + @GetMapping(value = "/oss/logout") + public void ossLogout() { + SecurityUtils.getSubject().logout(); + } + } diff --git a/backend/src/main/java/io/metersphere/controller/LoginController.java b/backend/src/main/java/io/metersphere/controller/LoginController.java index 997df80108..9b2b8987d9 100644 --- a/backend/src/main/java/io/metersphere/controller/LoginController.java +++ b/backend/src/main/java/io/metersphere/controller/LoginController.java @@ -1,10 +1,15 @@ package io.metersphere.controller; +import io.metersphere.commons.constants.OssMode; import io.metersphere.commons.constants.UserSource; +import io.metersphere.commons.user.SessionUser; +import io.metersphere.commons.utils.SessionUtils; import io.metersphere.controller.request.LoginRequest; import io.metersphere.service.UserService; +import org.apache.commons.lang3.StringUtils; import org.apache.shiro.SecurityUtils; import org.springframework.context.i18n.LocaleContextHolder; +import org.springframework.core.env.Environment; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; @@ -15,11 +20,21 @@ public class LoginController { @Resource private UserService userService; + @Resource + private Environment env; @GetMapping(value = "/isLogin") public ResultHolder isLogin() { if (SecurityUtils.getSubject().isAuthenticated()) { - return ResultHolder.success(LocaleContextHolder.getLocale()); + SessionUser user = SessionUtils.getUser(); + if (StringUtils.isBlank(user.getLanguage())) { + user.setLanguage(LocaleContextHolder.getLocale().toString()); + } + return ResultHolder.success(user); + } + String ossMode = env.getProperty("oss.mode"); + if (ossMode != null && StringUtils.equalsIgnoreCase(OssMode.CAS.name(), ossMode)) { + return ResultHolder.error("oss"); } return ResultHolder.error(""); } @@ -30,9 +45,19 @@ public class LoginController { return userService.login(request); } + @GetMapping(value = "/currentUser") + public ResultHolder currentUser() { + return ResultHolder.success(SecurityUtils.getSubject().getSession().getAttribute("user")); + } + @GetMapping(value = "/signout") public ResultHolder logout() { - SecurityUtils.getSubject().logout(); + String ossMode = env.getProperty("oss.mode"); + if (ossMode != null && StringUtils.equalsIgnoreCase(OssMode.CAS.name(), ossMode)) { + return ResultHolder.error("oss"); + } else { + SecurityUtils.getSubject().logout(); + } return ResultHolder.success(""); } @@ -42,5 +67,4 @@ public class LoginController { return userService.getDefaultLanguage(); } - } diff --git a/backend/src/main/java/io/metersphere/dto/UserDTO.java b/backend/src/main/java/io/metersphere/dto/UserDTO.java index 944c2a62a3..ca7753babc 100644 --- a/backend/src/main/java/io/metersphere/dto/UserDTO.java +++ b/backend/src/main/java/io/metersphere/dto/UserDTO.java @@ -1,6 +1,7 @@ package io.metersphere.dto; import io.metersphere.base.domain.Role; +import io.metersphere.base.domain.User; import io.metersphere.base.domain.UserRole; import lombok.Getter; import lombok.Setter; @@ -10,28 +11,7 @@ import java.util.List; @Getter @Setter -public class UserDTO { - private String id; - - private String name; - - private String email; - - private String phone; - - private String status; - - private String source; - - private Long createTime; - - private Long updateTime; - - private String language; - - private String lastWorkspaceId; - - private String lastOrganizationId; +public class UserDTO extends User { private List roles = new ArrayList<>(); diff --git a/backend/src/main/java/io/metersphere/security/ShiroDBRealm.java b/backend/src/main/java/io/metersphere/security/ShiroDBRealm.java index 30927a2ef2..71a2bef3b8 100644 --- a/backend/src/main/java/io/metersphere/security/ShiroDBRealm.java +++ b/backend/src/main/java/io/metersphere/security/ShiroDBRealm.java @@ -49,15 +49,16 @@ public class ShiroDBRealm extends AuthorizingRealm { */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { + String userId = (String) principals.getPrimaryPrincipal(); + return getAuthorizationInfo(userId, userService); + } - String userName = (String) principals.getPrimaryPrincipal(); + public static AuthorizationInfo getAuthorizationInfo(String userId, UserService userService) { SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); - // roles 内容填充 - UserDTO userDTO = userService.getUserDTO(userName); + UserDTO userDTO = userService.getUserDTO(userId); Set roles = userDTO.getRoles().stream().map(Role::getId).collect(Collectors.toSet()); authorizationInfo.setRoles(roles); - return authorizationInfo; } @@ -148,7 +149,6 @@ public class ShiroDBRealm extends AuthorizingRealm { if (!userService.checkUserPassword(userId, password)) { throw new IncorrectCredentialsException(Translator.get("password_is_incorrect")); } - // SessionUser sessionUser = SessionUser.fromUser(user); SessionUtils.putUser(sessionUser); return new SimpleAuthenticationInfo(userId, password, getName()); diff --git a/backend/src/main/java/io/metersphere/service/UserService.java b/backend/src/main/java/io/metersphere/service/UserService.java index 6895df41b1..ebeb4155b9 100644 --- a/backend/src/main/java/io/metersphere/service/UserService.java +++ b/backend/src/main/java/io/metersphere/service/UserService.java @@ -163,6 +163,17 @@ public class UserService { userMapper.insertSelective(user); } + public void createOssUser(User user) { + user.setCreateTime(System.currentTimeMillis()); + user.setUpdateTime(System.currentTimeMillis()); + user.setStatus(UserStatus.NORMAL); + if (StringUtils.isBlank(user.getEmail())) { + user.setEmail(user.getId() + "@metershpere.io"); + } + userMapper.insertSelective(user); + } + + private void checkEmailIsExist(String email) { UserExample userExample = new UserExample(); UserExample.Criteria criteria = userExample.createCriteria(); diff --git a/frontend/src/business/App.vue b/frontend/src/business/App.vue index ccd7c71aba..6f75a42fa0 100644 --- a/frontend/src/business/App.vue +++ b/frontend/src/business/App.vue @@ -25,6 +25,7 @@ import MsUser from "./components/common/head/HeaderUser"; import MsHeaderOrgWs from "./components/common/head/HeaderOrgWs"; import MsLanguageSwitch from "./components/common/head/LanguageSwitch"; + import {saveLocalStorage} from "../common/js/utils"; export default { name: 'app', @@ -36,7 +37,8 @@ beforeCreate() { this.$get("/isLogin").then(response => { if (response.data.success) { - this.$setLang(response.data.data); + this.$setLang(response.data.data.language); + saveLocalStorage(response.data); this.auth = true; } else { window.location.href = "/login" diff --git a/frontend/src/business/components/common/head/HeaderUser.vue b/frontend/src/business/components/common/head/HeaderUser.vue index 6c07ed5557..02a8cd6a65 100644 --- a/frontend/src/business/components/common/head/HeaderUser.vue +++ b/frontend/src/business/components/common/head/HeaderUser.vue @@ -18,6 +18,7 @@