• 企业微信开发之授权登录


    以前写过一篇公众号的授权登录https://blog.csdn.net/dsn727455218/article/details/65630151,今天给大家分享一下企业微信的授权登录。

    大致都差不多流程

    注意事项:

    1.网页授权及JS-SDK需要在企业微信上配置可信域名

    2.企业微信授权登录里面填写你的可信域名

    调用流程为:
    A) 用户访问第三方服务,第三方服务通过构造OAuth2链接(参数包括当前第三方服务的身份ID,以及重定向URI),将用户引导到认证服务器的授权页
    B) 用户选择是否同意授权
    C) 若用户同意授权,则认证服务器将用户重向到第一步指定的重定向URI,同时附上一个授权码。
    D) 第三方服务收到授权码,带上授权码来源的重定向URI,向认证服务器申请凭证。
    E) 认证服务器检查授权码和重定向URI的有效性,通过后颁发AccessToken(调用凭证)

    企业微信OAuth2接入流程

    使用OAuth2前须知

    关于网页授权的可信域名

    REDIRECT_URL中的域名,需要先配置至应用的“可信域名”,否则跳转时会提示“redirect_uri参数错误”。
    要求配置的可信域名,必须与访问链接的域名完全一致。举个例子:

    • 假定重定向访问的链接是:http://mail.qq.com:8080/cgi-bin/helloworld:
    配置域名是否正确原因
    mail.qq.com:8080 correct 配置域名与访问域名完全一致
    email.qq.com error 配置域名必须与访问域名完全一致
    support.mail.qq.com error 配置域名必须与访问域名完全一致
    *.qq.com error 不支持泛域名设置
    mail.qq.com error 配置域名必须与访问域名完全一致,包括端口号
    • 假定配置的可信域名是 mail.qq.com:
    访问链接是否正确原因
    https://mail.qq.com/cgi-bin/helloworld correct 配置域名与访问域名完全一致
    http://mail.qq.com/cgi-bin/redirect correct 配置域名与访问域名完全一致,与协议头/链接路径无关
    https://exmail.qq.com/cgi-bin/helloworld error 配置域名必须与访问域名完全一致

    关于UserID机制

    UserId用于在一个企业内唯一标识一个用户,通过网页授权接口可以获取到当前用户的UserId信息,如果需要获取用户的更多信息可以调用 通讯录管理 - 成员接口 来获取。

    缓存方案建议

    通过OAuth2.0验证接口获取成员身份会有一定的时间开销。对于频繁获取成员身份的场景,建议采用如下方案:
    1、企业应用中的URL链接直接填写企业自己的页面地址
    2、成员操作跳转到步骤1的企业页面时,企业后台校验是否有标识成员身份的cookie信息,此cookie由企业生成
    3、如果没有匹配的cookie,则重定向到OAuth验证链接,获取成员的身份信息后,由企业后台植入标识成员身份的cookie信息
    4、根据cookie获取成员身份后,再进入相应的页面

    构造网页授权链接
    如果企业需要在打开的网页里面携带用户的身份信息,第一步需要构造如下的链接来获取code参数:

         



    https://open.weixin.qq.com/connect/oauth2/authorize?appid=CORPID&redirect_uri=REDIRECT_URI&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect

    参数说明:

    参数必须说明
    appid 企业的CorpID
    redirect_uri 授权后重定向的回调链接地址,请使用urlencode对链接进行处理
    response_type 返回类型,此时固定为:code
    scope 应用授权作用域。企业自建应用固定填写:snsapi_base
    state 重定向后会带上state参数,企业可以填写a-zA-Z0-9的参数值,长度不可超过128个字节
    #wechat_redirect 终端使用此参数判断是否需要带上身份信息

    agentid 否 企业应用的id。
    当scope是snsapi_userinfo或snsapi_privateinfo时,该参数必填。
    注意redirect_uri的域名必须与该应用的可信域名一致。

    员工点击后,页面将跳转至 redirect_uri?code=CODE&state=STATE,企业可根据code参数获得员工的userid。code长度最大为512字节。

    权限说明:
    企业无限制;第三方使用snsapi_privateinfo的scope时,应用必须有’成员敏感信息授权’的权限。

    根据code获取成员信息
    请求方式:GET(HTTPS)
    请求地址:https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?access_token=ACCESS_TOKEN&code=CODE

    参数说明:

    参数    必须    说明
    access_token    是    调用接口凭证
    code    是    通过成员授权获取到的code,最大为512字节。每次成员授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期。

    权限说明:
    跳转的域名须完全匹配access_token对应应用的可信域名。

    返回结果:
    a) 当用户为企业成员时返回示例如下:

    {
    "errcode":0,
    "errmsg":"ok",
    "UserId":"USERID",
    "DeviceId":"DEVICEID",
    "user_ticket":"USER_TICKET",
    "expires_in":7200
    }

    参数    说明
    errcode    返回码
    errmsg    对返回码的文本描述内容
    UserId    成员UserID
    DeviceId    手机设备号(由企业微信在安装时随机生成,删除重装会改变,升级不受影响)
    user_ticket    成员票据,最大为512字节。
    scope为snsapi_userinfo或snsapi_privateinfo,且用户在应用可见范围之内时返回此参数。
    后续利用该参数可以获取用户信息或敏感信息。
     
    expires_in    user_token的有效时间(秒),随user_ticket一起返回
    非企业成员授权时返回示例如下:

    {
    "errcode":0,
    "errmsg":"ok",
    "OpenId":"OPENID",
    "DeviceId":"DEVICEID"
    }


    参数    说明
    errcode    返回码
    errmsg    对返回码的文本描述内容
    OpenId    非企业成员的标识,对当前企业唯一
    DeviceId    手机设备号(由企业微信在安装时随机生成,删除重装会改变,升级不受影响)
    出错返回示例:

    {
    "errcode":40029,
    "errmsg":"invalid code"
    }


    实现代码:

    package com.eqiao.bidata.weixin.controller;
     
    import com.eqiao.bidata.weixin.common.AccessToken;
    import com.eqiao.bidata.weixin.common.Result;
    import com.eqiao.bidata.weixin.common.WeiXinQiYeConstants;
    import com.eqiao.bidata.weixin.common.WeiXinQiYeUtil;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpSession;
    import java.io.UnsupportedEncodingException;
     
    /**
     * 单纯实现OAuth2验证,不使用注解及拦截器
     * Created by zhaoxinguo on 2017/7/11.
     */
    @Controller
    public class SimpleOAuth2Controller {
     
        private Logger logger = LoggerFactory.getLogger(SimpleOAuth2Controller.class);
     
        /**
         * 拼接网页授权链接
         * 此处步骤也可以用页面链接代替
         * @return
         */
        @RequestMapping(value = { "/oauth2wx.do" })
        public String Oauth2API(HttpServletRequest request){
            //获取项目域名
            String requestUrl = request.getServerName();
            String contextPath = request.getContextPath();
            logger.info("domain name: " + requestUrl + " project name: " + contextPath);
            //拼接微信回调地址
            String backUrl ="http://" + requestUrl + contextPath + "/oauth2me.do";
            String redirect_uri = "";
            try {
                redirect_uri = java.net.URLEncoder.encode(backUrl, "utf-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
                logger.error("ecdoe error: " + e.getMessage());
            }
            String oauth2Url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + WeiXinQiYeConstants.CORPID + "&redirect_uri=" + redirect_uri
                    + "&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect";
            return "redirect:" + oauth2Url;
        }
     
        /**
         * 授权回调请求处理
         * @return
         */
        @RequestMapping(value = { "/oauth2me.do" })
        public String oAuth2Url(HttpServletRequest request, @RequestParam String code){
            // 调用获取access_token的接口
            AccessToken accessToken = WeiXinQiYeUtil.access_token();
            HttpSession session = request.getSession();
            if (accessToken != null && accessToken.getAccess_token() != null) {
                // 调用获取用户信息的接口
                String UserId = getMemberGuidByCode(accessToken.getAccess_token(), code, WeiXinQiYeConstants.AGENTID);
                logger.info("UserId: " + UserId);
                if (UserId != null) {
                    session.setAttribute("UserId", UserId);
                    logger.info("UserId放入session成功!");
                }
            }
            // 这里简单处理,存储到session中
            return "user/result";
        }
     
        /**
         * 调用接口获取用户信息
         *
         * @param token
         * @param code
         * @return
         */
        public String getMemberGuidByCode(String token, String code, String agentId) {
            logger.info("code==" + code + " token=" + token + " agentId=" +agentId);
            Result result = WeiXinQiYeUtil.oAuth2GetUserByCode(token, code, agentId);
            logger.info("result= " + result);
            if (result.getErrcode().equals("0")) {
                if (result.getUserId() != null  && result.getUserId().length() > 0) {
                    // 此处可以通过微信授权用code还钱的Userid查询自己本地服务器中的数据
                    logger.info("result.getUserId(): " + result.getUserId());
                    return result.getUserId();
                }
            }
            return "";
        }
     
    }
     
    /**
         * OAuth2验证接口根据code获取成员信息
         *
         * @param token
         * @param code
         * @return
         */
        public static Result oAuth2GetUserByCode(String token, String code, String agentId) {
            Result result = new Result();
            String menuUrl = WeiXinQiYeConstants.GET_OAUTH2_URL.replace("ACCESS_TOKEN", token).replace("CODE", code).replace("AGENTID", agentId + "");
            String userinfo = JHttpUtils.doGet(menuUrl);
            logger.info("userinfo: " + userinfo);
            JSONObject jsonObject = null;
            if (userinfo != null) {
                try {
                    jsonObject = JSONObject.fromObject(userinfo);
                    logger.info("jsonObject: " + jsonObject);
                    if (jsonObject.getString("UserId") != null && jsonObject.getString("UserId").length() > 0) {
                        result.setErrmsg(jsonObject.getString("errmsg"));
                        result.setErrcode(jsonObject.getString("errcode"));
                        result.setUserId(jsonObject.getString("UserId"));
                    } else {
                        result.setErrmsg(jsonObject.getString("errmsg"));
                        result.setErrcode(jsonObject.getString("errcode"));
                    }
                } catch (Exception e) {
                    result.setErrmsg("accessToken 超时......");
                    result.setErrcode("42001");
                }
            }
            return result;
        }
     
    

    一个完整的流程就是这样。

    如遇到问题欢迎进群308742428

    喜欢的朋友可以关注下,粉丝也缺。

      如果对你有帮助,请打赏一下!!!

  • 相关阅读:
    从B树、B+树、B*树谈到R 树
    1.红黑树和自平衡二叉(查找)树区别 2.红黑树与B树的区别
    红黑树 Java实现
    自平衡二叉(查找树/搜索树/排序树) binary search tree
    从零开始: 二叉查找
    深入理解JAVA虚拟机JVM
    java中的变量各占得字节数
    java中new两个对象,在堆中开辟几个对象空间
    Spring面试,IoC和AOP的理解, @Transactional原理及使用
    EasyUI Tabs + Yii2.0实现iframe方式打开页面(解决共用静态文件引入加载的问题)
  • 原文地址:https://www.cnblogs.com/dsn727455218/p/10856677.html
Copyright © 2020-2023  润新知