企业微信授权登录

OAuth2简介

OAuth2的设计背景,在于允许用户在不告知第三方自己的帐号密码情况下,通过授权方式,让第三方服务可以获取自己的资源信息。
详细的协议介绍,开发者可以参考RFC 6749。

下面简单说明OAuth2中最经典的Authorization Code模式,流程如下:

企业微信授权登录_第1张图片

流程图中,包含四个角色。

· ResourceOwner为资源所有者,即为用户

· User-Agent为浏览器

· AuthorizationServer为认证服务器,可以理解为用户资源托管方,比如企业微信服务端

· Client为第三方服务

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

D)与E)的调用为后台调用,不通过浏览器进行

企业微信OAuth2接入流程

企业微信授权登录_第2张图片

使用OAuth2前须知

关于网页授权的可信域名

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

· 假定重定向访问的链接是:http://mail.qq.com:8080/cgi-bin/helloworld:

配置域名 是否正确 原因
mail.qq.com:8080 在这里插入图片描述 配置域名与访问域名完全一致
email.qq.com 在这里插入图片描述 配置域名必须与访问域名完全一致
support.mail.qq.com [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ligGLWEd-1596002531258)(file:///C:\Users\七秒钟~1\AppData\Local\Temp\ksohtml15124\wps5.jpg)] 配置域名必须与访问域名完全一致
*.qq.com 在这里插入图片描述 不支持泛域名设置
mail.qq.com [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jL0LHdNa-1596002531260)(file:///C:\Users\七秒钟~1\AppData\Local\Temp\ksohtml15124\wps7.jpg)] 配置域名必须与访问域名完全一致,包括端口号

· 假定配置的可信域名是 mail.qq.com:

访问链接 是否正确 原因
https://mail.qq.com/cgi-bin/helloworld 在这里插入图片描述 配置域名与访问域名完全一致
http://mail.qq.com/cgi-bin/redirect 在这里插入图片描述 配置域名与访问域名完全一致,与协议头/链接路径无关
https://exmail.qq.com/cgi-bin/helloworld 在这里插入图片描述 配置域名必须与访问域名完全一致

关于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 终端使用此参数判断是否需要带上身份信息

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

示例:

  1. 假定当前

  2. 企业CorpID:wxCorpID

  3. 访问链接:http://api.3dept.com/cgi-bin/query?action=get

  4. 根据URL规范,将上述参数分别进行UrlEncode,得到拼接的OAuth2链接为:

  5. https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxCorpId&redirect_uri=http%3a%2f%2fapi.3dept.com%2fcgi-bin%2fquery%3faction%3dget&response_type=code&scope=snsapi_base&state=#wechat_redirect

  6. 员工点击后,页面将跳转至

  7. http://api.3dept.com/cgi-bin/query?action=get&code=AAAAAAgG333qs9EdaPbCAP1VaOrjuNkiAZHTWgaWsZQ&state=

  8. 企业可根据code参数调用获得员工的userid

注意到,构造OAuth2链接中参数的redirect_uri是经过UrlEncode的

获取access_token

调试工具

获取access_token是调用企业微信API接口的第一步,相当于创建了一个登录凭证,其它的业务API接口,都需要依赖于access_token来鉴权调用者身份。
因此开发者,在使用业务接口前,要明确access_token的颁发来源,使用正确的access_token。

请求方式: GET(HTTPS
请求地址: https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=ID&corpsecret=SECRET
注:此处标注大写的单词ID和SECRET,为需要替换的变量,根据实际获取值更新。其它接口也采用相同的标注,不再说明。

参数说明:

参数 必须 说明
corpid 企业ID,获取方式参考:术语说明-corpid
corpsecret 应用的凭证密钥,获取方式参考:术语说明-secret

权限说明:
每个应用有独立的secret,获取到的access_token只能本应用使用,所以每个应用的access_token应该分开来获取

返回结果:

 {"errcode": 0,"errmsg": "ok","access_token": "accesstoken000001","expires_in": 7200

 }

参数说明:

参数 说明
errcode 出错返回码,为0表示成功,非0表示调用失败
errmsg 返回码提示语
access_token 获取到的凭证,最长为512字节
expires_in 凭证的有效时间(秒)

注意事项:
开发者需要缓存access_token,用于后续接口的调用(注意:不能频繁调用gettoken接口,否则会受到频率拦截)。当access_token失效或过期时,需要重新获取。

access_token的有效期通过返回的expires_in来传达,正常情况下为7200秒(2小时),有效期内重复获取返回相同结果,过期后获取会返回新的access_token。
由于企业微信每个应用的access_token是彼此独立的,所以进行缓存时需要区分应用来进行存储。
access_token至少保留512字节的存储空间。
企业微信可能会出于运营需要,提前使access_token失效,开发者应实现access_token失效时重新获取的逻辑。

获取访问用户身份

调试工具

该接口用于根据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对应应用的可信域名,否则会返回50001错误。
返回结果:
a) 当用户为企业成员时返回示例如下:

 {"errcode": 0,"errmsg": "ok","UserId":"USERID","DeviceId":"DEVICEID"

 }
参数 说明
errcode 返回码
errmsg 对返回码的文本描述内容
UserId 成员UserID。若需要获得用户详情信息,可调用通讯录接口:读取成员
DeviceId 手机设备号(由企业微信在安装时随机生成,删除重装会改变,升级不受影响)

b) 非企业成员授权时返回示例如下:

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

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

出错返回示例:

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

 }

读取成员

调试工具

在通讯录同步助手中此接口可以读取企业通讯录的所有成员信息,而自建应用可以读取该应用设置的可见范围内的成员信息。

**请求方式:**GET(HTTPS
**请求地址:**https://qyapi.weixin.qq.com/cgi-bin/user/get?access_token=ACCESS_TOKEN&userid=USERID

参数说明:

参数 必须 说明
access_token 调用接口凭证
userid 成员UserID。对应管理端的帐号,企业内必须唯一。不区分大小写,长度为1~64个字节

权限说明:

应用须拥有指定成员的查看权限。

返回结果:

 {"errcode": 0,"errmsg": "ok","userid": "zhangsan","name": "李四","department": [1, 2],"order": [1, 2],"position": "后台工程师","mobile": "13800000000","gender": "1","email": "[email protected]","is_leader_in_dept": [1, 0],"avatar": "http://wx.qlogo.cn/mmopen/ajNVdqHZLLA3WJ6DSZUfiakYe37PKnQhBIeOQBO4czqrnZDS79FH5Wm5m4X69TBicnHFlhiafvDwklOpZeXYQQ2icg/0","thumb_avatar": "http://wx.qlogo.cn/mmopen/ajNVdqHZLLA3WJ6DSZUfiakYe37PKnQhBIeOQBO4czqrnZDS79FH5Wm5m4X69TBicnHFlhiafvDwklOpZeXYQQ2icg/100","telephone": "020-123456","alias": "jackzhang","address": "广州市海珠区新港中路","open_userid": "xxxxxx","main_department": 1,"extattr": {"attrs": [{"type": 0,"name": "文本名称","text": {"value": "文本"}},{"type": 1,"name": "网页名称","web": {"url": "http://www.test.com","title": "标题"}}]},"status": 1,"qr_code": "https://open.work.weixin.qq.com/wwopen/userQRCode?vcode=xxx","external_position": "产品经理","external_profile": {"external_corp_name": "企业简称","external_attr": [{"type": 0,"name": "文本名称","text": {"value": "文本"}},{"type": 1,"name": "网页名称","web": {"url": "http://www.test.com","title": "标题"}},{"type": 2,"name": "测试app","miniprogram": {"appid": "wx8bd80126147dFAKE","pagepath": "/index","title": "my miniprogram"}}]}

 }

参数说明:

参数 说明
errcode 返回码
errmsg 对返回码的文本描述内容
userid 成员UserID。对应管理端的帐号,企业内必须唯一。不区分大小写,长度为1~64个字节
name 成员名称,此字段从2019年12月30日起,对新创建第三方应用不再返回,2020年6月30日起,对所有历史第三方应用不再返回,后续第三方仅通讯录应用可获取,第三方页面需要通过通讯录展示组件来展示名字
mobile 手机号码,第三方仅通讯录应用可获取
department 成员所属部门id列表,仅返回该应用有查看权限的部门id
order 部门内的排序值,默认为0。数量必须和department一致,数值越大排序越前面。值范围是[0, 2^32)
position 职务信息;第三方仅通讯录应用可获取
gender 性别。0表示未定义,1表示男性,2表示女性
email 邮箱,第三方仅通讯录应用可获取
is_leader_in_dept 表示在所在的部门内是否为上级。;第三方仅通讯录应用可获取
avatar 头像url。 第三方仅通讯录应用可获取
thumb_avatar 头像缩略图url。第三方仅通讯录应用可获取
telephone 座机。第三方仅通讯录应用可获取
alias 别名;第三方仅通讯录应用可获取
extattr 扩展属性,第三方仅通讯录应用可获取
status 激活状态: 1=已激活,2=已禁用,4=未激活,5=退出企业。 已激活代表已激活企业微信或已关注微工作台(原企业号)。未激活代表既未激活企业微信又未关注微工作台(原企业号)。
qr_code 员工个人二维码,扫描可添加为外部联系人(注意返回的是一个url,可在浏览器上打开该url以展示二维码);第三方仅通讯录应用可获取
external_profile 成员对外属性,字段详情见对外属性;第三方仅通讯录应用可获取
external_position 对外职务,如果设置了该值,则以此作为对外展示的职务,否则以position来展示。第三方仅通讯录应用可获取
address 地址。第三方仅通讯录应用可获取
open_userid 全局唯一。对于同一个服务商,不同应用获取到企业内同一个成员的open_userid是相同的,最多64个字节。仅第三方应用可获取
main_department 主部门

编写WXLoginUtil

//加@Component注解,交给spring工厂管理,并注入RedisTemplate
@Component
public class WXLoginUtil {
    @Autowired
    private RedisTemplate redisTemplate;

}
//开发者需要缓存access_token,用于后续接口的调用(注意:不能频繁调用gettoken接口,否则会受到频率拦截)。当access_token失效或过期时,需要重新获取。
1、根据corpid和corpsecret获取access_token
//获取access_token
    public  String getAccessToken(){
        String corpid = "企业id";
        String corpsecret = "企业secret";
        String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid="+corpid+"&corpsecret="+corpsecret;
        String access_token = (String) redisTemplate.opsForValue().get("access_token");
        if(access_token == null || "".equals(access_token)){
            try{
                HttpClient client = new DefaultHttpClient();
                HttpGet htttpGet = new HttpGet(url);
                //设置请求的报文头部的编码
                htttpGet.setHeader(new BasicHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"));
                //设置期望服务端返回的编码
                htttpGet.setHeader(new BasicHeader("Accept", "application/json;charset=utf-8"));
                HttpResponse response = client.execute(htttpGet);
                String charset = "UTF-8";
                if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                    /**读取服务器返回过来的json字符串数据**/
                    // 使用EntityUtils的toString方法,传递编码,默认编码是ISO-8859-1
                    String result = EntityUtils.toString(response.getEntity(), charset);
                    JSONObject jsonObject = JSONObject.parseObject(result);
                    access_token = jsonObject.getString("access_token");//access_token
                    Integer expires_in = Integer.valueOf(jsonObject.getString("expires_in"));//有效时间
                    redisTemplate.opsForValue().set("access_token",access_token,expires_in, TimeUnit.SECONDS);
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        return access_token;
    }
2、根据access_token以及前台传过来的code获取userid
//获取用户id
    public String getUserId(String code){
        String accessToken = getAccessToken();
        String url = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?access_token="+accessToken+"&code="+code;
        String userId = null;
        try{
            HttpClient client = new DefaultHttpClient();
            HttpGet htttpGet = new HttpGet(url);
            //设置请求的报文头部的编码
            htttpGet.setHeader(new BasicHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"));
            //设置期望服务端返回的编码
            htttpGet.setHeader(new BasicHeader("Accept", "application/json;charset=utf-8"));
            HttpResponse response = client.execute(htttpGet);
            String charset = "UTF-8";
            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                /**读取服务器返回过来的json字符串数据**/
                // 使用EntityUtils的toString方法,传递编码,默认编码是ISO-8859-1
                String result = EntityUtils.toString(response.getEntity(), charset);
                System.out.println("result="+result);
                JSONObject jsonObject = JSONObject.parseObject(result);
                userId = jsonObject.getString("UserId");
                System.out.println("userId="+userId);

            }

        }catch (Exception e){
            e.printStackTrace();
        }
        return userId;
    }
3、根据access_token以及userid获取用户信息
 //根据access_token和userid获取用户信息
    public Object getUserInfo(String accessToken,String userId){
        String url = "https://qyapi.weixin.qq.com/cgi-bin/user/get?access_token="+accessToken+"&userid="+userId;
        JSONObject jsonObject = null;
        try{
            HttpClient client = new DefaultHttpClient();
            HttpGet htttpGet = new HttpGet(url);
            //设置请求的报文头部的编码
            htttpGet.setHeader(new BasicHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"));
            //设置期望服务端返回的编码
            htttpGet.setHeader(new BasicHeader("Accept", "application/json;charset=utf-8"));
            HttpResponse response = client.execute(htttpGet);
            String charset = "UTF-8";
            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                /**读取服务器返回过来的json字符串数据**/
                // 使用EntityUtils的toString方法,传递编码,默认编码是ISO-8859-1
                String result = EntityUtils.toString(response.getEntity(), charset);
                jsonObject = JSONObject.parseObject(result);
                System.out.println(result);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return jsonObject;
    }

授权登录接口

//根据前端传参code,即可获取企业微信当前用户的身份信息
    @GetMapping("/userid")
    public String getUserId(@RequestParam(value = "code") String code){
        String userId = wxLoginUtil.getUserId(code);
        return userId;
    }

你可能感兴趣的:(Java笔记,java)