Merge pull request #2439 from dataease/pr@dev@feat_fit_xpack_cas

feat: 适配xpack-cas单点登录
This commit is contained in:
fit2cloud-chenyw 2022-06-16 18:42:06 +08:00 committed by GitHub
commit a18964f0b7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 570 additions and 65 deletions

View File

@ -18,6 +18,9 @@
<java.version>1.8</java.version>
<graalvm.version>20.1.0</graalvm.version>
<jwt.version>3.12.1</jwt.version>
<buji.version>4.0.0</buji.version>
<pac4j.version>3.3.0</pac4j.version>
</properties>
<dependencies>
@ -36,12 +39,7 @@
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!--<exclusions>
<exclusion>
<artifactId>spring-boot-starter-tomcat</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>-->
</dependency>
<dependency>
@ -89,11 +87,7 @@
<artifactId>lombok</artifactId>
</dependency>
<!--<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.2</version>
</dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
@ -122,7 +116,7 @@
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<artifactId>shiro-spring</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
@ -236,8 +230,7 @@
</exclusions>
</dependency>
<!--由于暂时插件接口未注册到公司仓库请先down下代码安装到本地仓库
https://github.com/dataease/dataease-plugins-->
<dependency>
<groupId>io.dataease</groupId>
<artifactId>dataease-plugin-interface</artifactId>
@ -336,6 +329,7 @@
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
</dependencies>
<build>
@ -350,9 +344,7 @@
</resource>
<resource>
<directory>src/main/resources</directory>
<!-- <includes>
<include>**/*</include>
</includes> -->
<filtering>true</filtering>
<excludes>
<exclude>static/**/*.woff</exclude>

View File

@ -38,6 +38,10 @@ public interface AuthApi {
@PostMapping("/logout")
String logout();
@ApiIgnore
@PostMapping("/deLogout")
String deLogout();
@ApiOperation("验证账号")
@PostMapping("/validateName")
Boolean validateName(Map<String, String> nameDto);
@ -50,6 +54,10 @@ public interface AuthApi {
@PostMapping("/isOpenOidc")
boolean isOpenOidc();
@ApiOperation("是否开启cas")
@PostMapping("/isOpenCas")
boolean isOpenCas();
@ApiIgnore
@PostMapping("/isPluginLoaded")
boolean isPluginLoaded();

View File

@ -0,0 +1,60 @@
package io.dataease.auth.config.cas;
import io.dataease.auth.service.impl.ShiroServiceImpl;
import io.dataease.commons.utils.CommonBeanFactory;
import io.dataease.service.system.SystemParameterService;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.util.AntPathMatcher;
import org.jasig.cas.client.authentication.UrlPatternMatcherStrategy;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@Component
public class CasStrategy implements UrlPatternMatcherStrategy {
private static Set<String> releaseTypes = new HashSet<>();
@PostConstruct
public void init() {
releaseTypes.add("anon");
releaseTypes.add("link");
releaseTypes.add("doc");
}
@Override
public boolean matches(String s) {
SystemParameterService service = CommonBeanFactory.getBean(SystemParameterService.class);
String serviceValue = service.getValue("cas.callBack");
if (StringUtils.isBlank(serviceValue)) return false;
String serverName = serviceValue.substring(0, serviceValue.indexOf("/cas/callBack"));
int beginIndex = -1;
if ((beginIndex = s.indexOf(serverName)) != -1) {
s = s.substring(beginIndex + serverName.length());
}
if (StringUtils.equals("/", s)) return false;
if (StringUtils.equals("/login", s)) return false;
if (StringUtils.startsWith(s, "/cas/callBack")) return false;
if (StringUtils.equals("/api/auth/deLogout", s)) return true;
AntPathMatcher antPathMatcher = new AntPathMatcher();
ShiroServiceImpl shiroService = CommonBeanFactory.getBean(ShiroServiceImpl.class);
Map<String, String> stringStringMap = shiroService.loadFilterChainDefinitionMap();
for (Map.Entry<String, String> entry : stringStringMap.entrySet()) {
if (releaseTypes.contains(entry.getValue())) {
boolean matches = antPathMatcher.matches(entry.getKey(), s);
if (matches) {
return true;
}
}
}
return false;
}
@Override
public void setPattern(String s) {
}
}

View File

@ -17,12 +17,14 @@ import io.dataease.i18n.Translator;
import io.dataease.plugins.common.entity.XpackLdapUserEntity;
import io.dataease.plugins.config.SpringContextUtil;
import io.dataease.plugins.util.PluginUtils;
import io.dataease.plugins.xpack.cas.service.CasXpackService;
import io.dataease.plugins.xpack.ldap.dto.request.LdapValidateRequest;
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 org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
@ -36,7 +38,9 @@ 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 {
@ -50,6 +54,9 @@ public class AuthServer implements AuthApi {
@Autowired
private SysUserService sysUserService;
@Resource
private SystemParameterService systemParameterService;
@Override
public Object login(@RequestBody LoginDto loginDto) throws Exception {
String username = RsaUtil.decryptByPrivateKey(RsaProperties.privateKey, loginDto.getUsername());
@ -159,6 +166,37 @@ public class AuthServer implements AuthApi {
return DEFAULT_PWD;
}
@Override
public String deLogout() {
String token = ServletUtils.getToken();
if (StringUtils.isEmpty(token) || StringUtils.equals("null", token) || StringUtils.equals("undefined", token)) {
return "success";
}
SecurityUtils.getSubject().logout();
String result = null;
Integer defaultLoginType = systemParameterService.defaultLoginType();
if (defaultLoginType == 3 && isOpenCas()) {
HttpServletRequest request = ServletUtils.request();
HttpSession session = request.getSession();
session.invalidate();
CasXpackService casXpackService = SpringContextUtil.getBean(CasXpackService.class);
result = casXpackService.logout();
}
try {
Long userId = JWTUtils.tokenInfoByToken(token).getUserId();
authUserService.clearCache(userId);
if (StringUtils.isBlank(result)) {
result = "success";
}
} catch (Exception e) {
LogUtil.error(e);
if (StringUtils.isBlank(result)) {
result = "fail";
}
}
return result;
}
@Override
public String logout() {
String token = ServletUtils.getToken();
@ -170,20 +208,36 @@ public class AuthServer implements AuthApi {
OidcXpackService oidcXpackService = SpringContextUtil.getBean(OidcXpackService.class);
oidcXpackService.logout(idToken);
}
}
if (StringUtils.isEmpty(token) || StringUtils.equals("null", token) || StringUtils.equals("undefined", token)) {
return "success";
}
SecurityUtils.getSubject().logout();
String result = null;
Integer defaultLoginType = systemParameterService.defaultLoginType();
if (defaultLoginType == 3 && isOpenCas()) {
HttpServletRequest request = ServletUtils.request();
HttpSession session = request.getSession();
session.invalidate();
CasXpackService casXpackService = SpringContextUtil.getBean(CasXpackService.class);
result = casXpackService.logout();
}
try {
Long userId = JWTUtils.tokenInfoByToken(token).getUserId();
authUserService.clearCache(userId);
if (StringUtils.isBlank(result)) {
result = "success";
}
} catch (Exception e) {
LogUtil.error(e);
return "fail";
if (StringUtils.isBlank(result)) {
result = "fail";
}
}
return "success";
return result;
}
@Override
@ -211,6 +265,17 @@ public class AuthServer implements AuthApi {
return authUserService.supportOidc();
}
@Override
public boolean isOpenCas() {
Boolean licValid = PluginUtils.licValid();
if (!licValid)
return false;
Boolean supportCas = authUserService.supportCas();
return authUserService.supportCas();
}
@Override
public boolean isPluginLoaded() {
Boolean licValid = PluginUtils.licValid();

View File

@ -14,6 +14,8 @@ public interface AuthUserService {
SysUserEntity getLdapUserByName(String username);
SysUserEntity getCasUserByName(String username);
SysUserEntity getUserBySub(String sub);
List<String> roles(Long userId);
@ -28,6 +30,8 @@ public interface AuthUserService {
Boolean supportOidc();
Boolean supportCas();
Boolean pluginLoaded();

View File

@ -10,6 +10,7 @@ import io.dataease.plugins.common.base.domain.SysUser;
import io.dataease.plugins.common.base.mapper.SysUserMapper;
import io.dataease.plugins.common.service.PluginCommonService;
import io.dataease.plugins.config.SpringContextUtil;
import io.dataease.plugins.xpack.cas.service.CasXpackService;
import io.dataease.plugins.xpack.ldap.service.LdapXpackService;
import io.dataease.plugins.xpack.oidc.service.OidcXpackService;
@ -65,6 +66,11 @@ public class AuthUserServiceImpl implements AuthUserService {
return authMapper.findLdapUserByName(username);
}
@Override
public SysUserEntity getCasUserByName(String username) {
return authMapper.findCasUserByName(username);
}
@Override
public SysUserEntity getUserBySub(String sub) {
return authMapper.findUserBySub(sub);
@ -147,6 +153,15 @@ public class AuthUserServiceImpl implements AuthUserService {
return oidcXpackService.isSuuportOIDC();
}
@Override
public Boolean supportCas() {
Map<String, CasXpackService> beansOfType = SpringContextUtil.getApplicationContext().getBeansOfType((CasXpackService.class));
if (beansOfType.keySet().size() == 0) return false;
CasXpackService casXpackService = SpringContextUtil.getBean(CasXpackService.class);
if (ObjectUtils.isEmpty(casXpackService)) return false;
return casXpackService.suuportCas();
}
@Override
public Boolean pluginLoaded() {
Map<String, PluginCommonService> beansOfType = SpringContextUtil.getApplicationContext().getBeansOfType((PluginCommonService.class));

View File

@ -84,6 +84,8 @@ public class ShiroServiceImpl implements ShiroService {
filterChainDefinitionMap.put("/api/pluginCommon/staticInfo/**", ANON);
filterChainDefinitionMap.put("/plugin/oidc/authInfo", ANON);
filterChainDefinitionMap.put("/sso/callBack*", ANON);
filterChainDefinitionMap.put("/cas/callBack*", ANON);
filterChainDefinitionMap.put("/cas/reset", ANON);
filterChainDefinitionMap.put("/unauth", ANON);
filterChainDefinitionMap.put("/display/**", ANON);
@ -91,7 +93,6 @@ public class ShiroServiceImpl implements ShiroService {
filterChainDefinitionMap.put("/downline", ANON);
filterChainDefinitionMap.put("/common-files/**", ANON);
filterChainDefinitionMap.put("/linkage/getPanelAllLinkageInfo/**", ANON);
filterChainDefinitionMap.put("/api/auth/logout", "logout");
filterChainDefinitionMap.put("/api/link/resourceDetail/**", "link");
filterChainDefinitionMap.put("/api/link/viewDetail/**", "link");

View File

@ -3,7 +3,10 @@ package io.dataease.commons.filter;
import io.dataease.commons.exception.DEException;
import io.dataease.commons.holder.ThreadLocalContextHolder;
import io.dataease.commons.wrapper.XssAndSqlHttpServletRequestWrapper;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
@ -20,7 +23,10 @@ public class SqlFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (ObjectUtils.isEmpty(RequestContextHolder.getRequestAttributes())) {
ServletRequestAttributes attributes = new ServletRequestAttributes((HttpServletRequest) request);
RequestContextHolder.setRequestAttributes(attributes);
}
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
if ("TRACE".equalsIgnoreCase(httpRequest.getMethod()) || "TRACK".equalsIgnoreCase(httpRequest.getMethod())) {

View File

@ -7,6 +7,7 @@ import io.dataease.controller.sys.response.MailInfo;
import io.dataease.dto.SystemParameterDTO;
import io.dataease.listener.DatasetCheckListener;
import io.dataease.listener.util.CacheUtils;
import io.dataease.plugins.xpack.cas.dto.CasSaveResult;
import io.dataease.service.FileService;
import io.dataease.service.system.EmailService;
import io.dataease.service.system.SystemParameterService;
@ -66,14 +67,14 @@ public class SystemParameterController {
@RequiresPermissions("sysparam:read")
@PostMapping("/edit/basic")
public void editBasic(@RequestBody List<SystemParameter> systemParameter) {
public CasSaveResult editBasic(@RequestBody List<SystemParameter> systemParameter) {
int timeout = Integer.parseInt(systemParameter.stream().filter(
parameter -> parameter.getParamKey().equals("basic.frontTimeOut")
).findFirst().get().getParamValue());
if (timeout < 0 || timeout > 300) { //增加了合法性检验
throw new NumberFormatException("Timeout Range Error!");
}
systemParameterService.editBasic(systemParameter);
return systemParameterService.editBasic(systemParameter);
}
@PostMapping("/testConnection")

View File

@ -27,6 +27,8 @@ public interface AuthMapper {
SysUserEntity findLdapUserByName(@Param("username") String username);
SysUserEntity findCasUserByName(@Param("username") String username);
SysUserEntity findUserBySub(@Param("sub") String sub);

View File

@ -49,6 +49,11 @@
select user_id, username,nick_name, dept_id, password, enabled,email, phone, language ,is_admin, `from` from sys_user a where username = #{username} and a.from = 1
</select>
<select id="findCasUserByName" resultMap="baseMap">
select user_id, username,nick_name, dept_id, password, enabled,email, phone, language ,is_admin, `from` from sys_user a where username = #{username} and a.from = 3
</select>
<select id="findUserBySub" resultMap="baseMap">
select user_id, username,nick_name, dept_id, password, enabled,email, phone, language ,is_admin, `from` from sys_user where sub = #{sub}
</select>

View File

@ -0,0 +1,27 @@
package io.dataease.listener;
import io.dataease.plugins.config.SpringContextUtil;
import io.dataease.plugins.xpack.cas.service.CasXpackService;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import javax.servlet.ServletContext;
import java.util.Map;
@Component
public class CasStatusListener implements ApplicationListener<ApplicationReadyEvent> {
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
Map<String, CasXpackService> beansOfType = SpringContextUtil.getApplicationContext().getBeansOfType((CasXpackService.class));
if (beansOfType.keySet().size() == 0) return;
CasXpackService casXpackService = SpringContextUtil.getBean(CasXpackService.class);
if (ObjectUtils.isEmpty(casXpackService)) return;
ServletContext servletContext = event.getApplicationContext().getBean(ServletContext.class);
casXpackService.checkCasStatus(servletContext);
}
}

View File

@ -0,0 +1,103 @@
package io.dataease.plugins.server;
import cn.hutool.core.util.RandomUtil;
import io.dataease.auth.entity.SysUserEntity;
import io.dataease.auth.entity.TokenInfo;
import io.dataease.auth.service.AuthUserService;
import io.dataease.auth.util.JWTUtils;
import io.dataease.commons.utils.CodingUtil;
import io.dataease.commons.utils.LogUtil;
import io.dataease.commons.utils.ServletUtils;
import io.dataease.service.sys.SysUserService;
import io.dataease.service.system.SystemParameterService;
import org.apache.commons.lang3.StringUtils;
import org.jasig.cas.client.authentication.AttributePrincipal;
import org.jasig.cas.client.util.AssertionHolder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import springfox.documentation.annotations.ApiIgnore;
import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
@ApiIgnore
@RequestMapping("/cas")
@Controller
public class CasServer {
@Autowired
private AuthUserService authUserService;
@Autowired
private SysUserService sysUserService;
@Resource
private SystemParameterService systemParameterService;
@GetMapping("/callBack")
public ModelAndView callBack() {
ModelAndView modelAndView = new ModelAndView("redirect:/");
HttpServletResponse response = ServletUtils.response();
AttributePrincipal principal = AssertionHolder.getAssertion().getPrincipal();
String name = principal.getName();
try {
SysUserEntity sysUserEntity = authUserService.getCasUserByName(name);
if(null == sysUserEntity){
String s = RandomUtil.randomString(6);
String email = s + "@xxx.com";
sysUserService.validateCasUser(name);
sysUserService.saveCASUser(name, email);
sysUserEntity = authUserService.getUserByName(name);
}
String realPwd = CodingUtil.md5(sysUserService.defaultPWD());
TokenInfo tokenInfo = TokenInfo.builder().userId(sysUserEntity.getUserId()).username(sysUserEntity.getUsername()).build();
String token = JWTUtils.sign(tokenInfo, realPwd);
ServletUtils.setToken(token);
Cookie cookie_token = new Cookie("Authorization", token);cookie_token.setPath("/");
response.addCookie(cookie_token);
}catch(Exception e) {
String msg = e.getMessage();
if (null != e.getCause()) {
msg = e.getCause().getMessage();
}
try {
msg = URLEncoder.encode(msg, "UTF-8");
LogUtil.error(e);
Cookie cookie_error = new Cookie("CasError", msg);
cookie_error.setPath("/");
response.addCookie(cookie_error);
return modelAndView;
} catch (UnsupportedEncodingException e1) {
e.printStackTrace();
}
}
return modelAndView;
}
@GetMapping("/reset")
@ResponseBody
public String reset() {
systemParameterService.resetCas();
String token = ServletUtils.getToken();
if (StringUtils.isNotBlank(token)) {
Long userId = JWTUtils.tokenInfoByToken(token).getUserId();
authUserService.clearCache(userId);
}
HttpServletRequest request = ServletUtils.request();
request.getSession().invalidate();
return "已经切换默认登录方式";
}
}

View File

@ -0,0 +1,35 @@
package io.dataease.plugins.server;
import io.dataease.plugins.config.SpringContextUtil;
import io.dataease.plugins.xpack.cas.dto.CasSaveResult;
import io.dataease.plugins.xpack.cas.service.CasXpackService;
import io.dataease.plugins.xpack.display.dto.response.SysSettingDto;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.annotations.ApiIgnore;
import java.util.List;
@ApiIgnore
@RequestMapping("/plugin/cas")
@RestController
public class XCasServer {
@PostMapping("/info")
public List<SysSettingDto> getOidcInfo() {
CasXpackService casXpackService = SpringContextUtil.getBean(CasXpackService.class);
return casXpackService.casSettings();
}
@RequiresPermissions("sysparam:read")
@PostMapping("/save")
public CasSaveResult save(@RequestBody List<SysSettingDto> settings) {
CasXpackService casXpackService = SpringContextUtil.getBean(CasXpackService.class);
return casXpackService.save(settings);
}
}

View File

@ -124,6 +124,31 @@ public class SysUserService {
}
}
@Transactional
public void saveCASUser(String name, String email) {
long now = System.currentTimeMillis();
SysUser sysUser = new SysUser();
sysUser.setUsername(name);
sysUser.setNickName(name);
sysUser.setEmail(email);
sysUser.setPassword(CodingUtil.md5(DEFAULT_PWD));
sysUser.setCreateTime(now);
sysUser.setUpdateTime(now);
sysUser.setEnabled(1L);
sysUser.setLanguage("zh_CN");
sysUser.setFrom(3);
sysUser.setIsAdmin(false);
// sysUser.setSub(ssoUserInfo.getSub());
sysUserMapper.insert(sysUser);
SysUser dbUser = findOne(sysUser);
if (null != dbUser && null != dbUser.getUserId()) {
// oidc默认角色是普通员工
List<Long> roleIds = new ArrayList<Long>();
roleIds.add(2L);
saveUserRoles( dbUser.getUserId(), roleIds);
}
}
public String defaultPWD() {
return DEFAULT_PWD;
}
@ -323,6 +348,14 @@ public class SysUserService {
return null;
}
public void validateCasUser(String userName) {
SysUserExample example = new SysUserExample();
example.createCriteria().andUsernameEqualTo(userName);
List<SysUser> users = sysUserMapper.selectByExample(example);
if(CollectionUtils.isNotEmpty(users)) {
throw new RuntimeException("用户ID【"+userName+"】已存在,请联系管理员");
}
}
public void validateExistUser(String userName, String nickName, String email) {
SysUserExample example = new SysUserExample();
if (StringUtils.isNotBlank(userName)) {

View File

@ -10,8 +10,12 @@ import io.dataease.plugins.common.base.domain.FileMetadata;
import io.dataease.plugins.common.base.domain.SystemParameter;
import io.dataease.plugins.common.base.domain.SystemParameterExample;
import io.dataease.plugins.common.base.mapper.SystemParameterMapper;
import io.dataease.plugins.config.SpringContextUtil;
import io.dataease.plugins.xpack.cas.dto.CasSaveResult;
import io.dataease.plugins.xpack.cas.service.CasXpackService;
import io.dataease.service.FileService;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -22,6 +26,8 @@ import javax.imageio.ImageIO;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import io.dataease.ext.*;
@Service
@ -29,6 +35,7 @@ import io.dataease.ext.*;
public class SystemParameterService {
private final static String LOGIN_TYPE_KEY = "basic.loginType";
private final static String CAS_LOGIN_TYPE = "3";
@Resource
private SystemParameterMapper systemParameterMapper;
@Resource
@ -81,8 +88,11 @@ public class SystemParameterService {
return result;
}
public void editBasic(List<SystemParameter> parameters) {
parameters.forEach(parameter -> {
@Transactional
public CasSaveResult editBasic(List<SystemParameter> parameters) {
CasSaveResult casSaveResult = afterSwitchDefaultLogin(parameters);
for (int i = 0; i < parameters.size(); i++) {
SystemParameter parameter = parameters.get(i);
SystemParameterExample example = new SystemParameterExample();
example.createCriteria().andParamKeyEqualTo(parameter.getParamKey());
@ -92,8 +102,65 @@ public class SystemParameterService {
systemParameterMapper.insert(parameter);
}
example.clear();
}
return casSaveResult;
}
@Transactional
public void resetCas() {
Map<String, CasXpackService> beansOfType = SpringContextUtil.getApplicationContext().getBeansOfType((CasXpackService.class));
if (beansOfType.keySet().size() == 0) DEException.throwException("当前未启用CAS");
CasXpackService casXpackService = SpringContextUtil.getBean(CasXpackService.class);
if (ObjectUtils.isEmpty(casXpackService)) DEException.throwException("当前未启用CAS");
String loginTypePk = "basic.loginType";
SystemParameter loginTypeParameter = systemParameterMapper.selectByPrimaryKey(loginTypePk);
if (ObjectUtils.isNotEmpty(loginTypeParameter) && StringUtils.equals("3", loginTypeParameter.getParamValue())) {
loginTypeParameter.setParamValue("0");
systemParameterMapper.updateByPrimaryKeySelective(loginTypeParameter);
}
casXpackService.setEnabled(false);
}
public CasSaveResult afterSwitchDefaultLogin(List<SystemParameter> parameters) {
CasSaveResult casSaveResult = new CasSaveResult();
casSaveResult.setNeedLogout(false);
Map<String, CasXpackService> beansOfType = SpringContextUtil.getApplicationContext().getBeansOfType((CasXpackService.class));
if (beansOfType.keySet().size() == 0) return casSaveResult;
CasXpackService casXpackService = SpringContextUtil.getBean(CasXpackService.class);
if (ObjectUtils.isEmpty(casXpackService)) return casSaveResult;
AtomicReference<String> loginType = new AtomicReference();
boolean containLoginType = parameters.stream().anyMatch(param -> {
if (StringUtils.equals(param.getParamKey(), LOGIN_TYPE_KEY)) {
loginType.set(param.getParamValue());
return true;
}
return false;
});
if (!containLoginType) return casSaveResult;
SystemParameter systemParameter = systemParameterMapper.selectByPrimaryKey(LOGIN_TYPE_KEY);
String originVal = null;
if (ObjectUtils.isNotEmpty(systemParameter)) {
originVal = systemParameter.getParamValue();
}
if (StringUtils.equals(originVal, loginType.get())) return casSaveResult;
if (StringUtils.equals(CAS_LOGIN_TYPE, loginType.get())) {
casSaveResult.setNeedLogout(true);
casXpackService.setEnabled(true);
casSaveResult.setCasEnable(true);
}
if (StringUtils.equals(CAS_LOGIN_TYPE, originVal)) {
casSaveResult.setNeedLogout(true);
casXpackService.setEnabled(false);
casSaveResult.setCasEnable(false);
}
return casSaveResult;
}
public List<SystemParameter> getParamList(String type) {
@ -102,6 +169,8 @@ public class SystemParameterService {
return systemParameterMapper.selectByExample(example);
}
public String getVersion() {
return System.getenv("MS_VERSION");
}

View File

@ -114,6 +114,13 @@ server.compression.min-response-size=1024
server.servlet.context-parameters.configurationStrategy=SYSTEM_PROPERTIES
server.servlet.session.cookie.http-only=true
server.servlet.session.tracking-modes=cookie

View File

@ -79,8 +79,10 @@
"@vue/cli-plugin-babel": "3.6.0",
"@vue/cli-plugin-eslint": "^3.9.1",
"@vue/cli-service": "^3.3.1",
"add-asset-html-webpack-plugin": "^3.1.3",
"babel-eslint": "10.0.1",
"chalk": "2.4.2",
"clean-webpack-plugin": "^1.0.1",
"connect": "3.6.6",
"copy-webpack-plugin": "^4.6.0",
"eslint": "5.15.3",
@ -95,11 +97,9 @@
"script-ext-html-webpack-plugin": "2.1.3",
"script-loader": "^0.7.2",
"serve-static": "^1.13.2",
"add-asset-html-webpack-plugin": "^3.1.3",
"clean-webpack-plugin": "^1.0.1",
"vue-template-compiler": "2.6.10",
"webpack-cli": "^3.2.3",
"vuetify": "^2.6.6"
"vuetify": "^2.6.6",
"webpack-cli": "^3.2.3"
},
"engines": {
"node": ">=8.9",

View File

@ -22,6 +22,13 @@ export function logout() {
})
}
export function deLogout() {
return request({
url: '/api/auth/deLogout',
method: 'post'
})
}
export function needModifyPwd() {
return request({
url: '/api/auth/useInitPwd',
@ -79,6 +86,13 @@ export function oidcStatus() {
})
}
export function casStatus() {
return request({
url: '/api/auth/isOpenCas',
method: 'post'
})
}
export function pluginLoaded() {
return request({
url: '/api/auth/isPluginLoaded',

View File

@ -471,7 +471,8 @@ export default {
display: 'Display Setting',
ldap: 'LDAP Setting',
oidc: 'OIDC Setting',
theme: 'Theme Setting'
theme: 'Theme Setting',
cas: 'CAS Setting'
},
license: {
i18n_no_license_record: 'No License Record',
@ -657,7 +658,8 @@ export default {
test_recipients: 'Test recipients',
tip: 'Tip: use as test mail recipient only',
engine_mode_setting: 'Engine Setting',
kettle_setting: 'Kettle Setting'
kettle_setting: 'Kettle Setting',
cas_selected_warn: 'Selecting CAS will cause you to login again'
},
chart: {
view_reset: 'View Reset',

View File

@ -471,7 +471,8 @@ export default {
display: '顯示設置',
ldap: 'LDAP設置',
oidc: 'OIDC設置',
theme: '主題設置'
theme: '主題設置',
cas: 'CAS設置'
},
license: {
i18n_no_license_record: '沒有 License 記錄',
@ -645,7 +646,7 @@ export default {
login_type: '默認登錄方式',
empty_front: '為空則默認取值10秒',
empty_msg: '為空則默認取值30天',
front_error: '請填寫0-300正整數', //修改了提示信息
front_error: '請填寫0-300正整數', // 修改了提示信息
msg_error: '請填寫1-365正整數',
SMTP_port: 'SMTP端口',
SMTP_account: 'SMTP賬戶',
@ -659,7 +660,8 @@ export default {
test_recipients: '測試收件人',
tip: '提示:僅用來作爲測試郵件收件人',
engine_mode_setting: '引擎設置',
kettle_setting: 'Kettle 設置'
kettle_setting: 'Kettle 設置',
cas_selected_warn: '選擇CAS方式保存後會註銷當前回話重新登錄'
},
chart: {
view_reset: '视图重置',

View File

@ -472,7 +472,8 @@ export default {
display: '显示设置',
ldap: 'LDAP设置',
oidc: 'OIDC设置',
theme: '主题设置'
theme: '主题设置',
cas: 'CAS设置'
},
license: {
i18n_no_license_record: '没有 License 记录',
@ -660,7 +661,8 @@ export default {
test_recipients: '测试收件人',
tip: '提示:仅用来作为测试邮件收件人',
engine_mode_setting: '引擎设置',
kettle_setting: 'Kettle 设置'
kettle_setting: 'Kettle 设置',
cas_selected_warn: '选择CAS方式保存后会注销当前回话重新登录'
},
chart: {
view_reset: '视图重置',

View File

@ -197,6 +197,9 @@ export default {
bus.$on('set-top-menu-active-info', this.setTopMenuActiveInfo)
bus.$on('set-top-text-info', this.setTopTextInfo)
bus.$on('set-top-text-active-info', this.setTopTextActiveInfo)
bus.$on('sys-logout', param => {
this.logout(param)
})
this.showTips && this.$nextTick(() => {
const drop = this.$refs['my-drop']
drop && drop.show && drop.show()
@ -300,9 +303,13 @@ export default {
}
return route.children.filter(kid => !kid.hidden).length
},
async logout() {
await this.$store.dispatch('user/logout')
this.$router.push(`/login?redirect=${this.$route.fullPath}`)
async logout(param) {
const result = await this.$store.dispatch('user/logout', param)
if (result !== 'success' && result !== 'fail') {
window.location.href = result
} else {
this.$router.push(`/login?redirect=${this.$route.fullPath}`)
}
},
loadUiInfo() {
this.$store.dispatch('user/getUI').then(() => {

View File

@ -6,6 +6,7 @@ module.exports = {
/* for sso */
IdTokenKey: 'IdToken',
AccessTokenKey: 'AccessToken',
CASSESSION: 'JSESSIONID',
/**
* @type {boolean} true | false

View File

@ -1,4 +1,4 @@
import { login, logout, getInfo, getUIinfo, languageApi } from '@/api/user'
import { login, logout, deLogout, getInfo, getUIinfo, languageApi } from '@/api/user'
import { getToken, setToken, removeToken, setSysUI } from '@/utils/auth'
import { resetRouter } from '@/router'
import { format } from '@/utils/formatUi'
@ -136,13 +136,15 @@ const actions = {
},
// user logout
logout({ commit, state }) {
logout({ commit, state }, param) {
const method = param && param.casEnable ? deLogout : logout
return new Promise((resolve, reject) => {
logout(state.token).then(() => {
method(state.token).then(res => {
debugger
removeToken() // must remove token first
resetRouter()
commit('RESET_STATE')
resolve()
resolve(res.data)
}).catch(error => {
reject(error)
})

View File

@ -7,6 +7,8 @@ const IdTokenKey = Config.IdTokenKey
const AccessTokenKey = Config.AccessTokenKey
const casSessionKey = Config.CASSESSION
const linkTokenKey = Config.LinkTokenKey
export function getIdToken() {
@ -22,6 +24,7 @@ export function setToken(token) {
}
export function removeToken() {
Cookies.remove(casSessionKey)
Cookies.remove(IdTokenKey)
Cookies.remove(AccessTokenKey)
return Cookies.remove(TokenKey)

View File

@ -38,6 +38,7 @@
<el-radio :label="0" size="mini">{{ $t('login.default_login') }}</el-radio>
<el-radio v-if="loginTypes.includes(1)" :label="1" size="mini">LDAP</el-radio>
<el-radio v-if="loginTypes.includes(2)" :label="2" size="mini">OIDC</el-radio>
<el-radio v-if="loginTypes.includes(3)" :label="3" size="mini">CAS</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
@ -67,8 +68,8 @@
<script>
import { basicInfo, updateInfo } from '@/api/system/basic'
import { ldapStatus, oidcStatus } from '@/api/user'
import { ldapStatus, oidcStatus, casStatus } from '@/api/user'
import bus from '@/utils/bus'
export default {
name: 'EmailSetting',
data() {
@ -86,7 +87,7 @@ export default {
rules: {
frontTimeOut: [
{
pattern: '^([0-9]|\\b[1-9]\\d\\b|\\b[1-2]\\d\\d\\b|\\b300\\b)$', //0-300
pattern: '^([0-9]|\\b[1-9]\\d\\b|\\b[1-2]\\d\\d\\b|\\b300\\b)$', // 0-300
message: this.$t('system_parameter_setting.front_error'),
trigger: 'blur'
}
@ -98,7 +99,8 @@ export default {
trigger: 'blur'
}
]
}
},
originLoginType: null
}
},
beforeCreate() {
@ -113,6 +115,12 @@ export default {
this.loginTypes.push(2)
}
})
casStatus().then(res => {
if (res.success && res.data) {
this.loginTypes.push(3)
}
})
},
created() {
this.query()
@ -126,6 +134,9 @@ export default {
if (this.formInline && !this.formInline.loginType) {
this.formInline.loginType = 0
}
if (!this.originLoginType) {
this.originLoginType = this.formInline.loginType
}
this.$nextTick(() => {
this.$refs.formInline.clearValidate()
@ -150,24 +161,45 @@ export default {
this.$refs[formInline].validate(valid => {
if (valid) {
updateInfo(param).then(response => {
const flag = response.success
if (flag) {
this.$success(this.$t('commons.save_success'))
this.showEdit = true
this.showCancel = false
this.showSave = false
this.show = true
window.location.reload()
} else {
this.$message.error(this.$t('commons.save_failed'))
}
})
const needWarn = this.formInline.loginType === 3 && this.originLoginType !== 3
if (needWarn) {
this.$confirm(this.$t('system_parameter_setting.cas_selected_warn'), '', {
confirmButtonText: this.$t('commons.confirm'),
cancelButtonText: this.$t('commons.cancel'),
type: 'warning'
}).then(() => {
this.saveHandler(param)
}).catch(() => {
// this.$info(this.$t('commons.delete_cancel'))
})
return
}
this.saveHandler(param)
} else {
// this.result = false
}
})
},
saveHandler(param) {
updateInfo(param).then(response => {
const flag = response.success
if (flag) {
if (response.data && response.data.needLogout) {
const casEnable = response.data.casEnable
bus.$emit('sys-logout', { casEnable })
return
}
this.$success(this.$t('commons.save_success'))
this.showEdit = true
this.showCancel = false
this.showSave = false
this.show = true
window.location.reload()
} else {
this.$message.error(this.$t('commons.save_failed'))
}
})
},
cancel() {
this.showEdit = true
this.showCancel = false

View File

@ -3,7 +3,7 @@
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane :lazy="true" :label="$t('system_parameter_setting.basic_setting')" name="zero">
<basic-setting />
<basic-setting v-if="activeName === 'zero'" />
</el-tab-pane>
<el-tab-pane :lazy="true" :label="$t('system_parameter_setting.mailbox_service_settings')" name="first">
@ -34,10 +34,14 @@
<cluster-mode />
</el-tab-pane>
<el-tab-pane v-if="engineMode==='cluster'" :lazy="true" :label="$t('system_parameter_setting.kettle_setting')" name="eight">
<el-tab-pane v-if="engineMode==='cluster'" :lazy="true" :label="$t('system_parameter_setting.kettle_setting')" name="eight">
<kettle-setting />
</el-tab-pane>
<el-tab-pane v-if="isPluginLoaded" :lazy="true" :label="$t('sysParams.cas')" name="nine">
<plugin-com v-if="isPluginLoaded" ref="CasSetting" component-name="CasSetting" />
</el-tab-pane>
</el-tabs>
</layout-content>
</template>
@ -53,7 +57,7 @@ import { pluginLoaded } from '@/api/user'
import { engineMode } from '@/api/system/engine'
export default {
components: { BasicSetting, EmailSetting, LayoutContent, PluginCom, SimpleMode, ClusterMode, KettleSetting},
components: { BasicSetting, EmailSetting, LayoutContent, PluginCom, SimpleMode, ClusterMode, KettleSetting },
data() {
return {
activeName: 'zero',
@ -68,6 +72,9 @@ export default {
engineMode().then(res => {
this.engineMode = res.data
})
},
created() {
},
methods: {
handleClick(tab, event) {