jiangqs
2023-05-09 7a0dd449cc2f49c18c2bc324a3ed283b5f1b3cf5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
package com.ruoyi.auth.service;
 
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.shaded.org.checkerframework.checker.units.qual.A;
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.apache.commons.lang3.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.cache.RedisCache;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
 
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
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) {
 
        AgentConfigVo agentConfigVo = new AgentConfigVo();
        agentConfigVo.setAgentid(qywxInnerConfig.getAgentId());
        agentConfigVo.setCorpid(qywxInnerConfig.getCorpId());
 
        //临时票据
        String ticket = getJsApiTicket();
        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&timestamp=%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 accessToken = getAccessToken();
        String ticket = redisTemplate.opsForValue().get(Constants.QY_WX_TICKET_KEY);
        if(!StringUtils.isEmpty(ticket)){
            return ticket;
        }
        String url = String.format(qywxInnerConfig.getJsapiTicketAgentUrl(), 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(Constants.QY_WX_TICKET_KEY, ticket, expiresIn, TimeUnit.SECONDS);
 
        return ticket;
    }
}