package com.dk.oauth.service.impl; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.dk.common.infrastructure.constant.Constant; import com.dk.common.infrastructure.constant.OauthConstants; import com.dk.common.infrastructure.enums.ErrorCodeEnum; import com.dk.common.model.response.mst.StaffResponse; import com.dk.common.response.ResponseCodeEnum; import com.dk.common.response.ResponseResultUtil; import com.dk.common.response.ResponseResultVO; import com.dk.common.util.DateUtils; import com.dk.common.util.HttpUtils; import com.dk.oauth.config.WxConfig; import com.dk.oauth.dto.AuthAccessTokenDto; import com.dk.oauth.entity.*; import com.dk.oauth.feign.service.StaffFeign; import com.dk.oauth.mapper.AuthAccessTokenMapper; import com.dk.oauth.mapper.CompanyMapper; import com.dk.oauth.mapper.UserMapper; import com.dk.oauth.service.IAuthAccessTokenService; import com.dk.oauth.shiro.jwt.JWTGenerator; import com.dk.oauth.util.AESSecurityUtil; import com.dk.oauth.util.UUID; import lombok.extern.slf4j.Slf4j; import org.apache.oltu.oauth2.as.issuer.OAuthIssuerImpl; import org.apache.oltu.oauth2.common.exception.OAuthSystemException; import org.apache.shiro.SecurityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; /** * (AuthAccessToken)表服务实现类 * * @author dapeng * @since 2022-07-01 09:41:05 */ @Slf4j @Service("authAccessTokenService") public class AuthAccessTokenServiceImpl extends ServiceImpl implements IAuthAccessTokenService { private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Resource private AuthAccessTokenMapper authAccessTokenMapper; @Resource private UserMapper userMapper; @Resource private CompanyMapper companyMapper; @Value("${aes-key}") private String AESKey; @Resource private StringRedisTemplate stringRedisTemplate; @Autowired private WxConfig config; @Resource private StaffFeign staffFeign; /** * 分页查询 * * @param * @return */ @Override public ResponseResultVO pageQuery(AuthAccessTokenDto authAccessTokenDto) { if (null == authAccessTokenDto.getPage()) { authAccessTokenDto.setPage(new Page(0, 10)); } IPage authAccessTokenDtos = authAccessTokenMapper.pageQuery(authAccessTokenDto.getPage(), authAccessTokenDto); return ResponseResultUtil.success(authAccessTokenDtos); } /** * @desc : 查询用户最新token * @author : 洪旭东 * @date : 2022-08-02 17:30 */ public String getCurrentToken(Long userId) { return authAccessTokenMapper.getCurrentToken(userId); } /** * @desc : 登录凭证校验 * @author : 姜永辉 * @date : 2022/5/12 9:33 */ public ResponseResultVO loginWechat(Map map) { ResponseResultVO res = HttpUtils.get(config.getCode2Session() + "appid=" + config.getAppId() + "&secret=" + config.getAppSecret() + "&js_code=" + map.get("code") + "&grant_type=authorization_code"); if (res.getData() != null && res.getData().get("errcode") != null) { return ResponseResultUtil.error(ResponseCodeEnum.OPERATE_FAIL.getCode(), res.getData().get("errmsg") == null ? res.getData().getString("errcode") : "微信服务器异常:" + res.getData().getString("errmsg")); } return res; } /** * @desc : 获取token * @author : 洪旭东 * @date : 2024-02-20 14:04 */ public ResponseResultVO token(HttpServletRequest request) { UserLogin userLogin = userMapper.getByPhone(request.getParameter("phone")); if (userLogin == null || (!userLogin.getUserPwd().equals(request.getParameter("password")))) { //无用户 或 密码错误 return ResponseResultUtil.error(ErrorCodeEnum.USER_PASSWORD_ERROR.getCode(), ErrorCodeEnum.USER_PASSWORD_ERROR.getMessage()); } userLogin.checkUserLogin(); try { // 设置应用代码 userLogin.setAppCode(Constant.AppCode.WEB.getCode()); return createToken(userLogin); } catch (Exception e) { log.error("获取accessToken发生异常=", e); return ResponseResultUtil.error(HttpServletResponse.SC_BAD_REQUEST, e.getMessage()); } } /** * @desc : 小程序通过微信openid登录 * @author : 洪旭东 * @date : 2024-02-20 14:04 */ @Transactional(rollbackFor = Exception.class) public ResponseResultVO wxToken(UserWxLogin userWxLogin) { // todo userWxLogin.setUserWxid("1"); UserLogin userLogin = userMapper.getByWxid(userWxLogin.getUserWxid()); if (userLogin == null) { //无用户 return ResponseResultUtil.error(ErrorCodeEnum.USER_NOT_EXIST.getCode(), ErrorCodeEnum.USER_NOT_EXIST.getMessage()); } userLogin.checkUserLogin(); try { // 设置应用代码 userLogin.setAppCode(Constant.AppCode.WEIXIN.getCode()); return createToken(userLogin); } catch (Exception e) { log.error("获取accessToken发生异常=", e); return ResponseResultUtil.error(HttpServletResponse.SC_BAD_REQUEST, e.getMessage()); } } /** * @desc : 小程序通过微信openid登录--体验的账号 * @author : 姜永辉 * @date : 2024-02-20 14:04 */ @Transactional(rollbackFor = Exception.class) public ResponseResultVO wxFeignExperienceToken(UserWxLogin userWxLogin) { UserLogin userLogin = userMapper.getByWxid(userWxLogin.getUserWxid()); if (userLogin == null) { //无用户 return ResponseResultUtil.error(ErrorCodeEnum.USER_NOT_EXIST.getCode(), ErrorCodeEnum.USER_NOT_EXIST.getMessage()); } try { // 设置应用代码 userLogin.setAppCode(Constant.AppCode.WEIXIN.getCode()); return createFeignExperienceToken(userLogin); } catch (Exception e) { log.error("获取accessToken发生异常=", e); return ResponseResultUtil.error(HttpServletResponse.SC_BAD_REQUEST, e.getMessage()); } } /** * @desc : 登录验证成功,生成token * @author : 姜永辉 * @date : 2024-02-20 10:31 */ private ResponseResultVO createFeignExperienceToken(UserLogin userLogin) throws OAuthSystemException { String clientId = "dkic"; AuthAccessToken authAccessToken = new AuthAccessToken(); String username = ""; String userId = ""; username = userLogin.getUserName(); userId = String.valueOf(userLogin.getUserId()); //当前公司 CompanyResponse company = null; String accessToken = ""; if (userLogin.getCurrentCp() != null) { company = companyMapper.getByCpId(userLogin.getCurrentCp()); if (company != null) { JWTGenerator jwtGenerator = new JWTGenerator(); jwtGenerator.setSalt(username); jwtGenerator.setUsername(username); jwtGenerator.setUserId(userId); jwtGenerator.setClientId(clientId); jwtGenerator.setCpId(company.getCpId().toString()); jwtGenerator.setCpCode(company.getCpCode()); OAuthIssuerImpl oAuthIssuer = new OAuthIssuerImpl(jwtGenerator); accessToken = oAuthIssuer.accessToken(); log.info("服务器生成的accessToken=" + accessToken); // 保存token authAccessToken.setId(UUID.uuid32()); authAccessToken.setClientId(clientId); authAccessToken.setTokenId(accessToken); authAccessToken.setCreateDate(new Date()); authAccessToken.setCpId(company.getCpId().toString()); authAccessToken.setCpCode(company.getCpCode()); authAccessToken.setTokenExpiredSeconds(OauthConstants.EXPIRES_IN); log.info("---->>>SecurityUtils.getSubject().isAuthenticated() = " + SecurityUtils.getSubject().isAuthenticated()); // endregion // region 加密accessToken try { accessToken = AESSecurityUtil.encrypt(AESKey, accessToken); } catch (Exception e) { logger.error("sorry,accessToken({}) encode faild!!", accessToken); } } } // 默认取体验公司的dktest账号 Map collectQuery = new HashMap<>(); collectQuery.put("cpId", userLogin.getCurrentCp()); collectQuery.put("staffCode", "东科智云-标准版-体验"); ResponseResultVO feignExperience = staffFeign.getFeignExperience(collectQuery); StaffResponse s = new StaffResponse(); log.info("---->>>getFeignExperience = " + feignExperience.toString()); //如果没有成功返回,状态设置为待审 if (feignExperience.getCode() != ResponseCodeEnum.SUCCESS.getCode()) { //无用户 return ResponseResultUtil.error(ErrorCodeEnum.USER_NOT_EXIST.getCode(), ErrorCodeEnum.USER_NOT_EXIST.getMessage()); } else { s = feignExperience.getData(); } UserLoginSuccess userLoginSuccess = new UserLoginSuccess() .setByUserLogin(userLogin) .setAccessToken(accessToken) .setCompany(company) .setStaffResponse(s) // .setMenuList(userMapper.getMenuByUser(userLogin.getAppCode(),userId,company.getCpId(),"zh_CN")) ; return ResponseResultUtil.success(userLoginSuccess); } /** * @desc : 注册 * @author : 洪旭东 * @date : 2024-02-20 13:55 */ @Transactional(rollbackFor = Exception.class) public ResponseResultVO register(UserWxLogin userWxLogin) { UserLogin userLogin = userMapper.getByWxid(userWxLogin.getUserWxid()); //通过openid查到用户,但手机号为空 if (userLogin != null && userLogin.getUserPhone() == null) { //将其他微信用户的相同电话清空 userMapper.cleanPhone(userWxLogin.getUserPhone()); //更新当前手机号 userMapper.updatePhone(userLogin.getUserId(), userWxLogin.getUserPhone()); } if (userLogin == null) { //openid没查到,用手机号再查一次 userLogin = userMapper.getByPhone(userWxLogin.getUserPhone()); } else if (userLogin.getUserWxid() == null) { //通过手机号查到了用户,但是openid是空,更新上当前的openid userMapper.updateWxid(userLogin.getUserId(), userWxLogin.getUserWxid()); } //用户无法通过openid或手机号查到 或 通过手机号查到了,但openid不同 if (userLogin == null || !userWxLogin.getUserWxid().equals(userLogin.getUserWxid())) { //将其他微信用户的相同电话清空 userMapper.cleanPhone(userWxLogin.getUserPhone()); //注册 userMapper.insert(userWxLogin); userLogin = userMapper.getByWxid(userWxLogin.getUserWxid()); } userLogin.checkUserLogin(); try { return createToken(userLogin); } catch (Exception e) { log.error("获取accessToken发生异常=", e); return ResponseResultUtil.error(HttpServletResponse.SC_BAD_REQUEST, e.getMessage()); } } /** * @desc : 验证当前扫码登录是否匹配到openid * @author : 洪旭东 * @date : 2024-02-20 14:04 */ public ResponseResultVO checkLoginOpenId(String uuid) { String openId = stringRedisTemplate.opsForValue().get(Constant.RedisConstant.REDIS_LOGIN_UUID.getName() + uuid); if (openId != null && !"".equals(openId)) { UserLogin userLogin = userMapper.getByWxid(openId); if (userLogin == null) { stringRedisTemplate.opsForValue().set(Constant.RedisConstant.REDIS_LOGIN_UUID.getName() + uuid, "", 5, TimeUnit.MINUTES); //无用户 return ResponseResultUtil.error(ErrorCodeEnum.USER_NOT_EXIST.getCode(), ErrorCodeEnum.USER_NOT_EXIST.getMessage()); } userLogin.checkUserLogin(); try { return createToken(userLogin); } catch (OAuthSystemException e) { e.printStackTrace(); log.error("获取accessToken发生异常=", e); return ResponseResultUtil.error(HttpServletResponse.SC_BAD_REQUEST, e.getMessage()); } } else { return ResponseResultUtil.error(ResponseCodeEnum.NO_LOGIN); } } /** * @desc : 登录验证成功,生成token * @author : 洪旭东 * @date : 2024-02-20 10:31 */ private ResponseResultVO createToken(UserLogin userLogin) throws OAuthSystemException { String clientId = "dkic"; AuthAccessToken authAccessToken = new AuthAccessToken(); // region 开始生成Access Token String username = ""; String userId = ""; username = userLogin.getUserName(); userId = String.valueOf(userLogin.getUserId()); // endregion //当前公司 CompanyResponse company = null; String accessToken = ""; if (userLogin.getCurrentCp() != null) { company = companyMapper.getByCpId(userLogin.getCurrentCp()); if (company != null) { JWTGenerator jwtGenerator = new JWTGenerator(); jwtGenerator.setSalt(username); jwtGenerator.setUsername(username); jwtGenerator.setUserId(userId); jwtGenerator.setClientId(clientId); jwtGenerator.setCpId(company.getCpId().toString()); jwtGenerator.setCpCode(company.getCpCode()); jwtGenerator.setAppCode(userLogin.getAppCode()); OAuthIssuerImpl oAuthIssuer = new OAuthIssuerImpl(jwtGenerator); accessToken = oAuthIssuer.accessToken(); log.info("服务器生成的accessToken=" + accessToken); // 保存token authAccessToken.setId(UUID.uuid32()); authAccessToken.setClientId(clientId); authAccessToken.setTokenId(accessToken); authAccessToken.setCreateDate(new Date()); authAccessToken.setUserId(userId); authAccessToken.setCpId(company.getCpId().toString()); authAccessToken.setCpCode(company.getCpCode()); authAccessToken.setAppCode(userLogin.getAppCode()); authAccessToken.setTokenExpiredSeconds(OauthConstants.EXPIRES_IN); log.info("---->>>SecurityUtils.getSubject().isAuthenticated() = " + SecurityUtils.getSubject().isAuthenticated()); // endregion // region 加密accessToken try { accessToken = AESSecurityUtil.encrypt(AESKey, accessToken); // 往redis记录缓存 Map ul = new HashMap<>(); ul.put("accessToken",accessToken); ul.put("opUpdateTime",LocalDateTime.now()); stringRedisTemplate.opsForValue().set(Constant.RedisConstant.REDIS_USER_LOGIN.getName() + '_' + userId + '_' + userLogin.getAppCode(), JSONObject.toJSONString(ul)); } catch (Exception e) { logger.error("sorry,accessToken({}) encode faild!!", accessToken); } } } UserLoginSuccess userLoginSuccess = new UserLoginSuccess() .setByUserLogin(userLogin) .setAccessToken(accessToken) .setCompany(company); return ResponseResultUtil.success(userLoginSuccess); } /** * @desc : 生成微信临时二维码 * @author : 洪旭东 * @date : 2024-02-20 17:00 */ public ResponseResultVO getQrCode(String uuid) { //获取access token String accessToken = stringRedisTemplate.opsForValue().get(Constant.RedisConstant.REDIS_WECHAT_ACCESS_TOKEN.getName()); if (accessToken == null) { ResponseResultVO tokenRes = HttpUtils.get(config.getAccessToken() + "appid=" + config.getOffiAccountAppId() + "&secret=" + config.getOffiAccountAppSecret()); if (tokenRes.getCode() == ResponseCodeEnum.SUCCESS.getCode()) { stringRedisTemplate.opsForValue().set(Constant.RedisConstant.REDIS_WECHAT_ACCESS_TOKEN.getName(), tokenRes.getData().getString("access_token"), 5, TimeUnit.MINUTES); accessToken = tokenRes.getData().getString("access_token"); } else { return tokenRes; } } //生成临时二维码 String ticket; JSONObject json = new JSONObject(); //有效期30天,最大可设置30天 json.put("expire_seconds", 2592000); json.put("action_name", "QR_STR_SCENE"); JSONObject scene = new JSONObject(); scene.put("scene_str", "dkic-scan-login-" + uuid); JSONObject actionInfo = new JSONObject(); actionInfo.put("scene", scene); json.put("action_info", actionInfo); ResponseResultVO ticketRes = HttpUtils.post(config.getQrcodeCreate() + accessToken, json); if (ticketRes.getCode() == ResponseCodeEnum.SUCCESS.getCode()) { ticket = ticketRes.getData().getString("ticket"); } else { return ticketRes; } stringRedisTemplate.opsForValue().set(Constant.RedisConstant.REDIS_LOGIN_UUID.getName() + uuid, "", 5, TimeUnit.MINUTES); return ResponseResultUtil.success(config.getShowQrCode() + ticket); } /** * @desc : 生成微信小程序二维码 * @author : 姜永辉 * @date : 2024-02-20 17:00 */ public ResponseResultVO getWxQrCode(Map map) { System.out.println("getWxQrCode:" + map); //获取access token String accessToken = stringRedisTemplate.opsForValue().get(Constant.RedisConstant.REDIS_WECHAT_QRCODE_ACCESS_TOKEN.getName()); if (accessToken == null) { ResponseResultVO tokenRes = HttpUtils.get(config.getAccessToken() + "appid=" + config.getAppId() + "&secret=" + config.getAppSecret()); if (tokenRes.getCode() == ResponseCodeEnum.SUCCESS.getCode()) { stringRedisTemplate.opsForValue().set(Constant.RedisConstant.REDIS_WECHAT_QRCODE_ACCESS_TOKEN.getName(), tokenRes.getData().getString("access_token"), 5, TimeUnit.MINUTES); accessToken = tokenRes.getData().getString("access_token"); } else { return tokenRes; } } //二维码参数 Map param = new HashMap<>(); param.put("page", "pages/welcome/welcome"); param.put("scene", "s=" + map.get("openid")); param.put("check_path", false); //生成二维码接口地址 String url = config.getUnlimitedQRCode() + accessToken; //文件名称 String fileName = java.util.UUID.randomUUID().toString() + ".png"; //文件相对路径 - 返回 String relativePath = "/qr_code/" + fileName; //文件绝对路径 - 写 String absolutelyPath = this.createDirByPath("qr_code") + fileName; ResponseResultVO responseResultVO = HttpUtils.postReturnFile(url, param, absolutelyPath); if (responseResultVO.getCode() == ResponseCodeEnum.SUCCESS.getCode()) { return ResponseResultUtil.success(relativePath); } else { return responseResultVO; } } private String createDirByPath(String type) { /** * @date_time 2020-07-31 09:42 * @author H_x_d * @description 根据类型创建目录文件夹 * @param [type] * @return java.lang.String */ String path = type + "/" + DateUtils.formatNow("yyyy-MM-dd") + "/"; String base = config.getUploadPath(); File baseDir = new File(base); if (!baseDir.exists()) { baseDir.mkdir(); } String[] paths = path.split("/"); for (int i = 0; i < paths.length; i++) { base = base + "/" + paths[i]; File dir = new File(base); if (!dir.exists()) { dir.mkdir(); } } return base + "/"; } }