拦截器实现微信公众号授权

/ springboot / 2 条评论 / 1887浏览

微信公众号授权

微信公众号授权流程:
1.用户进入页面 》 页面重定向到微信授权 》 用户授权 》 重定向回指定地址 》 拿到CODE
2.根据CODE获取OPENID及ACCESS_TOKEN(授权用的token)
3.根据OPENID及ACCESS_TOKEN获取用户基本信息
4.可选:刷新access_token、检验授权凭证(access_token)是否有效

使用拦截器实现微信授权

配置微信授权拦截器

@Configuration
public class WebConfigurer implements WebMvcConfigurer {

    @Autowired
    private WxAuthInterceptor wxAuthInterceptor;

    /**
     * 配置微信授权拦截器
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // addPathPatterns("/**") 表示拦截所有的请求,
        // excludePathPatterns("/login", "/register") 表示除了登陆与注册之外,因为登陆注册不需要登陆也可以访问
        registry.addInterceptor(wxAuthInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns("/error/*", "/msg/*", "/user/*");
    }
}

实现微信拦截器

/**
 * 微信授权获取用户信息拦截器 <br>
 * 第一步:获取code <br>
 * 第二步:根据code获取用户的openid和access_token <br>
 * 第三步:根据openid和access_token获取用户的基本信息 <br>
 * 附:刷新access_token、检验授权凭证(access_token)是否有效
 * 增加:后续可以使用redis替代session,前端使用token
 *      1.进入拦截器时先判断token中有没有值,没有就请求微信授权接口
 *      2.获取到用户信息后生成token,以key(token): value(userInfo)的方式存入redis 并设置时效
 *      3.将token写入Cookie中 并设置时效
 *      4.后面的请求只需要校验Cookie中有没有token参数即可(同时去redis中能不能获取到数据)
 * @author langao_q
 * @since 2020-07-29 16:53
 */
@Slf4j
@Component
public class WxAuthInterceptor implements HandlerInterceptor {

    @Autowired
    private WxConfig wxConfig;

    @Autowired
    private UserService userService;

    /**
     * 授权后获取用户基本信息
     */
    final static String USER_IFNO = "userInfo";

    /**
     * token名
     */
    final static String COOKI_NAME_TOKEN = "token";

    /**
     * 获取用户数据类型
     */
    final static String SNSSCOPE_USERINFO = "snsapi_userinfo";

    /**
     * 微信oath2授权地址;通过该地址拿到用户授权code码
     */
    final static String OAUTH_URL = "https://open.weixin.qq.com/connect/oauth2/authorize";

    /**
     * 根据拿到的code获取openId以及access_token
     */
    final static String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/access_token";

    /**
     * 授权后获取用户基本信息
     */
    final static String USERINFO_URL = "https://api.weixin.qq.com/sns/userinfo";


    @Override
    public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) throws IOException {
        //code只能使用一次,5分钟未被使用自动过期
        String code = req.getParameter("code");
        String uri = req.getRequestURI();

        try {
            if (req.getSession().getAttribute(USER_IFNO) != null) {
                //如果session中已经有用户信息 说明已经授权过了直接放行
                return true;
            } else if (!uri.equals(req.getContextPath() + "/callback")) {
                //如果请求uri不相等 说明未拿到code 需要去授权
                String rurl = "http://" + req.getServerName() + req.getContextPath() + req.getServletPath();
                if (req.getQueryString() != null) {
                    rurl += "?" + req.getQueryString();
                }
                String callbackUrl = getRedirectUrl(req, rurl);
                String _url = OAUTH_URL + "?appid=" + wxConfig.getAppId() + "&redirect_uri=" + URLEncoder.encode(callbackUrl, "UTF-8") + "&response_type=code&scope=" + SNSSCOPE_USERINFO + "&state=STATE#wechat_redirect";
                res.sendRedirect(_url);
                return false;
            } else {
                //已经授权过 可以拿到code去获取openid和用户数据
                String rUrl = req.getParameter("rUrl");
                rUrl = StrUtil.isBlank("rUrl") ? "/index" : rUrl;
                log.info("-----第一步:获取code-----" + code);

                String redirectUrl = getRedirectUrl(req, rUrl);
                Map<String, Object> mapOpenid = new HashMap<String, Object>();
                mapOpenid.put("code", code);
                mapOpenid.put("appid", wxConfig.getAppId());
                mapOpenid.put("secret", wxConfig.getAppSecret());
                mapOpenid.put("redirect_uri", rUrl);
                mapOpenid.put("view", "web");
                mapOpenid.put("grant_type", "authorization_code");
                String resultOpenid = HttpUtil.post(ACCESS_TOKEN_URL, mapOpenid);

                JSONObject objOpenid = JSONUtil.parseObj(resultOpenid);
                 /*{
                      "access_token":"ACCESS_TOKEN",
                      "expires_in":7200,
                      "refresh_token":"REFRESH_TOKEN",
                      "openid":"OPENID",
                      "scope":"SCOPE"
                    }*/
                log.info("-----第二步:获取openid-----" + objOpenid);
                Map<String, Object> userMap = new HashMap<>();
                userMap.put("access_token", objOpenid.getStr("access_token"));
                userMap.put("openid", objOpenid.getStr("openid"));
                userMap.put("lang", "zh_CN");
                String resultUser = HttpUtil.get(USERINFO_URL, userMap);

                JSONObject objUser = JSONUtil.parseObj(resultUser);
                /*{
                  "openid":" OPENID",
                  "nickname": NICKNAME,
                  "sex":"1",
                  "province":"PROVINCE",
                  "city":"CITY",
                  "country":"COUNTRY",
                  "headimgurl":       "http://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/46",
                  "privilege":[ "PRIVILEGE1" "PRIVILEGE2"     ],
                  "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
                }*/
                log.info("-----第三步:保存/更新用户信息-----" + objUser);
                //添加session
                req.getSession().setAttribute(USER_IFNO, objUser);
                //写cookie
                addCookie(res, COOKI_NAME_TOKEN, objUser.getStr("openid"));
                //保存/更新用户信息后,重定向到业务地址
                res.sendRedirect(rUrl);
                return false;
            }
        } catch (Exception e) {
            log.error("-----授权错误-----"+  e);
            req.getSession().setAttribute("errorMgs", e);
            res.sendRedirect(req.getContextPath() + "/error");
            return false;
        }
    }

    /**
     * 获取项目重定向地址
     *
     * @param req  request对象
     * @param rUrl 当前请求的url(需要保存下来)
     * @return 跳转的url
     */
    protected static String getRedirectUrl(HttpServletRequest req, String rUrl) throws UnsupportedEncodingException {
        String mainUrl = "http://" + req.getServerName() + req.getContextPath();
        return mainUrl + "/callback" + "?rUrl=" + (StrUtil.isNotBlank(rUrl) ? URLEncoder.encode(rUrl, "UTF-8") : "");
    }

    /**
     * 把token写到Cookie中
     * @param response
     * @param name
     * @param value
     */
    private void addCookie(HttpServletResponse response, String name, String value){
        Cookie cookie = new Cookie(name, value);
        cookie.setMaxAge(3600*24 * 7);
        cookie.setPath("/");
        response.addCookie(cookie);
    }

    /**
     * 获取cookie中的token
     * @param request
     * @return
     */
    private String getTokenCookie(HttpServletRequest request) {
        Cookie[] cookies = request.getCookies();
        if(cookies != null){
            for (Cookie cookie : cookies){
                if(cookie.getName().equals(COOKI_NAME_TOKEN)){
                    return cookie.getValue();
                }
            }
        }
        return null;
    }
}