zhangherong
2025-06-25 23855599412c4d61b38d78f0f3abd3430a48b5b1
lxzn-module-system/lxzn-system-biz/src/main/java/org/jeecg/modules/system/controller/ThirdLoginController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,421 @@
package org.jeecg.modules.system.controller;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xkcoding.justauth.AuthRequestFactory;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.request.AuthRequest;
import me.zhyd.oauth.utils.AuthStateUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.util.PasswordUtil;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.common.util.RestUtil;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.config.thirdapp.ThirdAppConfig;
import org.jeecg.config.thirdapp.ThirdAppTypeItemVo;
import org.jeecg.modules.base.service.BaseCommonService;
import org.jeecg.modules.system.entity.SysThirdAccount;
import org.jeecg.modules.system.entity.SysUser;
import org.jeecg.modules.system.model.ThirdLoginModel;
import org.jeecg.modules.system.service.ISysThirdAccountService;
import org.jeecg.modules.system.service.ISysUserService;
import org.jeecg.modules.system.service.impl.ThirdAppDingtalkServiceImpl;
import org.jeecg.modules.system.service.impl.ThirdAppWechatEnterpriseServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.List;
/**
 * @Author scott
 * @since 2018-12-17
 */
@Controller
@RequestMapping("/sys/thirdLogin")
@Slf4j
public class ThirdLoginController {
   @Autowired
   private ISysUserService sysUserService;
   @Autowired
   private ISysThirdAccountService sysThirdAccountService;
   @Autowired
   private BaseCommonService baseCommonService;
   @Autowired
    private RedisUtil redisUtil;
   @Autowired
   private AuthRequestFactory factory;
   @Autowired
   ThirdAppConfig thirdAppConfig;
   @Autowired
   private ThirdAppWechatEnterpriseServiceImpl thirdAppWechatEnterpriseService;
   @Autowired
   private ThirdAppDingtalkServiceImpl thirdAppDingtalkService;
   @RequestMapping("/render/{source}")
    public void render(@PathVariable("source") String source, HttpServletResponse response) throws IOException {
        log.info("第三方登录进入render:" + source);
        AuthRequest authRequest = factory.get(source);
        String authorizeUrl = authRequest.authorize(AuthStateUtils.createState());
        log.info("第三方登录认证地址:" + authorizeUrl);
        response.sendRedirect(authorizeUrl);
    }
   @RequestMapping("/{source}/callback")
    public String loginThird(@PathVariable("source") String source, AuthCallback callback,ModelMap modelMap) {
      log.info("第三方登录进入callback:" + source + " params:" + JSONObject.toJSONString(callback));
        AuthRequest authRequest = factory.get(source);
        AuthResponse response = authRequest.login(callback);
        log.info(JSONObject.toJSONString(response));
        Result<JSONObject> result = new Result<JSONObject>();
        if(response.getCode()==2000) {
           JSONObject data = JSONObject.parseObject(JSONObject.toJSONString(response.getData()));
           String username = data.getString("username");
           String avatar = data.getString("avatar");
           String uuid = data.getString("uuid");
           //构造第三方登录信息存储对象
         ThirdLoginModel tlm = new ThirdLoginModel(source, uuid, username, avatar);
           //判断有没有这个人
         //update-begin-author:wangshuai date:20201118 for:修改成查询第三方账户表
           LambdaQueryWrapper<SysThirdAccount> query = new LambdaQueryWrapper<SysThirdAccount>();
           query.eq(SysThirdAccount::getThirdUserUuid, uuid);
           query.eq(SysThirdAccount::getThirdType, source);
           List<SysThirdAccount> thridList = sysThirdAccountService.list(query);
         SysThirdAccount user = null;
           if(thridList==null || thridList.size()==0) {
            //否则直接创建新账号
            user = sysThirdAccountService.saveThirdUser(tlm);
           }else {
              //已存在 åªè®¾ç½®ç”¨æˆ·å ä¸è®¾ç½®å¤´åƒ
              user = thridList.get(0);
           }
           // ç”Ÿæˆtoken
         //update-begin-author:wangshuai date:20201118 for:从第三方登录查询是否存在用户id,不存在绑定手机号
         if(oConvertUtils.isNotEmpty(user.getSysUserId())) {
            String sysUserId = user.getSysUserId();
            SysUser sysUser = sysUserService.getById(sysUserId);
            String token = saveToken(sysUser);
             modelMap.addAttribute("token", token);
         }else{
            modelMap.addAttribute("token", "绑定手机号,"+""+uuid);
         }
         //update-end-author:wangshuai date:20201118 for:从第三方登录查询是否存在用户id,不存在绑定手机号
      //update-begin--Author:wangshuai  Date:20200729 for:接口在签名校验失败时返回失败的标识码 issues#1441--------------------
        }else{
         modelMap.addAttribute("token", "登录失败");
      }
      //update-end--Author:wangshuai  Date:20200729 for:接口在签名校验失败时返回失败的标识码 issues#1441--------------------
        result.setSuccess(false);
        result.setMessage("第三方登录异常,请联系管理员");
        return "thirdLogin";
    }
   /**
    * åˆ›å»ºæ–°è´¦å·
    * @param model
    * @return
    */
   @PostMapping("/user/create")
   @ResponseBody
   public Result<String> thirdUserCreate(@RequestBody ThirdLoginModel model) {
      log.info("第三方登录创建新账号:" );
      Result<String> res = new Result<>();
      Object operateCode = redisUtil.get(CommonConstant.THIRD_LOGIN_CODE);
      if(operateCode==null || !operateCode.toString().equals(model.getOperateCode())){
         res.setSuccess(false);
         res.setMessage("校验失败");
         return res;
      }
      //创建新账号
      //update-begin-author:wangshuai date:20201118 for:修改成从第三方登录查出来的user_id,在查询用户表尽行token
      SysThirdAccount user = sysThirdAccountService.saveThirdUser(model);
      if(oConvertUtils.isNotEmpty(user.getSysUserId())){
         String sysUserId = user.getSysUserId();
         SysUser sysUser = sysUserService.getById(sysUserId);
         // ç”Ÿæˆtoken
         String token = saveToken(sysUser);
         //update-end-author:wangshuai date:20201118 for:修改成从第三方登录查出来的user_id,在查询用户表尽行token
         res.setResult(token);
         res.setSuccess(true);
      }
      return res;
   }
   /**
    * ç»‘定账号 éœ€è¦è®¾ç½®å¯†ç  éœ€è¦èµ°ä¸€éæ ¡éªŒ
    * @param json
    * @return
    */
   @PostMapping("/user/checkPassword")
   @ResponseBody
   public Result<String> checkPassword(@RequestBody JSONObject json) {
      Result<String> result = new Result<>();
      Object operateCode = redisUtil.get(CommonConstant.THIRD_LOGIN_CODE);
      if(operateCode==null || !operateCode.toString().equals(json.getString("operateCode"))){
         result.setSuccess(false);
         result.setMessage("校验失败");
         return result;
      }
      String username = json.getString("uuid");
      SysUser user = this.sysUserService.getUserByName(username);
      if(user==null){
         result.setMessage("用户未找到");
         result.setSuccess(false);
         return result;
      }
      String password = json.getString("password");
      String salt = user.getSalt();
      String passwordEncode = PasswordUtil.encrypt(user.getUsername(), password, salt);
      if(!passwordEncode.equals(user.getPassword())){
         result.setMessage("密码不正确");
         result.setSuccess(false);
         return result;
      }
      sysUserService.updateById(user);
      result.setSuccess(true);
      // ç”Ÿæˆtoken
      String token = saveToken(user);
      result.setResult(token);
      return result;
   }
   private String saveToken(SysUser user) {
      // ç”Ÿæˆtoken
      String token = JwtUtil.sign(user.getUsername(), user.getPassword());
      redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token);
      // è®¾ç½®è¶…æ—¶æ—¶é—´
      redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME * 2 / 1000);
      return token;
   }
   /**
    * ç¬¬ä¸‰æ–¹ç™»å½•回调接口
    * @param token
    * @param thirdType
    * @return
    * @throws Exception
    */
   @SuppressWarnings("unchecked")
   @RequestMapping(value = "/getLoginUser/{token}/{thirdType}", method = RequestMethod.GET)
   @ResponseBody
   public Result<JSONObject> getThirdLoginUser(@PathVariable("token") String token,@PathVariable("thirdType") String thirdType) throws Exception {
      Result<JSONObject> result = new Result<JSONObject>();
      String username = JwtUtil.getUsername(token);
      //1. æ ¡éªŒç”¨æˆ·æ˜¯å¦æœ‰æ•ˆ
      SysUser sysUser = sysUserService.getUserByName(username);
      result = sysUserService.checkUserIsEffective(sysUser, null);
      if(!result.isSuccess()) {
         return result;
      }
      //update-begin-author:wangshuai date:20201118 for:如果真实姓名和头像不存在就取第三方登录的
      LambdaQueryWrapper<SysThirdAccount> query = new LambdaQueryWrapper<>();
      query.eq(SysThirdAccount::getSysUserId,sysUser.getId());
      query.eq(SysThirdAccount::getThirdType,thirdType);
      SysThirdAccount account = sysThirdAccountService.getOne(query);
      if(oConvertUtils.isEmpty(sysUser.getRealname())){
         sysUser.setRealname(account.getRealname());
      }
      if(oConvertUtils.isEmpty(sysUser.getAvatar())){
         sysUser.setAvatar(account.getAvatar());
      }
      //update-end-author:wangshuai date:20201118 for:如果真实姓名和头像不存在就取第三方登录的
      JSONObject obj = new JSONObject();
      //用户登录信息
      obj.put("userInfo", sysUser);
      //token ä¿¡æ¯
      obj.put("token", token);
      result.setResult(obj);
      result.setSuccess(true);
      result.setCode(200);
      baseCommonService.addLog("用户名: " + username + ",登录成功[第三方用户]!", CommonConstant.LOG_TYPE_1, null);
      return result;
   }
   /**
    * ç¬¬ä¸‰æ–¹ç»‘定手机号返回token
    *
    * @param jsonObject
    * @return
    */
   @ApiOperation("手机号登录接口")
   @PostMapping("/bindingThirdPhone")
   @ResponseBody
   public Result<String> bindingThirdPhone(@RequestBody JSONObject jsonObject) {
      Result<String> result = new Result<String>();
      String phone = jsonObject.getString("mobile");
      String thirdUserUuid = jsonObject.getString("thirdUserUuid");
      // æ ¡éªŒéªŒè¯ç 
      String captcha = jsonObject.getString("captcha");
      //update-begin-author:taoyan date:2022-9-13 for: VUEN-2245 ã€æ¼æ´žã€‘发现新漏洞待处理20220906
      String redisKey = CommonConstant.PHONE_REDIS_KEY_PRE+phone;
      Object captchaCache = redisUtil.get(redisKey);
      //update-end-author:taoyan date:2022-9-13 for: VUEN-2245 ã€æ¼æ´žã€‘发现新漏洞待处理20220906
      if (oConvertUtils.isEmpty(captcha) || !captcha.equals(captchaCache)) {
         result.setMessage("验证码错误");
         result.setSuccess(false);
         return result;
      }
      //校验用户有效性
      SysUser sysUser = sysUserService.getUserByPhone(phone);
      if(sysUser != null){
         // å­˜åœ¨ç”¨æˆ·ï¼Œç›´æŽ¥ç»‘定
         sysThirdAccountService.updateThirdUserId(sysUser,thirdUserUuid);
      }else{
         // ä¸å­˜åœ¨æ‰‹æœºå·ï¼Œåˆ›å»ºç”¨æˆ·
         sysUser = sysThirdAccountService.createUser(phone,thirdUserUuid);
      }
      String token = saveToken(sysUser);
      result.setSuccess(true);
      result.setResult(token);
      return result;
   }
   /**
    * ä¼ä¸šå¾®ä¿¡/钉钉 OAuth2登录
    *
    * @param source
    * @param state
    * @return
    */
   @ResponseBody
   @GetMapping("/oauth2/{source}/login")
   public String oauth2LoginCallback(@PathVariable("source") String source, @RequestParam("state") String state, HttpServletResponse response) throws Exception {
      String url;
      if (ThirdAppConfig.WECHAT_ENTERPRISE.equalsIgnoreCase(source)) {
         ThirdAppTypeItemVo config = thirdAppConfig.getWechatEnterprise();
         StringBuilder builder = new StringBuilder();
         // æž„造企业微信OAuth2登录授权地址
         builder.append("https://open.weixin.qq.com/connect/oauth2/authorize");
         // ä¼ä¸šçš„CorpID
         builder.append("?appid=").append(config.getClientId());
         // æŽˆæƒåŽé‡å®šå‘的回调链接地址,请使用urlencode对链接进行处理
         String redirectUri = RestUtil.getBaseUrl() + "/sys/thirdLogin/oauth2/wechat_enterprise/callback";
         builder.append("&redirect_uri=").append(URLEncoder.encode(redirectUri, "UTF-8"));
         // è¿”回类型,此时固定为:code
         builder.append("&response_type=code");
         // åº”用授权作用域。
         // snsapi_base:静默授权,可获取成员的的基础信息(UserId与DeviceId);
         builder.append("&scope=snsapi_base");
         // é‡å®šå‘后会带上state参数,长度不可超过128个字节
         builder.append("&state=").append(state);
         // ç»ˆç«¯ä½¿ç”¨æ­¤å‚数判断是否需要带上身份信息
         builder.append("#wechat_redirect");
         url = builder.toString();
      } else if (ThirdAppConfig.DINGTALK.equalsIgnoreCase(source)) {
         ThirdAppTypeItemVo config = thirdAppConfig.getDingtalk();
         StringBuilder builder = new StringBuilder();
         // æž„造钉钉OAuth2登录授权地址
         builder.append("https://login.dingtalk.com/oauth2/auth");
         // æŽˆæƒé€šè¿‡/拒绝后回调地址。
         // æ³¨æ„ éœ€è¦ä¸Žæ³¨å†Œåº”用时登记的域名保持一致。
         String redirectUri = RestUtil.getBaseUrl() + "/sys/thirdLogin/oauth2/dingtalk/callback";
         builder.append("?redirect_uri=").append(URLEncoder.encode(redirectUri, "UTF-8"));
         // å›ºå®šå€¼ä¸ºcode。
         // æŽˆæƒé€šè¿‡åŽè¿”回authCode。
         builder.append("&response_type=code");
         // æ­¥éª¤ä¸€ä¸­åˆ›å»ºçš„应用详情中获取。
         // ä¼ä¸šå†…部应用:client_id为应用的AppKey。
         builder.append("&client_id=").append(config.getClientId());
         // æŽˆæƒèŒƒå›´ï¼ŒæŽˆæƒé¡µé¢æ˜¾ç¤ºçš„æŽˆæƒä¿¡æ¯ä»¥åº”用注册时配置的为准。
         // openid:授权后可获得用户userid
         builder.append("&scope=openid");
         // è·ŸéšauthCode原样返回。
         builder.append("&state=").append(state);
            //update-begin---author:wangshuai ---date:20220613  for:[issues/I5BOUF]oauth2 é’‰é’‰æ— æ³•登录------------
            builder.append("&prompt=").append("consent");
            //update-end---author:wangshuai ---date:20220613  for:[issues/I5BOUF]oauth2 é’‰é’‰æ— æ³•登录--------------
            url = builder.toString();
      } else {
         return "不支持的source";
      }
      log.info("oauth2 login url:" + url);
      response.sendRedirect(url);
      return "login…";
   }
    /**
     * ä¼ä¸šå¾®ä¿¡/钉钉 OAuth2登录回调
     *
     * @param code
     * @param state
     * @param response
     * @return
     */
   @ResponseBody
   @GetMapping("/oauth2/{source}/callback")
   public String oauth2LoginCallback(
         @PathVariable("source") String source,
         // ä¼ä¸šå¾®ä¿¡è¿”回的code
         @RequestParam(value = "code", required = false) String code,
         // é’‰é’‰è¿”回的code
         @RequestParam(value = "authCode", required = false) String authCode,
         @RequestParam("state") String state,
         HttpServletResponse response) {
        SysUser loginUser;
        if (ThirdAppConfig.WECHAT_ENTERPRISE.equalsIgnoreCase(source)) {
            log.info("【企业微信】OAuth2登录进入callback:code=" + code + ", state=" + state);
            loginUser = thirdAppWechatEnterpriseService.oauth2Login(code);
            if (loginUser == null) {
                return "登录失败";
            }
        } else if (ThirdAppConfig.DINGTALK.equalsIgnoreCase(source)) {
         log.info("【钉钉】OAuth2登录进入callback:authCode=" + authCode + ", state=" + state);
         loginUser = thirdAppDingtalkService.oauth2Login(authCode);
         if (loginUser == null) {
            return "登录失败";
         }
        } else {
            return "不支持的source";
        }
        try {
         //update-begin-author:taoyan date:2022-6-30 for: å·¥ä½œæµå‘送消息 ç‚¹å‡»æ¶ˆæ¯é“¾æŽ¥è·³è½¬åŠžç†é¡µé¢
         String redirect = "";
         if (state.indexOf("?") > 0) {
            String[] arr = state.split("\\?");
            state = arr[0];
            if(arr.length>1){
               redirect = arr[1];
            }
         }
         String token = saveToken(loginUser);
         state += "/oauth2-app/login?oauth2LoginToken=" + URLEncoder.encode(token, "UTF-8");
         //update-begin---author:wangshuai ---date:20220613  for:[issues/I5BOUF]oauth2 é’‰é’‰æ— æ³•登录------------
         state += "&thirdType=" + source;
         //state += "&thirdType=" + "wechat_enterprise";
         if (redirect != null && redirect.length() > 0) {
            state += "&" + redirect;
         }
         //update-end-author:taoyan date:2022-6-30 for: å·¥ä½œæµå‘送消息 ç‚¹å‡»æ¶ˆæ¯é“¾æŽ¥è·³è½¬åŠžç†é¡µé¢
            //update-end---author:wangshuai ---date:20220613  for:[issues/I5BOUF]oauth2 é’‰é’‰æ— æ³•登录------------
         log.info("OAuth2登录重定向地址: " + state);
            try {
                response.sendRedirect(state);
                return "ok";
            } catch (IOException e) {
                e.printStackTrace();
                return "重定向失败";
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return "解码失败";
        }
    }
}