e00e58109e48fda3499da1a4661d2e17df1d2426.svn-base 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. package org.jeecg.modules.system.controller;
  2. import com.alibaba.fastjson.JSONObject;
  3. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  4. import com.xkcoding.justauth.AuthRequestFactory;
  5. import io.swagger.annotations.ApiOperation;
  6. import lombok.extern.slf4j.Slf4j;
  7. import me.zhyd.oauth.model.AuthCallback;
  8. import me.zhyd.oauth.model.AuthResponse;
  9. import me.zhyd.oauth.request.AuthRequest;
  10. import me.zhyd.oauth.utils.AuthStateUtils;
  11. import org.jeecg.common.api.vo.Result;
  12. import org.jeecg.common.constant.CommonConstant;
  13. import org.jeecg.common.system.util.JwtUtil;
  14. import org.jeecg.common.util.PasswordUtil;
  15. import org.jeecg.common.util.RedisUtil;
  16. import org.jeecg.common.util.RestUtil;
  17. import org.jeecg.common.util.oConvertUtils;
  18. import org.jeecg.config.thirdapp.ThirdAppConfig;
  19. import org.jeecg.config.thirdapp.ThirdAppTypeItemVo;
  20. import org.jeecg.modules.base.service.BaseCommonService;
  21. import org.jeecg.modules.system.entity.SysThirdAccount;
  22. import org.jeecg.modules.system.entity.SysUser;
  23. import org.jeecg.modules.system.model.ThirdLoginModel;
  24. import org.jeecg.modules.system.service.ISysThirdAccountService;
  25. import org.jeecg.modules.system.service.ISysUserService;
  26. import org.jeecg.modules.system.service.impl.ThirdAppDingtalkServiceImpl;
  27. import org.jeecg.modules.system.service.impl.ThirdAppWechatEnterpriseServiceImpl;
  28. import org.springframework.beans.factory.annotation.Autowired;
  29. import org.springframework.stereotype.Controller;
  30. import org.springframework.ui.ModelMap;
  31. import org.springframework.web.bind.annotation.*;
  32. import javax.servlet.http.HttpServletResponse;
  33. import java.io.IOException;
  34. import java.io.UnsupportedEncodingException;
  35. import java.net.URLEncoder;
  36. import java.util.List;
  37. /**
  38. * @Author scott
  39. * @since 2018-12-17
  40. */
  41. @Controller
  42. @RequestMapping("/sys/thirdLogin")
  43. @Slf4j
  44. public class ThirdLoginController {
  45. @Autowired
  46. private ISysUserService sysUserService;
  47. @Autowired
  48. private ISysThirdAccountService sysThirdAccountService;
  49. @Autowired
  50. private BaseCommonService baseCommonService;
  51. @Autowired
  52. private RedisUtil redisUtil;
  53. @Autowired
  54. private AuthRequestFactory factory;
  55. @Autowired
  56. ThirdAppConfig thirdAppConfig;
  57. @Autowired
  58. private ThirdAppWechatEnterpriseServiceImpl thirdAppWechatEnterpriseService;
  59. @Autowired
  60. private ThirdAppDingtalkServiceImpl thirdAppDingtalkService;
  61. @RequestMapping("/render/{source}")
  62. public void render(@PathVariable("source") String source, HttpServletResponse response) throws IOException {
  63. log.info("第三方登录进入render:" + source);
  64. AuthRequest authRequest = factory.get(source);
  65. String authorizeUrl = authRequest.authorize(AuthStateUtils.createState());
  66. log.info("第三方登录认证地址:" + authorizeUrl);
  67. response.sendRedirect(authorizeUrl);
  68. }
  69. @RequestMapping("/{source}/callback")
  70. public String loginThird(@PathVariable("source") String source, AuthCallback callback,ModelMap modelMap) {
  71. log.info("第三方登录进入callback:" + source + " params:" + JSONObject.toJSONString(callback));
  72. AuthRequest authRequest = factory.get(source);
  73. AuthResponse response = authRequest.login(callback);
  74. log.info(JSONObject.toJSONString(response));
  75. Result<JSONObject> result = new Result<JSONObject>();
  76. if(response.getCode()==2000) {
  77. JSONObject data = JSONObject.parseObject(JSONObject.toJSONString(response.getData()));
  78. String username = data.getString("username");
  79. String avatar = data.getString("avatar");
  80. String uuid = data.getString("uuid");
  81. //构造第三方登录信息存储对象
  82. ThirdLoginModel tlm = new ThirdLoginModel(source, uuid, username, avatar);
  83. //判断有没有这个人
  84. //update-begin-author:wangshuai date:20201118 for:修改成查询第三方账户表
  85. LambdaQueryWrapper<SysThirdAccount> query = new LambdaQueryWrapper<SysThirdAccount>();
  86. query.eq(SysThirdAccount::getThirdUserUuid, uuid);
  87. query.eq(SysThirdAccount::getThirdType, source);
  88. List<SysThirdAccount> thridList = sysThirdAccountService.list(query);
  89. SysThirdAccount user = null;
  90. if(thridList==null || thridList.size()==0) {
  91. //否则直接创建新账号
  92. user = sysThirdAccountService.saveThirdUser(tlm);
  93. }else {
  94. //已存在 只设置用户名 不设置头像
  95. user = thridList.get(0);
  96. }
  97. // 生成token
  98. //update-begin-author:wangshuai date:20201118 for:从第三方登录查询是否存在用户id,不存在绑定手机号
  99. if(oConvertUtils.isNotEmpty(user.getSysUserId())) {
  100. String sysUserId = user.getSysUserId();
  101. SysUser sysUser = sysUserService.getById(sysUserId);
  102. String token = saveToken(sysUser);
  103. modelMap.addAttribute("token", token);
  104. }else{
  105. modelMap.addAttribute("token", "绑定手机号,"+""+uuid);
  106. }
  107. //update-end-author:wangshuai date:20201118 for:从第三方登录查询是否存在用户id,不存在绑定手机号
  108. //update-begin--Author:wangshuai Date:20200729 for:接口在签名校验失败时返回失败的标识码 issues#1441--------------------
  109. }else{
  110. modelMap.addAttribute("token", "登录失败");
  111. }
  112. //update-end--Author:wangshuai Date:20200729 for:接口在签名校验失败时返回失败的标识码 issues#1441--------------------
  113. result.setSuccess(false);
  114. result.setMessage("第三方登录异常,请联系管理员");
  115. return "thirdLogin";
  116. }
  117. /**
  118. * 创建新账号
  119. * @param model
  120. * @return
  121. */
  122. @PostMapping("/user/create")
  123. @ResponseBody
  124. public Result<String> thirdUserCreate(@RequestBody ThirdLoginModel model) {
  125. log.info("第三方登录创建新账号:" );
  126. Result<String> res = new Result<>();
  127. Object operateCode = redisUtil.get(CommonConstant.THIRD_LOGIN_CODE);
  128. if(operateCode==null || !operateCode.toString().equals(model.getOperateCode())){
  129. res.setSuccess(false);
  130. res.setMessage("校验失败");
  131. return res;
  132. }
  133. //创建新账号
  134. //update-begin-author:wangshuai date:20201118 for:修改成从第三方登录查出来的user_id,在查询用户表尽行token
  135. SysThirdAccount user = sysThirdAccountService.saveThirdUser(model);
  136. if(oConvertUtils.isNotEmpty(user.getSysUserId())){
  137. String sysUserId = user.getSysUserId();
  138. SysUser sysUser = sysUserService.getById(sysUserId);
  139. // 生成token
  140. String token = saveToken(sysUser);
  141. //update-end-author:wangshuai date:20201118 for:修改成从第三方登录查出来的user_id,在查询用户表尽行token
  142. res.setResult(token);
  143. res.setSuccess(true);
  144. }
  145. return res;
  146. }
  147. /**
  148. * 绑定账号 需要设置密码 需要走一遍校验
  149. * @param json
  150. * @return
  151. */
  152. @PostMapping("/user/checkPassword")
  153. @ResponseBody
  154. public Result<String> checkPassword(@RequestBody JSONObject json) {
  155. Result<String> result = new Result<>();
  156. Object operateCode = redisUtil.get(CommonConstant.THIRD_LOGIN_CODE);
  157. if(operateCode==null || !operateCode.toString().equals(json.getString("operateCode"))){
  158. result.setSuccess(false);
  159. result.setMessage("校验失败");
  160. return result;
  161. }
  162. String username = json.getString("uuid");
  163. SysUser user = this.sysUserService.getUserByName(username);
  164. if(user==null){
  165. result.setMessage("用户未找到");
  166. result.setSuccess(false);
  167. return result;
  168. }
  169. String password = json.getString("password");
  170. String salt = user.getSalt();
  171. String passwordEncode = PasswordUtil.encrypt(user.getUsername(), password, salt);
  172. if(!passwordEncode.equals(user.getPassword())){
  173. result.setMessage("密码不正确");
  174. result.setSuccess(false);
  175. return result;
  176. }
  177. sysUserService.updateById(user);
  178. result.setSuccess(true);
  179. // 生成token
  180. String token = saveToken(user);
  181. result.setResult(token);
  182. return result;
  183. }
  184. private String saveToken(SysUser user) {
  185. // 生成token
  186. String token = JwtUtil.sign(user.getUsername(), user.getPassword());
  187. redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token);
  188. // 设置超时时间
  189. redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME / 1000);
  190. return token;
  191. }
  192. @SuppressWarnings("unchecked")
  193. @RequestMapping(value = "/getLoginUser/{token}/{thirdType}", method = RequestMethod.GET)
  194. @ResponseBody
  195. public Result<JSONObject> getThirdLoginUser(@PathVariable("token") String token,@PathVariable("thirdType") String thirdType) throws Exception {
  196. Result<JSONObject> result = new Result<JSONObject>();
  197. String username = JwtUtil.getUsername(token);
  198. //1. 校验用户是否有效
  199. SysUser sysUser = sysUserService.getUserByName(username);
  200. result = sysUserService.checkUserIsEffective(sysUser);
  201. if(!result.isSuccess()) {
  202. return result;
  203. }
  204. //update-begin-author:wangshuai date:20201118 for:如果真实姓名和头像不存在就取第三方登录的
  205. LambdaQueryWrapper<SysThirdAccount> query = new LambdaQueryWrapper<>();
  206. query.eq(SysThirdAccount::getSysUserId,sysUser.getId());
  207. query.eq(SysThirdAccount::getThirdType,thirdType);
  208. SysThirdAccount account = sysThirdAccountService.getOne(query);
  209. if(oConvertUtils.isEmpty(sysUser.getRealname())){
  210. sysUser.setRealname(account.getRealname());
  211. }
  212. if(oConvertUtils.isEmpty(sysUser.getAvatar())){
  213. sysUser.setAvatar(account.getAvatar());
  214. }
  215. //update-end-author:wangshuai date:20201118 for:如果真实姓名和头像不存在就取第三方登录的
  216. JSONObject obj = new JSONObject();
  217. //用户登录信息
  218. obj.put("userInfo", sysUser);
  219. //token 信息
  220. obj.put("token", token);
  221. result.setResult(obj);
  222. result.setSuccess(true);
  223. result.setCode(200);
  224. baseCommonService.addLog("用户名: " + username + ",登录成功[第三方用户]!", CommonConstant.LOG_TYPE_1, null);
  225. return result;
  226. }
  227. /**
  228. * 第三方绑定手机号返回token
  229. *
  230. * @param jsonObject
  231. * @return
  232. */
  233. @ApiOperation("手机号登录接口")
  234. @PostMapping("/bindingThirdPhone")
  235. @ResponseBody
  236. public Result<String> bindingThirdPhone(@RequestBody JSONObject jsonObject) {
  237. Result<String> result = new Result<String>();
  238. String phone = jsonObject.getString("mobile");
  239. String thirdUserUuid = jsonObject.getString("thirdUserUuid");
  240. //校验用户有效性
  241. SysUser sysUser = sysUserService.getUserByPhone(phone);
  242. if(sysUser != null){
  243. sysThirdAccountService.updateThirdUserId(sysUser,thirdUserUuid);
  244. }else{
  245. // 不存在手机号,创建用户
  246. String smscode = jsonObject.getString("captcha");
  247. Object code = redisUtil.get(phone);
  248. if (!smscode.equals(code)) {
  249. result.setMessage("手机验证码错误");
  250. result.setSuccess(false);
  251. return result;
  252. }
  253. //创建用户
  254. sysUser = sysThirdAccountService.createUser(phone,thirdUserUuid);
  255. }
  256. String token = saveToken(sysUser);
  257. result.setSuccess(true);
  258. result.setResult(token);
  259. return result;
  260. }
  261. /**
  262. * 企业微信/钉钉 OAuth2登录
  263. *
  264. * @param source
  265. * @param state
  266. * @return
  267. */
  268. @ResponseBody
  269. @GetMapping("/oauth2/{source}/login")
  270. public String oauth2LoginCallback(@PathVariable("source") String source, @RequestParam("state") String state, HttpServletResponse response) throws Exception {
  271. String url;
  272. if (ThirdAppConfig.WECHAT_ENTERPRISE.equalsIgnoreCase(source)) {
  273. ThirdAppTypeItemVo config = thirdAppConfig.getWechatEnterprise();
  274. StringBuilder builder = new StringBuilder();
  275. // 构造企业微信OAuth2登录授权地址
  276. builder.append("https://open.weixin.qq.com/connect/oauth2/authorize");
  277. // 企业的CorpID
  278. builder.append("?appid=").append(config.getClientId());
  279. // 授权后重定向的回调链接地址,请使用urlencode对链接进行处理
  280. String redirectUri = RestUtil.getBaseUrl() + "/sys/thirdLogin/oauth2/wechat_enterprise/callback";
  281. builder.append("&redirect_uri=").append(URLEncoder.encode(redirectUri, "UTF-8"));
  282. // 返回类型,此时固定为:code
  283. builder.append("&response_type=code");
  284. // 应用授权作用域。
  285. // snsapi_base:静默授权,可获取成员的的基础信息(UserId与DeviceId);
  286. builder.append("&scope=snsapi_base");
  287. // 重定向后会带上state参数,长度不可超过128个字节
  288. builder.append("&state=").append(state);
  289. // 终端使用此参数判断是否需要带上身份信息
  290. builder.append("#wechat_redirect");
  291. url = builder.toString();
  292. } else if (ThirdAppConfig.DINGTALK.equalsIgnoreCase(source)) {
  293. ThirdAppTypeItemVo config = thirdAppConfig.getDingtalk();
  294. StringBuilder builder = new StringBuilder();
  295. // 构造钉钉OAuth2登录授权地址
  296. builder.append("https://login.dingtalk.com/oauth2/auth");
  297. // 授权通过/拒绝后回调地址。
  298. // 注意 需要与注册应用时登记的域名保持一致。
  299. String redirectUri = RestUtil.getBaseUrl() + "/sys/thirdLogin/oauth2/dingtalk/callback";
  300. builder.append("?redirect_uri=").append(URLEncoder.encode(redirectUri, "UTF-8"));
  301. // 固定值为code。
  302. // 授权通过后返回authCode。
  303. builder.append("&response_type=code");
  304. // 步骤一中创建的应用详情中获取。
  305. // 企业内部应用:client_id为应用的AppKey。
  306. builder.append("&client_id=").append(config.getClientId());
  307. // 授权范围,授权页面显示的授权信息以应用注册时配置的为准。
  308. // openid:授权后可获得用户userid
  309. builder.append("&scope=openid");
  310. // 跟随authCode原样返回。
  311. builder.append("&state=").append(state);
  312. url = builder.toString();
  313. } else {
  314. return "不支持的source";
  315. }
  316. log.info("oauth2 login url:" + url);
  317. response.sendRedirect(url);
  318. return "login…";
  319. }
  320. /**
  321. * 企业微信/钉钉 OAuth2登录回调
  322. *
  323. * @param code
  324. * @param state
  325. * @param response
  326. * @return
  327. */
  328. @ResponseBody
  329. @GetMapping("/oauth2/{source}/callback")
  330. public String oauth2LoginCallback(
  331. @PathVariable("source") String source,
  332. // 企业微信返回的code
  333. @RequestParam(value = "code", required = false) String code,
  334. // 钉钉返回的code
  335. @RequestParam(value = "authCode", required = false) String authCode,
  336. @RequestParam("state") String state,
  337. HttpServletResponse response
  338. ) {
  339. SysUser loginUser;
  340. if (ThirdAppConfig.WECHAT_ENTERPRISE.equalsIgnoreCase(source)) {
  341. log.info("【企业微信】OAuth2登录进入callback:code=" + code + ", state=" + state);
  342. loginUser = thirdAppWechatEnterpriseService.oauth2Login(code);
  343. if (loginUser == null) {
  344. return "登录失败";
  345. }
  346. } else if (ThirdAppConfig.DINGTALK.equalsIgnoreCase(source)) {
  347. log.info("【钉钉】OAuth2登录进入callback:authCode=" + authCode + ", state=" + state);
  348. loginUser = thirdAppDingtalkService.oauth2Login(authCode);
  349. if (loginUser == null) {
  350. return "登录失败";
  351. }
  352. } else {
  353. return "不支持的source";
  354. }
  355. try {
  356. String token = saveToken(loginUser);
  357. state += "/oauth2-app/login?oauth2LoginToken=" + URLEncoder.encode(token, "UTF-8");
  358. state += "&thirdType=" + "wechat_enterprise";
  359. log.info("OAuth2登录重定向地址: " + state);
  360. try {
  361. response.sendRedirect(state);
  362. return "ok";
  363. } catch (IOException e) {
  364. e.printStackTrace();
  365. return "重定向失败";
  366. }
  367. } catch (UnsupportedEncodingException e) {
  368. e.printStackTrace();
  369. return "解码失败";
  370. }
  371. }
  372. }