Merge pull request #3038 from dataease/pr@dev@perf_wecom

perf(系统管理-系统参数): 优化企业微信设置
This commit is contained in:
fit2cloud-chenyw 2022-09-02 17:12:30 +08:00 committed by GitHub
commit 80fb7b5666
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 275 additions and 16 deletions

View File

@ -62,6 +62,11 @@ public interface AuthApi {
@PostMapping("/isOpenCas")
boolean isOpenCas();
@ApiOperation("是否开启企业微信")
@PostMapping("/isOpenWecom")
boolean isOpenWecom();
@ApiIgnore
@PostMapping("/isPluginLoaded")
boolean isPluginLoaded();

View File

@ -282,11 +282,19 @@ public class AuthServer implements AuthApi {
Boolean licValid = PluginUtils.licValid();
if (!licValid)
return false;
Boolean supportCas = authUserService.supportCas();
return authUserService.supportCas();
}
@Override
public boolean isOpenWecom() {
Boolean licValid = PluginUtils.licValid();
if (!licValid)
return false;
return authUserService.supportWecom();
}
@Override
public boolean isPluginLoaded() {
Boolean licValid = PluginUtils.licValid();

View File

@ -16,7 +16,7 @@ public interface AuthUserService {
SysUserEntity getCasUserByName(String username);
SysUserEntity getUserBySub(String sub);
SysUserEntity getUserBySub(String sub, Integer from);
List<String> roles(Long userId);
@ -32,6 +32,8 @@ public interface AuthUserService {
Boolean supportCas();
Boolean supportWecom();
Boolean pluginLoaded();
void checkAdmin(String uname, String pwd);

View File

@ -18,6 +18,7 @@ import io.dataease.plugins.xpack.cas.service.CasXpackService;
import io.dataease.plugins.xpack.ldap.service.LdapXpackService;
import io.dataease.plugins.xpack.oidc.service.OidcXpackService;
import io.dataease.plugins.xpack.wecom.service.WecomXpackService;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cache.annotation.CacheEvict;
@ -76,8 +77,8 @@ public class AuthUserServiceImpl implements AuthUserService {
}
@Override
public SysUserEntity getUserBySub(String sub) {
return authMapper.findUserBySub(sub);
public SysUserEntity getUserBySub(String sub, Integer from) {
return authMapper.findUserBySub(sub, from);
}
@Override
@ -166,6 +167,15 @@ public class AuthUserServiceImpl implements AuthUserService {
return casXpackService.suuportCas();
}
@Override
public Boolean supportWecom() {
Map<String, WecomXpackService> beansOfType = SpringContextUtil.getApplicationContext().getBeansOfType((WecomXpackService.class));
if (beansOfType.keySet().size() == 0) return false;
WecomXpackService wecomXpackService = SpringContextUtil.getBean(WecomXpackService.class);
if (ObjectUtils.isEmpty(wecomXpackService)) return false;
return wecomXpackService.isOpen();
}
@Override
public Boolean pluginLoaded() {
Map<String, PluginCommonService> beansOfType = SpringContextUtil.getApplicationContext().getBeansOfType((PluginCommonService.class));

View File

@ -81,12 +81,17 @@ public class ShiroServiceImpl implements ShiroService {
filterChainDefinitionMap.put("/api/auth/validateName", ANON);
filterChainDefinitionMap.put("/api/auth/isOpenLdap", ANON);
filterChainDefinitionMap.put("/api/auth/isOpenOidc", ANON);
filterChainDefinitionMap.put("/api/auth/isOpenWecom", ANON);
filterChainDefinitionMap.put("/api/auth/isOpenDingtalk", ANON);
filterChainDefinitionMap.put("/api/auth/isOpenLark", ANON);
filterChainDefinitionMap.put("/api/auth/getPublicKey", ANON);
filterChainDefinitionMap.put("/api/pluginCommon/component/*", ANON);
filterChainDefinitionMap.put("/api/pluginCommon/staticInfo/**", ANON);
filterChainDefinitionMap.put("/plugin/oidc/authInfo", ANON);
filterChainDefinitionMap.put("/sso/callBack*", ANON);
filterChainDefinitionMap.put("/cas/callBack*", ANON);
filterChainDefinitionMap.put("/plugin/wecom/callBack*", ANON);
filterChainDefinitionMap.put("/plugin/wecom/getQrParam", ANON);
filterChainDefinitionMap.put("/cas/reset/**", ANON);
filterChainDefinitionMap.put("/unauth", ANON);

View File

@ -29,7 +29,7 @@ public interface AuthMapper {
SysUserEntity findCasUserByName(@Param("username") String username);
SysUserEntity findUserBySub(@Param("sub") String sub);
SysUserEntity findUserBySub(@Param("sub") String sub, @Param("userFrom") Integer userFrom);
List<CurrentRoleDto> roles(@Param("userId") Long userId);

View File

@ -55,7 +55,7 @@
</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 user_id, username,nick_name, dept_id, password, enabled,email, phone, language ,is_admin, `from` from sys_user where sub = #{sub} and `from` = #{userFrom}
</select>
<select id="roleCodes" resultType="String">

View File

@ -71,11 +71,11 @@ public class SSOServer {
SSOUserInfo ssoUserInfo = oidcXpackService.requestUserInfo(config, ssoToken.getAccessToken());
SysUserEntity sysUserEntity = authUserService.getUserBySub(ssoUserInfo.getSub());
SysUserEntity sysUserEntity = authUserService.getUserBySub(ssoUserInfo.getSub(), 2);
if (null == sysUserEntity) {
sysUserService.validateExistUser(ssoUserInfo.getUsername(), ssoUserInfo.getNickName(), ssoUserInfo.getEmail());
sysUserService.saveOIDCUser(ssoUserInfo);
sysUserEntity = authUserService.getUserBySub(ssoUserInfo.getSub());
sysUserEntity = authUserService.getUserBySub(ssoUserInfo.getSub(), 2);
}
TokenInfo tokenInfo = TokenInfo.builder().userId(sysUserEntity.getUserId()).username(sysUserEntity.getUsername()).build();
String realPwd = sysUserEntity.getPassword();

View File

@ -1,34 +1,62 @@
package io.dataease.plugins.server;
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.constants.SysLogConstants;
import io.dataease.commons.exception.DEException;
import io.dataease.commons.utils.DeLogUtils;
import io.dataease.commons.utils.LogUtil;
import io.dataease.commons.utils.ServletUtils;
import io.dataease.plugins.config.SpringContextUtil;
import io.dataease.plugins.xpack.display.dto.response.SysSettingDto;
import io.dataease.plugins.xpack.wecom.dto.entity.BaseQrResult;
import io.dataease.plugins.xpack.wecom.dto.entity.WecomAuthResult;
import io.dataease.plugins.xpack.wecom.dto.response.WecomInfo;
import io.dataease.plugins.xpack.wecom.service.WecomXpackService;
import io.dataease.service.sys.SysUserService;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import springfox.documentation.annotations.ApiIgnore;
import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
@ApiIgnore
@RequestMapping("/plugin/wecom")
@RestController
@Controller
public class XWecomServer {
@Resource
private AuthUserService authUserService;
@Resource
private SysUserService sysUserService;
@ResponseBody
@GetMapping("/info")
public WecomInfo getWecomInfo() {
WecomXpackService wecomXpackService = SpringContextUtil.getBean(WecomXpackService.class);
return wecomXpackService.info();
}
@ResponseBody
@RequiresPermissions("sysparam:read")
@PostMapping("/save")
public void save(@RequestBody List<SysSettingDto> settings) {
WecomXpackService wecomXpackService = SpringContextUtil.getBean(WecomXpackService.class);
wecomXpackService.save(settings);
}
@ResponseBody
@PostMapping("/testConn")
public void testConn(@RequestBody WecomInfo wecomInfo) {
WecomXpackService wecomXpackService = SpringContextUtil.getBean(WecomXpackService.class);
@ -38,4 +66,73 @@ public class XWecomServer {
throw new RuntimeException(e);
}
}
@ResponseBody
@PostMapping("/getQrParam")
public BaseQrResult getQrParam() {
WecomXpackService wecomXpackService = SpringContextUtil.getBean(WecomXpackService.class);
return wecomXpackService.getQrParam();
}
@GetMapping("/callBack")
public ModelAndView callBack(@RequestParam("code") String code, @RequestParam("state") String state) {
ModelAndView modelAndView = new ModelAndView("redirect:/");
HttpServletResponse response = ServletUtils.response();
WecomXpackService wecomXpackService = null;
String idToken = null;
try {
Map<String, WecomXpackService> beansOfType = SpringContextUtil.getApplicationContext().getBeansOfType((WecomXpackService.class));
if (beansOfType.keySet().size() == 0) {
DEException.throwException("缺少企业微信插件");
}
wecomXpackService = SpringContextUtil.getBean(WecomXpackService.class);
Boolean isOpen = wecomXpackService.isOpen();
if (!isOpen) {
DEException.throwException("未开启企业微信");
}
WecomAuthResult authResult = wecomXpackService.auth(code);
String userId = authResult.getUserId();
Map<String, Object> userMap = wecomXpackService.userInfo(userId);
SysUserEntity sysUserEntity = authUserService.getUserBySub(userId, 4);
if (null == sysUserEntity) {
Object emailObj = ObjectUtils.isEmpty(userMap.get("biz_mail")) ? userMap.get("email") : userMap.get("biz_mail");
String email = ObjectUtils.isEmpty(emailObj) ? "demo@wecom.work" : emailObj.toString();
sysUserService.validateExistUser(userId, userMap.get("name").toString(), email);
sysUserService.saveWecomCUser(userMap, userId, email);
sysUserEntity = authUserService.getUserBySub(userId, 4);
}
TokenInfo tokenInfo = TokenInfo.builder().userId(sysUserEntity.getUserId()).username(sysUserEntity.getUsername()).build();
String realPwd = sysUserEntity.getPassword();
String token = JWTUtils.sign(tokenInfo, realPwd);
ServletUtils.setToken(token);
DeLogUtils.save(SysLogConstants.OPERATE_TYPE.LOGIN, SysLogConstants.SOURCE_TYPE.USER, sysUserEntity.getUserId(), null, null, null);
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("OidcError", msg);
cookie_error.setPath("/");
return modelAndView;
} catch (UnsupportedEncodingException e1) {
e.printStackTrace();
}
}
return modelAndView;
}
}

View File

@ -33,6 +33,7 @@ import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Service
@ -126,6 +127,34 @@ public class SysUserService {
}
}
@Transactional
public void saveWecomCUser(Map<String, Object> userMap , String userId, String email) {
long now = System.currentTimeMillis();
SysUser sysUser = new SysUser();
sysUser.setUsername(userId);
sysUser.setNickName(userMap.get("name").toString());
sysUser.setEmail(email);
sysUser.setPassword(CodingUtil.md5(DEFAULT_PWD));
sysUser.setCreateTime(now);
sysUser.setUpdateTime(now);
sysUser.setEnabled((ObjectUtils.isNotEmpty(userMap.get("status")) && 1 == Integer.parseInt(userMap.get("status").toString())) ? 1L : 0L);
sysUser.setGender((ObjectUtils.isEmpty(userMap.get("gender")) || "1".equals(userMap.get("gender"))) ? "" : "");
sysUser.setLanguage("zh_CN");
sysUser.setFrom(4);
sysUser.setIsAdmin(false);
sysUser.setSub(userId);
sysUserMapper.insert(sysUser);
SysUser dbUser = findOne(sysUser);
if (null != dbUser && null != dbUser.getUserId()) {
// 默认角色是普通员工
List<Long> roleIds = new ArrayList<Long>();
roleIds.add(2L);
saveUserRoles( dbUser.getUserId(), roleIds);
}
}
@Transactional
public void saveCASUser(String name, String email) {
long now = System.currentTimeMillis();

View File

@ -0,0 +1,8 @@
DROP TABLE IF EXISTS `sys_external_token`;
CREATE TABLE `sys_external_token` (
`type` int(4) NOT NULL COMMENT '类型ID',
`token` varchar(255) NOT NULL COMMENT 'token',
`exp_time` bigint(13) NOT NULL COMMENT '过期时间',
PRIMARY KEY (`type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_general_ci;

View File

@ -100,6 +100,13 @@ export function casStatus() {
})
}
export function wecomStatus() {
return request({
url: '/api/auth/isOpenWecom',
method: 'post'
})
}
export function pluginLoaded() {
return request({
url: '/api/auth/isPluginLoaded',

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -1,10 +1,16 @@
<template>
<div>
<div v-show="contentShow" class="login-background">
<div class="login-container">
<el-row v-loading="loading" type="flex">
<el-col :span="12">
<el-form ref="loginForm" :model="loginForm" :rules="loginRules" size="default">
<div v-show="qrTypes.length" class="trans" @click="showQr">
<div v-show="imgAppShow" class="imgApp" />
</div>
<el-form v-show="!codeShow" ref="loginForm" :model="loginForm" :rules="loginRules" size="default">
<div class="login-logo">
<svg-icon v-if="!loginLogoUrl && axiosFinished" icon-class="DataEase" custom-class="login-logo-icon" />
<img v-if="loginLogoUrl && axiosFinished" :src="loginLogoUrl" alt="">
@ -16,8 +22,8 @@
{{ $t('login.welcome') + (uiInfo && uiInfo['ui.title'] && uiInfo['ui.title'].paramValue || ' DataEase') }}
</div>
<div class="login-form">
<el-form-item v-if="loginTypes.length > 1">
<el-radio-group v-if="loginTypes.length > 1" v-model="loginForm.loginType" @change="changeLoginType">
<el-form-item v-if="radioTypes.length > 1">
<el-radio-group v-if="radioTypes.length > 1" v-model="loginForm.loginType" @change="changeLoginType">
<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>
@ -51,6 +57,20 @@
{{ msg }}
</div>
</el-form>
<div v-show="codeShow" class="code">
<el-row>
<plugin-com v-if="loginTypes.includes(4) && codeIndex === 4" ref="WecomQr" component-name="WecomQr" />
<plugin-com v-if="loginTypes.includes(5) && codeIndex === 5" ref="DingtalkQr" component-name="DingtalkQr" />
<plugin-com v-if="loginTypes.includes(6) && codeIndex === 6" ref="FarkQr" component-name="FarkQr" />
</el-row>
<div v-if="qrTypes.length > 1" class="login-third-items">
<span v-if="qrTypes.includes(4)" class="login-third-item login-third-wecom" @click="switchCodeIndex(4)" />
<span v-if="qrTypes.includes(5)" class="login-third-item login-third-dingtalk" @click="switchCodeIndex(5)" />
<span v-if="qrTypes.includes(6)" class="login-third-item login-third-fark" @click="switchCodeIndex(6)" />
</div>
</div>
</el-col>
<el-col v-loading="!axiosFinished" :span="12">
<div v-if="!loginImageUrl && axiosFinished" class="login-image" />
@ -69,7 +89,7 @@
<script>
import { encrypt } from '@/utils/rsaEncrypt'
import { ldapStatus, oidcStatus, getPublicKey, pluginLoaded, defaultLoginType } from '@/api/user'
import { ldapStatus, oidcStatus, getPublicKey, pluginLoaded, defaultLoginType, wecomStatus } from '@/api/user'
import { getSysUI } from '@/utils/auth'
import { changeFavicon } from '@/utils/index'
import { initTheme } from '@/utils/ThemeUtil'
@ -107,12 +127,21 @@ export default {
],
defaultType: 0,
showFoot: false,
footContent: ''
footContent: '',
codeShow: false,
imgAppShow: true,
codeIndex: 4
}
},
computed: {
msg() {
return this.$store.state.user.loginMsg
},
qrTypes() {
return this.loginTypes && this.loginTypes.filter(item => item > 3) || []
},
radioTypes() {
return this.loginTypes && this.loginTypes.filter(item => item < 4) || []
}
},
watch: {
@ -145,6 +174,13 @@ export default {
}
this.setDefaultType()
})
wecomStatus().then(res => {
if (res.success && res.data) {
this.loginTypes.push(4)
}
this.setDefaultType()
})
getPublicKey().then(res => {
if (res.success && res.data) {
//
@ -179,6 +215,12 @@ export default {
},
methods: {
switchCodeIndex(codeIndex) {
this.codeIndex = codeIndex
},
showQr() {
this.codeShow = !this.codeShow
},
setDefaultType() {
if (this.loginTypes.includes(this.defaultType)) {
this.loginForm.loginType = this.defaultType
@ -412,4 +454,50 @@ export default {
zoom: 1;
margin: 0;
}
.trans {
position: absolute;
left: calc(50% - 64px);
top: 0;
width: 64px;
height: 64px;
background-image: url(../../assets/qrcode.png) ;
// background-color: var(--primary,#3370ff); // -- 线
cursor:pointer;
}
.imgApp {
width: 64px;
height: 64px;
background: linear-gradient(225deg,transparent 45px, #fff 0);
}
.code{
width: 100%;
height: 100%; //
text-align: center;
padding-top: 70px;
img {
width: 150px;
height: 150px;
padding: 10px;
}
}
.login-third-item {
display: inline-block;
width: 32px;
height: 32px;
border-radius: 50% 50%;
background: #ccc;
cursor: pointer;
margin: 0 10px;
}
.login-third-wecom {
background: url(../../assets/wecom.png) no-repeat 50%/cover;
}
.login-third-dingtalk {
background: url(../../assets/dingding01.png) no-repeat 50%/cover;
}
.login-third-fark {
background: url(../../assets/fark.png) no-repeat 50%/cover;
}
</style>