001/**
002 * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com).
003 * <p>
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 * <p>
008 *  http://www.apache.org/licenses/LICENSE-2.0
009 * <p>
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package io.jboot.wechat.interceptor;
017
018import com.jfinal.aop.Interceptor;
019import com.jfinal.aop.Invocation;
020import com.jfinal.core.JFinal;
021import com.jfinal.weixin.sdk.api.ApiConfigKit;
022import com.jfinal.weixin.sdk.api.ApiResult;
023import io.jboot.utils.StrUtil;
024import io.jboot.wechat.controller.JbootWechatController;
025
026import javax.servlet.http.HttpServletRequest;
027
028/**
029 * 获取用户信息的连接器。
030 * 相关文档:https://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html
031 */
032public class WechatUserInterceptor implements Interceptor {
033
034
035    public static final String AUTHORIZE_URL = "https://open.weixin.qq.com/connect/oauth2/authorize"
036            + "?appid={appid}"
037            + "&redirect_uri={redirecturi}"
038            + "&response_type=code"
039            + "&scope=snsapi_userinfo"
040            + "&state=235#wechat_redirect";
041
042
043    public static final String BASE_AUTHORIZE_URL = "https://open.weixin.qq.com/connect/oauth2/authorize"
044            + "?appid={appid}"
045            + "&redirect_uri={redirecturi}"
046            + "&response_type=code"
047            + "&scope=snsapi_base"
048            + "&state=235#wechat_redirect";
049
050
051    @Override
052    public void intercept(Invocation inv) {
053
054        JbootWechatController controller = (JbootWechatController) inv.getController();
055
056        /**
057         * 是否允许访问,默认情况下只有是微信浏览器允许访问
058         */
059        if (controller.isAllowVisit()) {
060            doIntercept(inv);
061            return;
062        }
063
064        controller.doNotAllowVisitRedirect();
065
066    }
067
068    private void doIntercept(Invocation inv) {
069
070        JbootWechatController controller = (JbootWechatController) inv.getController();
071
072
073        /**
074         * isFromBaseScope 表示 静默授权过来的
075         * 静默授权过来是无法获取用户的基本信息的,只能获得openid,
076         * 这个时候,只能查看openid在数据库里是否已经存在,
077         * 如果存在,则已经标示到了当前用户了
078         * 如果不存在,则通过snsapi_userinfo方式去获取,这样就可以获取到全部资料,保存到数据库
079         */
080        boolean isFromBaseScope = isFromBaseScope(inv);
081
082        if (isFromBaseScope) {
083
084            String openid = inv.getController().getSessionAttr(JbootWechatController.SESSION_WECHAT_OPEN_ID);
085            Object user = controller.doGetUserByOpenId(openid);
086            if (user != null) {
087                controller.setAttr(JbootWechatController.ATTR_USER_OBJECT, user);
088                inv.invoke();
089                return;
090            }
091        }
092
093        String wechatUserJson = controller.getSessionAttr(JbootWechatController.SESSION_WECHAT_USER_JSON);
094        if (validateUserJson(wechatUserJson)) {
095            Object user = controller.doSaveOrUpdateUserByApiResult(ApiResult.create(wechatUserJson));
096            if (user == null) {
097                controller.renderText("can not save or update user when get user from wechat");
098                return;
099            }
100            controller.setAttr(JbootWechatController.ATTR_USER_OBJECT, user);
101            inv.invoke();
102            return;
103        }
104
105
106        //移除脏数据后,再次进入授权页面
107        controller.clearWechatSession();
108
109
110        String appid = ApiConfigKit.getAppId();
111
112        HttpServletRequest request = controller.getRequest();
113        // 获取用户将要去的路径
114        String queryString = request.getQueryString();
115
116        // 被拦截前的请求URL
117        String toUrl = request.getRequestURI();
118        if (StrUtil.isNotBlank(queryString)) {
119            toUrl = toUrl.concat("?").concat(queryString);
120        }
121
122
123        String controllerKey = inv.getControllerPath();
124        String callbackControllerKey = controllerKey + "/wechatCallback";
125
126        if (!JFinal.me().getAllActionKeys().contains(callbackControllerKey)) {
127            callbackControllerKey = controllerKey.substring(0, controllerKey.lastIndexOf("/")) + "/wechatCallback";
128        }
129
130        String redirectUrl = controller.getBaseUrl() + callbackControllerKey + "?goto=" + StrUtil.urlEncode(toUrl);
131
132        redirectUrl = StrUtil.urlEncode(redirectUrl);
133        String authUrl = isFromBaseScope ? AUTHORIZE_URL : BASE_AUTHORIZE_URL;
134        String url = authUrl.replace("{redirecturi}", redirectUrl).replace("{appid}", appid.trim());
135        controller.redirect(url);
136    }
137
138    /**
139     * 验证微信用户的json信息是否正确
140     *
141     * @param wechatUserJson
142     * @return
143     */
144    protected boolean validateUserJson(String wechatUserJson) {
145        return StrUtil.isNotBlank(wechatUserJson)
146                && wechatUserJson.contains("openid")
147                && wechatUserJson.contains("nickname") //包含昵称
148                && wechatUserJson.contains("headimgurl"); //包含头像
149    }
150
151    protected boolean isFromBaseScope(Invocation inv) {
152        String scope = inv.getController().getSessionAttr(JbootWechatController.SESSION_WECHAT_SCOPE);
153        return scope != null && "snsapi_base".equalsIgnoreCase(scope);
154    }
155
156
157}