相关推荐recommended
ruoyi的springboot企业微信登录方式实现(企业内部应用开发)
作者:mmseoamin日期:2024-01-30

文章目录

  • 前言
  • 一、企业微信的登录接口
    • 1.主要调用的企业微信api
    • 2.获取企业微信的corpId
    • 3.建立企业应用
    • 4.配置回调域名和可信IP(*重点)
    • 二、企业微信用户数据库设计
      • 1.企业表
      • 2.企业用户表
      • 三、springboot登录接口实现
        • 1.新建实体SysUserEnterprise
        • 2.修改LoginUser类
        • 3.重写SysLoginService的login方法
        • 4.企业微信登录接口
        • 5.开放接口
        • 总结

          前言

          主要是讲ruoyi前后端分离框架,springboot的微信小程序的实现方式,之前我发过一篇微信小程序的登录方式,企业微信的登录实现方式也是类似的(企业内部应用开发)

          ruoyi的springboot企业微信登录方式实现(企业内部应用开发),在这里插入图片描述,第1张


          一、企业微信的登录接口

          根据企业微信开放文档企业,企业微信通过扫码后可以获得用户的code,可用于当前企业下现有的企业微信用户,并且授予基本信息

          1.主要调用的企业微信api

          获取用户登录身份

          请求方式:GET(HTTPS)

          请求地址:https://qyapi.weixin.qq.com/cgi-bin/auth/getuserinfo?access_token=ACCESS_TOKEN&code=CODE

          参数说明:

          属性类型必填说明
          access_tokenstring调用接口凭证
          codestring通过成员授权获取到的code,最大为512字节。每次成员授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期。前端给予的入参

          2.获取企业微信的corpId

          打开企业微信,点击自己头像(需要管理员权限),点击管理企业

          ruoyi的springboot企业微信登录方式实现(企业内部应用开发),在这里插入图片描述,第2张

          点击【我的企业】,最下面有一个企业ID,就是corpId

          3.建立企业应用

          继续停留在管理企业页面,点击【应用管理】,最底下有一个创建应用

          ruoyi的springboot企业微信登录方式实现(企业内部应用开发),在这里插入图片描述,第3张

          创建好了,里面会有AgentId和Secret,把它获取到,到时候要存到数据库里面.

          4.配置回调域名和可信IP(*重点)

          配置回调域名和可信IP,这是企业微信官方要求的,如果你的企业已经认证了并且主体跟你的服务器域名不相同就没法配置,解决方法我这里就不详细写了,一般是没有这个情况(巨坑)

          点击刚才创建的应用里面,拉到最下面

          ruoyi的springboot企业微信登录方式实现(企业内部应用开发),在这里插入图片描述,第4张

          设置可信域名(redirect_uri回调uri,数据库需要存储)和授权登录(填写你服务器域名就好)

          可信IP填写你服务器的IP

          注意:设置可信域名需要在你的服务器文件夹放置官方给你的文件,用于校验的

          二、企业微信用户数据库设计

          1.企业表

          CREATE TABLE `sys_enterprise` (
            `enterprise_id` bigint NOT NULL AUTO_INCREMENT COMMENT '企业ID',
            `enterprise_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '企业名称',
            `corpid` varchar(64) COLLATE utf8mb4_bin NOT NULL COMMENT '企业唯一corpid',
            `agentid` varchar(64) COLLATE utf8mb4_bin NOT NULL COMMENT '企业应用ID',
            `corpsecret` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '企业应用秘钥',
            `redirect_uri` varchar(255) COLLATE utf8mb4_bin NOT NULL COMMENT '回调URL',
            `del_flag` char(2) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '0' COMMENT '删除标志',
            `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '' COMMENT '创建者',
            `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
            `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '' COMMENT '更新者',
            `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
            `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '备注',
            PRIMARY KEY (`enterprise_id`),
            UNIQUE KEY `uidx_corpid` (`corpid`) USING BTREE
          ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC COMMENT='企业微信表';
          

          2.企业用户表

          CREATE TABLE `sys_user_enterprise` (
            `user_id` bigint NOT NULL COMMENT '用户ID,关联sys_user的id',
            `enterprise_id` bigint NOT NULL COMMENT '企业ID,关联sys_enterprise的id',
            `enterprise_corp_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '企业微信的企业ID',
            `enterprise_name` varchar(50) COLLATE utf8mb4_bin NOT NULL COMMENT '企业名称',
            `enterprise_user_id` varchar(255) COLLATE utf8mb4_bin NOT NULL COMMENT '企业微信用户ID(字符串)',
            `enterprise_nickname` varchar(50) COLLATE utf8mb4_bin NOT NULL COMMENT '企业微信用户昵称',
            `enterprise_dept_id` bigint DEFAULT NULL COMMENT '企业微信部门ID',
            `enterprise_dept_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '企业微信部门名称',
            PRIMARY KEY (`user_id`,`enterprise_id`)
          ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC COMMENT='用户企业微信表';
          

          数据库设计尽量照着ruoyi的格式来,然后一些基本信息看自己需求来。


          三、springboot登录接口实现

          1.新建实体SysUserEnterprise

          在common模块下新建一个SysUserEnterprise,和SysUser同级

          package com.ruoyi.common.core.domain.entity;
          import org.apache.commons.lang3.builder.ToStringBuilder;
          import org.apache.commons.lang3.builder.ToStringStyle;
          import com.ruoyi.common.core.domain.BaseEntity;
          package com.echain.common.core.domain.entity;
          import com.echain.common.annotation.Excel;
          import lombok.Data;
          import org.apache.commons.lang3.builder.ToStringBuilder;
          import org.apache.commons.lang3.builder.ToStringStyle;
          /**
           * 用户企业微信对象 sys_user_enterprise
           * 
           */
          @Data
          public class SysUserEnterprise
          {
              private static final long serialVersionUID = 1L;
              /** 用户ID */
              private Long userId;
              /** 企业ID */
              private Long enterpriseId;
              /** 企业微信用户ID */
              @Excel(name = "企业微信用户ID")
              private String enterpriseUserId;
              /** 企业微信企业ID */
              @Excel(name = "企业微信企业ID")
              private String enterpriseCorpId;
              /** 企业微信企业名称 */
              @Excel(name = "企业微信企业名称")
              private String enterpriseName;
              /** 企业微信用户昵称 */
              @Excel(name = "企业微信用户昵称")
              private String enterpriseNickname;
              /** 企业微信部门ID */
              @Excel(name = "企业微信部门ID")
              private Long enterpriseDeptId;
              /** 企业微信部门名称 */
              @Excel(name = "企业微信部门名称")
              private String enterpriseDeptName;
              public void setUserId(Long userId) 
              {
                  this.userId = userId;
              }
              public Long getUserId() 
              {
                  return userId;
              }
              public void setEnterpriseId(Long enterpriseId) 
              {
                  this.enterpriseId = enterpriseId;
              }
              public Long getEnterpriseId() 
              {
                  return enterpriseId;
              }
              public void setEnterpriseNickname(String enterpriseNickname) 
              {
                  this.enterpriseNickname = enterpriseNickname;
              }
              public String getEnterpriseNickname() 
              {
                  return enterpriseNickname;
              }
              public void setEnterpriseDeptId(Long enterpriseDeptId) 
              {
                  this.enterpriseDeptId = enterpriseDeptId;
              }
              public Long getEnterpriseDeptId() 
              {
                  return enterpriseDeptId;
              }
              public void setEnterpriseDeptName(String enterpriseDeptName) 
              {
                  this.enterpriseDeptName = enterpriseDeptName;
              }
              public String getEnterpriseDeptName() 
              {
                  return enterpriseDeptName;
              }
              @Override
              public String toString() {
                  return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
                      .append("userId", getUserId())
                      .append("enterpriseId", getEnterpriseId())
                      .append("enterpriseUserId", getEnterpriseUserId())
                      .append("enterpriseNickname", getEnterpriseNickname())
                      .append("enterpriseDeptId", getEnterpriseDeptId())
                      .append("enterpriseDeptName", getEnterpriseDeptName())
                      .toString();
              }
          }
          

          2.修改LoginUser类

          在com.echain.common.core.domain.model.LoginUser的类加一个SysUserEnterprise,生成getter和setter(用了lombok可不加),并且重写LoginUser构造函数

          /**             
              /**
               * 企业微信用户信息
               */
              private SysUserEnterprise userEnterprise;
              
              public LoginUser(SysUserEnterprise userEnterprise) {
                  this.userEnterprise = userEnterprise;
              }
          

          3.重写SysLoginService的login方法

          这个接口是为了走SpringSecurity的登录验证方式,跟ruoyi原来的类似,并且这个是有绑定原sys_user表的数据,可以走原用户角色权限

              public String login(SysUser user, SysUserEnterprise userEnterprise, String userid, String corpsecret) {
                  LoginUser loginUser = new LoginUser(userEnterprise);
                  loginUser.setUserId(user.getUserId());
                  loginUser.setDeptId(user.getDeptId());
                  loginUser.setUser(user);
                  loginUser.setPermissions(permissionService.getMenuPermission(user));
                  UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userid, corpsecret);
                  authenticationToken.setDetails(loginUser);
                  SecurityContextHolder.getContext().setAuthentication(authenticationToken);
                  recordLoginInfo(loginUser.getUserId());
                  // 生成token
                  return tokenService.createToken(loginUser);
              }
          

          4.企业微信登录接口

          先添加一个企业微信接口调用凭证接口

              public String getEnterpriseAccessToken(String corpid, String corpsecret) {
                  Map params = new HashMap<>(2);
                  params.put("corpid",corpid);
                  params.put("corpsecret",corpsecret);
                  String accessToken = redisCache.getCacheObject("wxEnterprise" + "-" + params.get("corpid"));
                  if (Objects.isNull(accessToken)) {
                      accessToken = HttpUtil.get("https://qyapi.weixin.qq.com/cgi-bin/gettoken", params, 7200);
                      redisCache.setCacheObject("wxEnterprise" + "-" + params.get("corpid"), accessToken, 2, TimeUnit.HOURS);
                  }
                  // 如果accessToken不正确再获取一次
                  if (!isAccessTokenValid(accessToken)){
                      redisCache.deleteObject("wxEnterprise" + "-" + params.get("corpid"));
                      accessToken = HttpUtil.get("https://qyapi.weixin.qq.com/cgi-bin/gettoken", params, 7200);
                      redisCache.setCacheObject("wxEnterprise" + "-" + params.get("corpid"), accessToken, 2, TimeUnit.HOURS);
                  }
                  return accessToken;
              }
          	public boolean isAccessTokenValid(String accessToken) {
                  JSONObject jsonObjects = JSONObject.parseObject(accessToken);
                  Integer errcodes = (Integer) jsonObjects.get("errcode");
                  // 如果错误码为0,则表示 access_token 有效;否则,表示无效
                  return errcodes == 0;
              }
          

          这里是用了redis缓存,其中corpid、corpsecret是前面说的入参,accessToken的有效期是两小时,但是不知道为什么官方的会失效,所以会判断是否有效无效就重新调一次。

          然后写springboot的登录接口

              /**
               * 企业微信登录
               */
              @GetMapping(value = "/enterpriseLogin")
              @ResponseBody
              @Transactional
              public AjaxResult login(@RequestParam("code") String code, @RequestParam("corpid") String corpid) {
                  AjaxResult ajax = AjaxResult.success();
                  try {
                      // 查找是否还存在这个企业
                      SysEnterprise enterprise = enterpriseServiceImpl.selectSysEnterpriseByCorpid(corpid);
                      if (Objects.isNull(enterprise)){
                          return AjaxResult.error("该企业不存在");
                      }
                      String accessToken = loginService.getEnterpriseAccessToken(corpid, enterprise.getCorpsecret());
                      JSONObject jsonObjects = JSONObject.parseObject(accessToken);
                      Integer errcodes = (Integer) jsonObjects.get("errcode");
                      if (errcodes == 0) {
                          Map param = new HashMap<>(2);
                          param.put("access_token", jsonObjects.getString("access_token"));
                          param.put("code", code);
                          String result = HttpUtil.get("https://qyapi.weixin.qq.com/cgi-bin/auth/getuserinfo", param, 7200);
                          JSONObject jsonObject = JSON.parseObject(result);
                          Integer errcode = (Integer) jsonObject.get("errcode");
                          if (errcode == 0) {
                              String userId = (String) jsonObject.get("userid");
                              QueryWrapper userEnterpriseQueryWrapper = new QueryWrapper<>();
                              userEnterpriseQueryWrapper.eq("enterprise_user_id", userId);
                              userEnterpriseQueryWrapper.eq("enterprise_corp_id", corpid);
                              SysUserEnterprise userEnterprise = userEnterpriseServiceImpl.getOne(userEnterpriseQueryWrapper);
                              if (Objects.nonNull(userEnterprise)){
                                  // 先查找用戶数据
                                  SysUser user = userService.selectUserById(userEnterprise.getUserId());
                                  // 生成令牌
                                  String token = loginService.login(user, userEnterprise, userId, enterprise.getCorpsecret());
                                  ajax.put(Constants.TOKEN, token);
                                  return ajax;
                              }
                          }
                      }
                      // 请求重定向
                  } catch (Exception e) {
                      throw new ServiceException(e.getMessage());
                  }
                  return AjaxResult.error("登录失败");
              }
          

          selectSysEnterpriseByCorpid方法这个自己写一下吧,实际就是数据库查sys_enterprise表是否存在这行数据.

          到这里就大功告成了,存数据库这个操作我就不详细说了,还有要注意如果没有引入mybatis-plus,QueryWrapper那边需要改成根据enterprise_user_id和enterprise_corp_id获取用户信息的方法

          5.开放接口

          com.echain.framework.config.SecurityConfig的configure方法放行登录接口,在前面的/login、/captchaImage接口加一个/enterpriseLogin(根据自身需求加)

          // 对于登录login 验证码captchaImage 允许匿名访问
          .antMatchers("/login", "/captchaImage", "/enterpriseLogin").anonymous()
          

          总结

          实现之后,就能跟以前ruoyi的登录,通过token携带来访问接口,因为绑定了SysUser用户表,所以权限实际上还是跟着原用户角色权限走了,非常方便;如果遇到无法设置回调域名问题,可以评论区提问,一般是说跟企业主体不一致导致的。

          参考博客:ruoyi的springboot微信小程序登录实现方式(我自己写的,感兴趣的可以去看一下)