概括
企业微疑动作一款立即办公通信东西,正在企业级使用中有许多功用场景,如:告警触达,数据拉收,登录考证。此中登录考证方法,能够交流脚机短疑考证码登录方法,能够必然水平上为公司节省一笔用度收入。原文便登录考证那一功用,设想比较具体的一整套处置计划。
设想
登录界里设想:
大抵接互设想:
登录账号,即域账户;暗码为域账号暗码,即启秘密码。二者皆不成为空。先后端皆需要拦阻校验;只需输出账号战暗码后,才气面打获得考证码;即:面打获得考证码,需要拦阻校验账号暗码可否为空。为空,则没有加入60s倒计时;没有为空,则加入倒计时,并挪用后端交心;后端照应获得考证码,即getWechatCode,将考证码收收到用户的企微,考证码为4位随机数字考证码不成为空,面打登录按钮前,先后端皆需要校验;登录胜利,则跳转到尾页;登录失利,弹窗展示失利疑息;
完毕
需要很大白,功用完毕便没有易。
登录界里是一个体系大概仄台使用的first sight,需要给用户留一个佳的影像。
前端
antd中并无自戴能够倒计时的按钮,但是antd pro的ProForm components中却是供给针对于考证码相干的组件ProFormCaptcha。
前端代码:
service.ts文献:- exportasyncfunctiongetWechatCode(params: any){returnrequest('/api/login/wechatCode',{method:'POST',data: params,});}exportasyncfunctionaccountLogin(params: LoginParamsType2){returnrequest('/api/login/login',{method:'POST',data: params,});}
复造代码 model.ts文献:- import{ stringify }from'querystring';import{ history, Reducer, Effect }from'umi';import{ accountLogin }from'@/services/login';import{ setAuthority, setCookie }from'@/utils/authority';import{ getPageQuery }from'@/utils/utils';import{ message }from'antd';exportinterfaceStateType{
- status?:'ok'|'error';
- type?: string;
- logMsg?: string;
- currentAuthority?:'user'|'guest'|'admin';}exportinterfaceLoginModelType{namespace: string;state: StateType;effects:{login: Effect;};reducers:{changeLoginStatus: Reducer<StateType>;setLoginErrorMsg: Reducer<StateType>;};}constModel: LoginModelType ={namespace:'login',state:{status:undefined,},effects:{*login({ payload, callback },{ call, put, select }){const response =yieldcall(accountLogin,{...payload});yieldput({type:'changeLoginStatus',payload: response,});// Login successfullyif(response.status ==='success'){setCookie("token", response.data?.jwtToken);const urlParams =newURL(window.location.href);const params =getPageQuery();
- message.success('登录胜利!');let{ redirect }= params as{redirect: string };if(redirect){const redirectUrlParams =newURL(redirect);if(redirectUrlParams.origin === urlParams.origin){
- redirect = redirect.substr(urlParams.origin.length);if(redirect.match(/^\/.*#/)){
- redirect = redirect.substr(redirect.indexOf('#')+1);}}else{
- window.location.href ='/';return;}}
- history.replace(redirect ||'/');}elseif(response.status ==='error'&& callback){callback()}},},reducers:{changeLoginStatus(state,{ payload }){setAuthority(payload.currentAuthority);return{...state,status: payload.status,logMsg: payload.msg,type:'account',};},},};exportdefault Model;
复造代码 Login.tsx文献,此中中心代码为throw new Error("获得考证码毛病");:- import React,{useState}from"react";import{Form, message}from'antd';import{connect, Dispatch, FormattedMessage, useIntl}from"umi";import type {StateType}from'@/models/login';import ProForm,{ProFormCaptcha, ProFormText}from'@ant-design/pro-form';import type {ConnectState}from'@/models/connect';import type {LoginParamsType2}from"@/services/login";import{getWechatCode}from"@/services/login";import styles from'./index.less';import{PasswordIcon, UserIdIcon, WechatCodeIcon}from'./svg/svg';interfaceLoginProps{dispatch: Dispatch;userLogin: StateType;
- submitting?: boolean;}constLoginMessage: React.FC<{content: string;}>=({content})=>(<div className={styles.loginMessage}>{content}</div>);constLogin: React.FC<LoginProps>=(props)=>{const{userLogin ={}, submitting}= props;const{status,type: loginType,}= userLogin;const[type]= useState<string>('account');const intl =useIntl();const[form]= Form.useForm();const[wechatCodeInfo, setWechatCodeInfo]= useState<any>({})consthandleSubmit=(values: LoginParamsType2)=>{const{dispatch}= props;dispatch({type:'login/login',payload:{...values, type},callback:()=>{dispatch({type:'imagecode/getImgCode',})}})};return(<div className={styles.main}><ProForm
- form={form}
- initialValues={{autoLogin:true,}}
- submitter={{render:(_, dom)=> dom.pop(),submitButtonProps:{loading: submitting,size:'large',style:{width:'100%',background:'#3F81EE',borderColor:'#3F81EE',},},searchConfig:{submitText:'登录',},}}
- onFinish={async(values)=>{handleSubmit(values);}}>{status ==='error'&&(<LoginMessage
- content={userLogin.logMsg ??''}/>)}{wechatCodeInfo.status ==='error'&&(<LoginMessage
- content={wechatCodeInfo.msg ??''}/>)}{type ==='account'&&(<><ProFormText
- name="loginName"
- fieldProps={{size:'large',prefix:<UserIdIcon className={styles.prefixIcon}/>,}}
- placeholder={intl.formatMessage({id:'pages.login.username.placeholder',defaultMessage:'请输出账号',})}
- rules={[{required:true,message:(<FormattedMessage
- id="pages.login.username.required"
- defaultMessage="请输出账号!"/>),},]}/><ProFormText.Password
- name="password"
- fieldProps={{size:'large',prefix:<PasswordIcon className={styles.prefixIcon}/>,}}
- placeholder={intl.formatMessage({id:'pages.login.password.placeholder',defaultMessage:'请输出您的暗码',})}
- rules={[{required:true,message:(<FormattedMessage
- id="pages.login.password.required"
- defaultMessage="请输出暗码!"/>),},]}/><ProFormCaptcha
- name="wechatCode"
- fieldProps={{size:'large',prefix:<WechatCodeIcon className={styles.prefixIcon}/>,}}
- captchaProps={{size:'small',}}
- placeholder={intl.formatMessage({id:'pages.login.wechatCode.placeholder',defaultMessage:'请输出企微考证码',})}
- captchaTextRender={(timing, count)=>{if(timing){return`${count} 获得考证码`;}return'获得考证码';}}
- rules={[{required:true,message:(<FormattedMessage
- id="pages.login.wechatCode.required"
- defaultMessage="请输出企微考证码!"/>),},{pattern:/^\d{4}$/,message:'请输出4位数字企微考证码!',},]}
- onGetCaptcha={async()=>{if(!form.getFieldValue('loginName')||!form.getFieldValue('password')){
- message.error('请输出用户名暗码');// 满意拦阻前提时,没有触收60s倒计时thrownewError("获得考证码毛病");return;}const values = form.getFieldsValue();getWechatCode({loginName: values.loginName,password: values.password,}).then((res: any)=>{setWechatCodeInfo(res)})}}/></>)}</ProForm></div>);}exportdefaultconnect(({login, loading}: ConnectState)=>({userLogin: login,submitting: loading.effects['login/login'],}))(Login);
复造代码 后端
Controller类,小我私家觉得不该该有所有数据校验逻辑,该当充足沉质化:- @RequestMapping("wechatCode")publicStringgetWechatCode(@RequestBodyJSONObject jsonObject){return userService.getWechatCode(jsonObject);}@RequestMapping("login")publicStringlogin(@RequestBodyJSONObject jsonObject){return userService.login(jsonObject);}
复造代码 service类:- @OverridepublicStringgetWechatCode(JSONObject jsonObject){String loginName = jsonObject.getString("loginName");String password = jsonObject.getString("password");if(StringUtils.isEmpty(loginName)||StringUtils.isEmpty(password)){returnJSONObject.toJSONString(ServiceUtil.returnError("用户名或者暗码不克不及为空!"));}try{// 鉴别用户可否存留DashboardUser user = userMapper.findUserByName(loginName);if(user ==null){returnJSONObject.toJSONString(ServiceUtil.returnError(Constant.USERNAME_PASSWORD_ERR_MSG));}// 鉴别暗码可否准确if(adminUserId.equals(user.getUserId())||1== user.getUserType()){// 一般用户if(!StringUtil.getMd5(password).equals(user.getUserPassword())){returnJSONObject.toJSONString(ServiceUtil.returnError(Constant.USERNAME_PASSWORD_ERR_MSG));}}else{// 域用户Boolean status =HttpUtil.checkDomain("CORP\"+ loginName, password, domainIp, domainPort);if(!status){returnJSONObject.toJSONString(ServiceUtil.returnError(Constant.USERNAME_PASSWORD_ERR_MSG));}}// 天生4位随机数字考证码并搁进慢存中,慢存key是用户名_login_valid_qiwei,value是考证码,有用期1分钟String wechatCode =DataUtil.getRandomNum(4);
- log.info("4位随机数字考证码是:{}", wechatCode);String wechatKey = loginName.concat("_login_valid_qiwei");RedisTool.setKeyValueExpire(jedisCluster, wechatKey, wechatCode,60);// 收收考证码到用户企微SendWeChatUtil.sendWeChat("您的账号在测验考试登录http://report.teslacorp.com,考证码:"+ wechatCode +",有用期1分钟,请实时处置。", user.getUserQiwei());returnJSONObject.toJSONString(ServiceUtil.returnSuccess(wechatCode));}catch(Exception e){returnJSONObject.toJSONString(ServiceUtil.returnError(e.getMessage()));}}@OverridepublicStringlogin(JSONObject jsonObject){String wechatCode = jsonObject.getString("wechatCode");String loginName = jsonObject.getString("loginName");String password = jsonObject.getString("password");if(StringUtils.isEmpty(wechatCode)){returnJSONObject.toJSONString(ServiceUtil.returnError("企微考证码过时,请沉试"));}if(StringUtils.isEmpty(loginName)||StringUtils.isEmpty(password)){returnJSONObject.toJSONString(ServiceUtil.returnError("用户名或者暗码不克不及为空!"));}// 考证企业微疑考证码String wechatKey = loginName.concat("_login_valid_qiwei");try{String cacheWechatCode =RedisTool.getValueByKey(jedisCluster, wechatKey);// 考证企业微疑考证码可否过时if(StringUtils.isEmpty(cacheWechatCode)){returnJSONObject.toJSONString(ServiceUtil.returnError("企微考证码过时,请沉试"));}// 考证企业微疑考证码可否准确if(!cacheWechatCode.equals(wechatCode)){returnJSONObject.toJSONString(ServiceUtil.returnError("企微考证码毛病,请沉试"));}RedisTool.delKey(jedisCluster,wechatKey);}catch(Exception e){returnJSONObject.toJSONString(ServiceUtil.returnError(e.getMessage()));}finally{RedisTool.delKey(jedisCluster, wechatKey);}try{// 鉴别用户可否存留DashboardUser user = userMapper.findUserByName(loginName);if(user ==null){returnJSONObject.toJSONString(ServiceUtil.returnError(Constant.USERNAME_PASSWORD_ERR_MSG));}// 一般用户if(adminUserId.equals(user.getUserId())||1== user.getUserType()){if(!StringUtil.getMd5(password).equals(user.getUserPassword())){returnJSONObject.toJSONString(ServiceUtil.returnError(Constant.USERNAME_PASSWORD_ERR_MSG));}}else{// 域用户Boolean status =HttpUtil.checkDomain("CORP\"+ loginName, password, domainIp, domainPort);if(!status){returnJSONObject.toJSONString(ServiceUtil.returnError(Constant.USERNAME_PASSWORD_ERR_MSG));}}List<DashboardRole> roleList = dashboardRoleResMapper.getRoleIdByUserId(user.getUserId());List<String> roleIds = roleList.stream().map(DashboardRole::getRoleId).collect(Collectors.toList());List<String> roleNames = roleList.stream().map(DashboardRole::getRoleName).collect(Collectors.toList());String jwtToken =JwtUtil.createJwt(user.getUserName(), user.getLoginName(), user.getUserId(), clientId, roleIds, roleNames,
- name, expiresSecond *1000L, base64Secret);JSONObject resultJson =newJSONObject();
- resultJson.put("jwtToken", jwtToken);returnJSONObject.toJSONString(ServiceUtil.returnSuccessData(resultJson));}catch(Exception e){returnJSONObject.toJSONString(ServiceUtil.returnError(e.getMessage()));}}
复造代码 参照
JWT初学学程
LDAP初学学程
企微东西类 |