package com.ruoyi.auth.service; import com.alibaba.fastjson.JSONObject; import com.ruoyi.auth.config.QywxInnerConfig; import com.ruoyi.auth.utils.RestUtils; import com.ruoyi.common.core.constant.Constants; import com.ruoyi.common.core.exception.ServiceException; import com.ruoyi.common.core.utils.StringUtils; import com.ruoyi.system.api.model.AgentConfigVo; import org.apache.commons.codec.binary.Hex; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.concurrent.TimeUnit; @Service public class QywxInnerService { private final static Logger logger = LoggerFactory.getLogger("QywxInnerService"); @Autowired private QywxInnerConfig qywxInnerConfig; @Autowired private StringRedisTemplate redisTemplate; public String getAccessToken(){ String accessToken = redisTemplate.opsForValue().get(Constants.QY_WX_ACCESS_TOKEN_KEY); if(!StringUtils.isEmpty(accessToken)){ return accessToken; } String corpId = qywxInnerConfig.getCorpId(); String agentSecret = qywxInnerConfig.getAgentSecret(); String accessTokenUrl = String.format(qywxInnerConfig.getAccessTokenUrl(), corpId, agentSecret); JSONObject response = RestUtils.get(accessTokenUrl); // 获取错误日志 if(response.containsKey(Constants.QY_WX_ERR_CODE) && response.getInteger(Constants.QY_WX_ERR_CODE) != 0){ logger.error(response.toString()); throw new ServiceException("获取企业微信ACCESS_TOKEN异常"); } accessToken = response.getString(Constants.QY_WX_ACCESS_TOKEN); Long expiresIn = response.getLong(Constants.QY_WX_EXPIRES_IN); expiresIn = expiresIn - 100; redisTemplate.opsForValue().set(Constants.QY_WX_ACCESS_TOKEN_KEY, accessToken, expiresIn, TimeUnit.SECONDS); return accessToken; } //********************************** H5应用 Oauth *************************// /** * 构造网页授权链接 * @param oauthRedirectUrl * @return */ public String getOauthUrl(String oauthRedirectUrl){ // 应用授权作用域。 // snsapi_base:静默授权,可获取成员的基础信息(UserId与DeviceId); // snsapi_userinfo:静默授权,可获取成员的详细信息,但不包含手机、邮箱等敏感信息; // snsapi_privateinfo:手动授权,可获取成员的详细信息,包含手机、邮箱等敏感信息(已不再支持获取手机号/邮箱)。 String corpId = qywxInnerConfig.getCorpId(); String state = qywxInnerConfig.getAuthorizeState(); String agentId = qywxInnerConfig.getAgentId(); // 构造网页授权链接 redirect_uri return String.format(qywxInnerConfig.getOauthUrl(), corpId, oauthRedirectUrl, agentId, state); } /** * 获取访问用户身份 * 获取访问用户敏感信息 * @param code * @return */ public JSONObject getOauthUser(String code) { String accessToken = getAccessToken(); // 获取访问用户身份 String getOauthUrl = String.format(qywxInnerConfig.getOauthUserUrl(), accessToken, code); JSONObject response = RestUtils.get(getOauthUrl); if(response.containsKey(Constants.QY_WX_ERR_CODE) && response.getInteger(Constants.QY_WX_ERR_CODE) != 0){ logger.error(response.toString()); throw new ServiceException("获取企业微信信息异常"); } logger.info("----------------userinfo-------------"); logger.info(response.toString()); //获取通讯录用户详情get String userId = response.getString(Constants.QY_WX_USER_ID); // 成员票据,最大为512字节,有效期为1800s。 scope为snsapi_privateinfo,获取访问用户敏感信息 String userTicket = response.getString(Constants.QY_WX_USER_TICKET); // 获取用户详情 包含部门信息 // String url = String.format(qywxInnerConfig.getUserDetailUrl(),accessToken,userId); // 获取访问用户敏感信息 String url = String.format(qywxInnerConfig.getUserDetailUrlAuth(), accessToken); JSONObject json = new JSONObject(); json.put(Constants.QY_WX_USER_TICKET, userTicket); JSONObject detailResponse = RestUtils.post(url, json); //获取错误日志 if(detailResponse.containsKey(Constants.QY_WX_ERR_CODE) && detailResponse.getInteger(Constants.QY_WX_ERR_CODE) != 0){ logger.error(detailResponse.toString()); throw new ServiceException("获取企业微信信息异常"); } logger.info("----------------userinfo detail -------------"); logger.info(detailResponse.toString()); return detailResponse; } public AgentConfigVo getAgentConfig(String url, String type) { AgentConfigVo agentConfigVo = new AgentConfigVo(); agentConfigVo.setAgentid(qywxInnerConfig.getAgentId()); agentConfigVo.setCorpid(qywxInnerConfig.getCorpId()); //临时票据 String ticket = getJsApiTicket(type); if (StringUtils.isEmpty(ticket)) { throw new ServiceException("获取票据异常"); } //当前时间戳 转成秒 long timestamp = System.currentTimeMillis() / 1000; //随机字符串 String nonceStr = Constants.QY_WX_NONCE_STR; String signature = getSignature(ticket, nonceStr, timestamp, url); agentConfigVo.setTimestamp(String.valueOf(timestamp)); agentConfigVo.setNonceStr(nonceStr); agentConfigVo.setSignature(signature); return agentConfigVo; } private String getSignature(String ticket, String nonceStr, long timestamp, String url) { String unEncryptStrBase = "jsapi_ticket=%s&noncestr=%s×tamp=%s&url=%s"; try { String unEncryptStr = String.format(unEncryptStrBase, ticket, nonceStr, timestamp, url); logger.info("----------------unEncryptStr-------------"); logger.info(unEncryptStr); MessageDigest digest = MessageDigest.getInstance("SHA-1"); // 调用digest方法,进行加密操作 byte[] cipherBytes = digest.digest(unEncryptStr.getBytes()); String encryptStr = Hex.encodeHexString(cipherBytes); logger.info("----------------getSignature-------------"); logger.info(encryptStr); return encryptStr; } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return null; } /** * 获取ticket * @paran type * @return */ public String getJsApiTicket(String type) { String accessToken = getAccessToken(); String ticketKey = Constants.QY_WX_TICKET_KEY; String jsapiTicketUrl = qywxInnerConfig.getJsapiTicketUrl(); if("agent_config".equals(type)){ ticketKey = ticketKey + type; jsapiTicketUrl = qywxInnerConfig.getJsapiTicketAgentUrl(); } String ticket = redisTemplate.opsForValue().get(ticketKey); if(!StringUtils.isEmpty(ticket)){ return ticket; } String url = String.format(jsapiTicketUrl, accessToken); JSONObject response = RestUtils.get(url); if(response.containsKey(Constants.QY_WX_ERR_CODE) && response.getInteger(Constants.QY_WX_ERR_CODE) != 0){ logger.error(response.toString()); throw new ServiceException("获取企业微信信息异常"); } ticket = response.getString(Constants.QY_WX_TICKET); Long expiresIn = response.getLong(Constants.QY_WX_EXPIRES_IN); expiresIn = expiresIn - 100; redisTemplate.opsForValue().set(ticketKey, ticket, expiresIn, TimeUnit.SECONDS); return ticket; } }