chenyc
2025-12-09 b38f8abd8a9865148792f4bc996c461211b88561
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
/**
 * 企业微信 OAuth 2.0 认证模块
 */
 
/**
 * 获取企业应用的配置信息
 * 需要在 config.js 中设置以下内容:
 * - CORP_ID: 企业ID
 * - AGENT_ID: 应用ID
 * - REDIRECT_URI: 回调地址
 */
export class WeChatOAuth {
    constructor(config) {
        this.corpId = config.corpId;
        this.agentId = config.agentId;
        this.redirectUri = config.redirectUri;
        this.userInfo = null;
    }
 
    /**
     * 获取 OAuth 授权链接
     */
    getAuthUrl() {
        const params = new URLSearchParams({
            appid: this.corpId,
            redirect_uri: this.redirectUri,
            response_type: 'code',
            scope: 'snsapi_userinfo',
            state: this.generateState()
        });
        return `https://open.weixin.qq.com/connect/oauth2/authorize?${params.toString()}#wechat_redirect`;
    }
 
    /**
     * 生成随机 state 参数,用于防 CSRF
     */
    generateState() {
        const state = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
        sessionStorage.setItem('oauth_state', state);
        return state;
    }
 
    /**
     * 验证 state 参数
     */
    verifyState(state) {
        const savedState = sessionStorage.getItem('oauth_state');
        sessionStorage.removeItem('oauth_state');
        return state === savedState;
    }
 
    /**
     * 从 URL 获取授权码
     */
    getAuthCode() {
        const params = new URLSearchParams(window.location.search);
        const code = params.get('code');
        const state = params.get('state');
 
        if (!code) {
            return null;
        }
 
        if (!this.verifyState(state)) {
            console.error('State 验证失败,可能存在 CSRF 攻击');
            return null;
        }
 
        return code;
    }
 
    /**
     * 使用授权码向后端交换用户信息
     * @param {string} code - 授权码
     * @returns {Promise<Object>} 用户信息
     */
    async exchangeCodeForUserInfo(code) {
        try {
            const response = await fetch('/api/wecom/getUserInfo', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({ code })
            });
 
            if (!response.ok) {
                throw new Error(`HTTP error! status: ${response.status}`);
            }
 
            const data = await response.json();
            
            if (data.code === 0) {
                this.userInfo = data.data;
                // 将用户信息存储到 sessionStorage(会话级别)
                sessionStorage.setItem('wecom_user_info', JSON.stringify(data.data));
                return data.data;
            } else {
                throw new Error(data.message || '获取用户信息失败');
            }
        } catch (error) {
            console.error('交换授权码失败:', error);
            throw error;
        }
    }
 
    /**
     * 从本地存储获取用户信息
     */
    getUserInfo() {
        if (this.userInfo) {
            return this.userInfo;
        }
 
        const stored = sessionStorage.getItem('wecom_user_info');
        if (stored) {
            this.userInfo = JSON.parse(stored);
            return this.userInfo;
        }
 
        return null;
    }
 
    /**
     * 清除用户信息和授权状态
     */
    clearUserInfo() {
        this.userInfo = null;
        sessionStorage.removeItem('wecom_user_info');
        sessionStorage.removeItem('oauth_state');
    }
 
    /**
     * 检查用户是否已授权
     */
    isAuthorized() {
        return this.getUserInfo() !== null;
    }
}
 
export default WeChatOAuth;