【笔记】一些开发的流程

目录

微信绑定登录

抖音小程序开发

顶象无感验证

企业微信代开发

微信绑定登录

1.实现微信绑定有两种方式

  • 微信服务号(需用户关注服务号)
  • 微信开放平台- OAuth2.0(以下采用这种方式)

2.微信开放平台OAuth2.0授权登录材料

  • 开发者账号(通过资质认证)
  • 账号下有一个已审核通过的网站应用
  • 申请微信登录功能且通过审核

3.用户使用微信登录流程:

  • 请求微信OAuth2.0授权登录:
    打开以下连接(将微信登录按钮设置成跳转到以下网址,然后后端返回微信生成的二维码)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    https://open.weixin.qq.com/connect/qrconnect?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect

    # 参数说明:
    # appid:应用唯一标识(前面认证网页应用中获得)
    # redirect_uri:重定向地址,需要进行UrlEncode(前面认证网页应用中获得)
    # response_type:填code
    # scope:应用授权作用域,拥有多个作用域用逗号(,)分隔,网页应用目前仅填写snsapi_login即可
    # 返回:
    # 二维码
  • 用户扫码授权后跳转到重定向地址并附参数code
  • 重定向的地址如下,获得code后去申请获得openid
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

    # 参数说明:
    # appid:应用唯一标识,在微信开放平台提交应用审核通过后获得
    # secret:应用密钥AppSecret,在微信开放平台提交应用审核通过后获得
    # code:填写第一步获取的code参数
    # grant_type:填authorization_code
    # 返回:
    # {
    # "access_token":"ACCESS_TOKEN",
    # "expires_in":7200,
    # "refresh_token":"REFRESH_TOKEN",
    # "openid":"OPENID",
    # "scope":"SCOPE",
    # "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
    # }
  • 使用获得的open_id在数据库中找用户资料
    • 若找到则以该用户身份生成token登录
    • 若找不到则提示未绑定

4.整体实现有两种方案:

  • 方案一:
    • 前端:显示二维码(这里可以内嵌在网页里)
    • 后端:用重定向跳转的路由接受code,用code去申请获得openid,然后与数据库中对照
  • 方案二:
    • 后端负责所有工作:显示二维码(这里只能显示一个页面来单独展示二维码),用重定向跳转的路由接受code,用code去申请获得openid,然后与数据库中对照

抖音小程序开发

1.小程序项目中单个页面会依赖不同类型的文件

  • .json 后缀的 JSON 配置文件
  • .ttml 后缀的 TTML 模板文件
  • .ttss 后缀的 TTSS 样式文件
  • .js 后缀的 JS 脚本文件

2.小程序的目录结构

1
2
3
4
5
6
7
8
9
10
11
.
├── app.js //小程序逻辑
├── app.json //小程序公共配置
├── app.ttss //小程序公共样式表
├── project.config.json //项目配置
└── pages
└── pageA
├── pageA.ttss
├── pageA.js
├── pageA.json
└── pageA.ttml

3.一个简体流程就是

  • 根目录
    • app.js和app.ttss都可以空着不写
    • app.json里改一下navigationBarTitleText用来显示标题栏名称
    • project.config.json里面填一下appid和projectname就行
  • 在pages文件夹里写
    • index.ttml文件里写网页结构,语法和html相似,但关键字不一样
    • index.js文件,用来写函数
    • index.ttss文件用来写index网页的样式

4.个位计算器的demo

  • 实现功能:两个个位数字的+-*/运算,有两个按钮计算、随机,点击随机会自动生成不同数字和运算符,点击计算会根据当前运算式显示结果
  • index.ttml
    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
    <view class="intro">Welcome to Sc-test</view>
    <view class="content">
    <!--显示块-->
    <view class="content_up_1">
    <picker class="calculation" value="{{index_1}}" range="{{array}}" data-value="1" bindchange="bindPickerChange" bindcancel="bindPickerCancel">
    <view class="picker">
    {{array[index_1]}}
    </view>
    </picker>
    <picker class="calculation" value="{{cal_num}}" range="{{cal_array}}" data-value="1" bindchange="bindPickerChange" bindcancel="bindPickerCancel">
    <view class="picker">
    {{cal_array[cal_num]}}
    </view>
    </picker>
    <picker class="calculation" value="{{index_2}}" range="{{array}}" data-value="1" bindchange="bindPickerChange" bindcancel="bindPickerCancel">
    <view class="picker">
    {{array[index_2]}}
    </view>
    </picker>
    </view>
    <!--
    <view>
    <button type="primary" bindtap="func1">页面主操作 Normal</button>
    <button type="primary" loading="true">页面主操作 Loading</button>
    <button type="primary" disabled="true">页面主操作 Disabled</button>
    </view>
    -->
    <view class="content_up_1">
    <view class="calculation_submit" bindtap="bindSubmit">计算</view>
    <view class="calculation_random" bindtap="bindRandom">随机</view>
    <!-- <view class="calculation_clear" bindtap="bindClear">清空</view> -->
    </view>

    <view class="content_down">
    <view class="text">{{result}}</view>
    </view>
  • index.js
    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
    const app = getApp()
    var that

    Page({
    data: {
    array: [
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10
    ],
    cal_array: [
    '+', '-', '*', '/',
    ],
    index_1: 2,
    index_2: 0,
    cal_num: 0,
    result: '',
    },
    onLoad: function () {
    console.log('Welcome to Mini Code')
    },

    bindSubmit: function(e){
    var temple
    if(this.data.cal_array[this.data.cal_num] == '+') temple = this.data.array[this.data.index_1] + this.data.array[this.data.index_2]
    if(this.data.cal_array[this.data.cal_num] == '-') temple = this.data.array[this.data.index_1] - this.data.array[this.data.index_2]
    if(this.data.cal_array[this.data.cal_num] == '*') temple = this.data.array[this.data.index_1] * this.data.array[this.data.index_2]
    if(this.data.cal_array[this.data.cal_num] == '/') temple = this.data.array[this.data.index_1] / this.data.array[this.data.index_2]
    this.setData({
    result: temple,
    })
    },

    bindRandom: function (e) {
    this.setData({
    index_1: Math.floor(Math.random() * 10),//下标,0到9的随机整数
    index_2: Math.floor(Math.random() * 10),//下标,0到9的随机整数
    cal_num: Math.floor(Math.random() * 4),//下标,0到2的随机整数
    result: '',
    })
    },

    })
  • index.ttss
    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
    .intro {
    margin: 30px;
    text-align: center;
    }

    .my-button {
    border-radius: 20px;
    }
    .my-button:after {
    border-color: #f00;
    border-radius: 40px; /* 需要设置为按钮圆角的两倍 */
    }

    .my-button-2 {
    border: 1px solid;
    }
    .my-button-2:after {
    display: none;
    }

    .content {
    display: flex;
    flex-direction: column; /*orientation 排版 column:垂直 row:水平*/
    }

    .content_up{
    width: max-content;
    height: 300rpx;
    }

    .content_up_1{
    margin-top: 20rpx;
    width: max-content;
    display: flex;
    flex-direction: row; /*orientation 排版 column:垂直 row:水平*/
    align-items: center;
    justify-content: center;
    text-align: center;
    }

    .picker{
    height: 80rpx;
    color:#585858;
    text-align: center;
    justify-content: center;
    align-items: center;
    display: flex;
    }

    .calculation_random{
    width: 155rpx;
    height: 80rpx;
    border-radius:8rpx;
    margin-left: 22rpx;
    background: rgba(83,217,105,1);
    color:#ffffff;
    text-align: center;
    justify-content: center;
    align-items: center;
    display: flex;
    }

    .calculation_submit{
    width: 155rpx;
    height: 80rpx;
    border-radius:8rpx;
    margin-left: 22rpx;
    background: rgb(3, 78, 16);
    color:#ffffff;
    text-align: center;
    justify-content: center;
    align-items: center;
    display: flex;
    }

    .calculation{
    width: 158rpx;
    height: 80rpx;
    border-radius:8rpx;
    margin-left: 22rpx;
    border:1rpx solid rgba(209,209,209,1);
    background: #F9F9F9;
    }

    .text{
    height: 80rpx;
    color:#585858;
    text-align: center;
    justify-content: flex-start;
    align-items: center ;
    margin-left: 20rpx;
    display: flex;
    }

5.抖音小程序与后端交互

  • 前端js部分(写在某个函数里面的)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    const task = tt.request({
    url: '127.0.0.1:8080/index/bar', // 目标服务器url
    dataType: 'string',
    success: (res) => {
    this.setData({
    result: res.data,
    })
    },
    fail: (res) => {
    console.log('get失败')
    this.setData({
    err_inf: res.errMsg,
    })
    },
    });
  • 后端部分
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    func main() {
    router := gin.Default()
    router.GET("/index/bar", response)
    router.Run()
    }
    func response(c *gin.Context) {
    str := "123456789"
    c.String(200, str)
    fmt.Println(str)
    }

顶象无感验证

1.使用无感验证之前需要在顶象控制台新建一个实例

  • 输入:自己页面运行的域名
  • 输出:生成独有的appid、appsecret和接入域名(需要在后端和前端手动配置)

2.两种模式,在顶象自己的控制台上切换

  • 用户通过人机识别后,开启无感验证,利用地理位置等信息记忆该用户,后续不需要再进行人机识别,用户每次滑动人机验证成功后就会得到一个token
  • 不使用无感验证,每次使用都需要进行人机识别

3.前端三种表现形式

  • 嵌入式,直接在网页上展示完全验证界面
    1
    2
    3
    4
    5
    6
    7
    8
    var demo_1 = _dx.Captcha(document.getElementById('demo-embed'), {
    appId: appId,
    style: 'embed',
    width: 300,
    success: function(token) {
    window.console && console.log('success, token:', token)
    }
    })
  • 向上浮现的内联式,一个滚动条,用户拉取时才在其上方浮现验证界面
    1
    2
    3
    4
    5
    var demo_2 = _dx.Captcha(document.getElementById('demo-inline-up'), {
    appId: appId,
    style: 'inline',
    language: 'en' // 表示滚动条内的语言为英语
    })
  • 向下浮现的内联式
    1
    2
    3
    4
    5
    var demo_3 = _dx.Captcha(document.getElementById('demo-inline-down'), {
    appId: appId,
    style: 'inline',
    inlineFloatPosition: 'down'
    })
  • 弹出式,一个按钮,点击后弹出验证界面
    1
    2
    3
    4
    5
    6
    7
    8
    var demo_4 = _dx.Captcha(document.getElementById('demo-popup'), {
    appId: appId,
    style: 'popup'
    })

    document.getElementById('btn-popup').onclick = function() {
    demo_4.show()
    }

4.Go写的后端(gin框架)

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
package main

import (
"Captcha_Project/captcha-client"
"fmt"
"github.com/gin-gonic/gin"
)

func main(){
router := gin.Default()
router.LoadHTMLGlob("tmp/*")
router.GET("/index", show_index)
router.GET("/index/done", show_done)
router.GET("/index/bar", response)
router.Run()
}

func show_index(c *gin.Context){
fmt.Println("show_index")
c.HTML(200, "index.html", gin.H{"title": "测试"})
}

func show_done(c *gin.Context){
fmt.Println("show_done")
c.HTML(200, "done.html", gin.H{"title": "done"})
}

func response(c *gin.Context){
fmt.Println("response")
token := c.Query("token")
fmt.Println(token)
Run_verify(token)
}

func Run_verify(token string) {
/*
参数token在前端完成验证后可以获取到,随业务请求发送到后台,有效期为两分钟
参数ip(string)可选,提交业务参数的客户端ip
*/
fmt.Println("Run_verify")
appId := "fb6d598f6f2911728e76ba2e60ca9ae0" //和前端验证码的appId保持一致,在顶象的应用配置里有
appSecret := "cfc89571ec179ef809fe7f29bf1c7be3" //appSecret为秘钥,在顶象的应用配置里有
captchaClient := captcha_client.NewCaptchaClient(appId, appSecret) //构建一个用户对象
captchaClient.SetTimeout(2000) //设置超时时间,单位毫秒
captchaClient.SetCaptchaUrl("https://xx.dingxiang-inc.com/api/tokenVerify") //指定服务器地址,saas可在控制台,应用管理页面最上方获取
captchaResponse := captchaClient.VerifyToken(token) //token在前端完成验证后可以获取到,随业务请求发送到后台,token有效期为两分钟
//captchaResponse := captchaClient.VerifyTokenAndIP(token, ip)
//针对一些token冒用的情况,除了判断token的合法性还会校验提交业务参数的客户端ip和验证码颁发token的客户端ip是否一致
//fmt.Println(captchaResponse.CaptchaStatus) //确保验证状态是SERVER_SUCCESS,SDK中有容错机制,在网络出现异常的情况会返回通过
//fmt.Println(captchaResponse.Ip) //验证码服务采集到的客户端ip
if captchaResponse.Result { //为真表示验证通过
//此处为验证通过下一步的代码
fmt.Println("done!")
} else { //为假表示验证失败
//此处为阻断该次请求或者进行下一次验证的代码
fmt.Println("fail!")
}
}

5.流程

  • 前端进行人机验证:
    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
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width">
    <meta http-equiv="X-UA-Compatible" content="IE=8">
    <link rel="shortcut icon" href="https://dingxiang-inc.com/favicon.ico">
    <title>{{.title}}</title>
    <script src="https://cdn.dingxiang-inc.com/fe/common/jquery/1.9.1/jquery.min.js"></script>
    <script src="https://cdn.dingxiang-inc.com/fe/common/beautify/1.6.14/beautify.js"></script>
    </head>
    <body>
    <script src="https://cdn.dingxiang-inc.com/ctu-group/captcha-ui/index.js"></script>

    <div id="c1"></div>

    <div class="ln">
    <h2>人机验证</h2>

    <div class="block">
    <div class="eg">
    <div id="demo-embed"></div>
    </div>
    <script>
    var demo_1 = _dx.Captcha(document.getElementById('demo-embed'), {
    appId: 'fb6d598f6f2911728e76ba2e60ca9ae0',
    apiServer: 'cap.dingxiang-inc.com',
    style: 'embed',
    width: 300,
    success: function (token) {
    $.ajax({
    type:"GET",
    url:"http://127.0.0.1:8080/index/bar",
    dataType:"text",
    data: { token: token}
    });
    //window.console && console.log('success, token:', token)
    window.location.href="http://127.0.0.1:8080/index/done"
    }
    })
    </script>
    </div>
    </div>

    <script>
    function getCfg (key) {
    try {
    return localStorage.getItem(key)
    } catch (e) {
    return undefined
    }
    }

    function setCfg (key, val) {
    try {
    localStorage.setItem(key, val)
    } catch (e) {
    }
    }

    var WG_KEY = 'dx-captcha-demo-wg'
    var g_wg = getCfg(WG_KEY) !== '0'

    function updateStateInfo (v) {
    if (!v) {
    _dx.Captcha._clearVID()
    }
    }

    $(document).ready(function () {
    $('pre').each(function () {
    var pre = $(this)
    var s = pre.parent().find('script').html()
    pre.html(js_beautify(s, {indent_size: 2}))
    })

    if (g_wg) {
    $("#switchOn").prop('checked', true)
    } else {
    $("#switchOff").prop('checked', true)
    }


    updateStateInfo(g_wg)
    })

    function updateCheckStatus(obj) {
    var val = !!parseInt(obj.value)
    g_wg = val
    setCfg(WG_KEY, val ? 1 : 0)
    updateStateInfo(val)
    }



    function resetCaptcha (idx) {
    // _dx.Captcha._clearVID()
    if (!g_wg) {
    _dx.Captcha.getByIdx(idx).reload()
    }
    }

    (function () {
    var i
    for (i = 1; i <= 4; i++) {
    (function (i2) {
    var cpt = _dx.Captcha.getByIdx(i2)
    if (!cpt) {
    setTimeout(arguments.callee, 100, i2)
    return
    }
    cpt.on('verifySuccess', function () {
    setTimeout(function () {resetCaptcha(i2)}, 3000)
    if (!g_wg) {
    _dx.Captcha._clearVID()
    }
    })
    })(i)
    }
    })()
    </script>

    <script src="https://cdn.dingxiang-inc.com/ctu-group/captcha-ui/index.js"></script>
    </body>
    </html>
  • 后端根据token和客户端ip调顶象写好的api进行验证
    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
    package main

    import (
    "Captcha_Project/captcha-client"
    "fmt"
    "github.com/gin-gonic/gin"
    )

    func main(){
    router := gin.Default()
    router.LoadHTMLGlob("tmp/*")
    router.GET("/index", show_index)
    router.GET("/index/done", show_done)
    router.GET("/index/bar", response)
    router.Run()
    }

    func show_index(c *gin.Context){
    fmt.Println("show_index")
    c.HTML(200, "index.html", gin.H{"title": "测试"})
    }

    func show_done(c *gin.Context){
    fmt.Println("show_done")
    c.HTML(200, "done.html", gin.H{"title": "done"})
    }

    func response(c *gin.Context){
    fmt.Println("response")
    token := c.Query("token")
    fmt.Println(token)
    Run_verify(token)
    }

    func Run_verify(token string) {
    /*
    参数token在前端完成验证后可以获取到,随业务请求发送到后台,有效期为两分钟
    参数ip(string)可选,提交业务参数的客户端ip
    */
    fmt.Println("Run_verify")
    appId := "fb6d598f6f2911728e76ba2e60ca9ae0" //和前端验证码的appId保持一致,在顶象的应用配置里有
    appSecret := "cfc89571ec179ef809fe7f29bf1c7be3" //appSecret为秘钥,在顶象的应用配置里有
    captchaClient := captcha_client.NewCaptchaClient(appId, appSecret) //构建一个用户对象
    captchaClient.SetTimeout(2000) //设置超时时间,单位毫秒
    captchaClient.SetCaptchaUrl("https://xx.dingxiang-inc.com/api/tokenVerify") //指定服务器地址,saas可在控制台,应用管理页面最上方获取
    captchaResponse := captchaClient.VerifyToken(token) //token在前端完成验证后可以获取到,随业务请求发送到后台,token有效期为两分钟
    //captchaResponse := captchaClient.VerifyTokenAndIP(token, ip)
    //针对一些token冒用的情况,除了判断token的合法性还会校验提交业务参数的客户端ip和验证码颁发token的客户端ip是否一致
    //fmt.Println(captchaResponse.CaptchaStatus) //确保验证状态是SERVER_SUCCESS,SDK中有容错机制,在网络出现异常的情况会返回通过
    //fmt.Println(captchaResponse.Ip) //验证码服务采集到的客户端ip
    if captchaResponse.Result { //为真表示验证通过
    //此处为验证通过下一步的代码
    fmt.Println("done!")
    } else { //为假表示验证失败
    //此处为阻断该次请求或者进行下一次验证的代码
    fmt.Println("fail!")
    }
    }
  • token验证成功跳转到下一页面(一般是登录成功页面)

企业微信代开发

1.自建应用的流程:

  • 获取access_token:
    1
    (get)https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=ID&corpsecret=SECRET
  • 企业微信向接受者发送信息:
    1
    (post)https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=ACCESS_TOKEN

2.参数介绍

  • corpid:每个企业都拥有唯一的corpid,获取此信息可在管理后台“我的企业”-“企业信息”下查看“企业ID”(需要有管理员权限)
  • userid:每个成员都有唯一的userid,即所谓“帐号”。在管理后台->“通讯录”->点进某个成员的详情页,可以看到。
  • external_userid:企业外部联系人的id,可能是微信用户,也可能是企业微信用户。需要开发者(尤其是第三方开发者)注意的是,对于同一个外部联系人,不同调用方(企业/第三方服务商)获取到的external_userid是不同的。
  • agentid:每个应用都有唯一的agentid。在管理后台->“应用与小程序”->“应用”,点进某个应用,即可看到agentid。
  • secret:secret是企业应用里面用于保障数据安全的“钥匙”,每一个应用都有一个独立的访问密钥,为了保证数据的安全,secret务必不能泄漏。

3.用户登录授权

  • 自建应用
    1
    2
    3
    4
    5
    6
    7
    8
    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是终端使用此参数判断是否需要带上身份信息
  • 代开发应用
    1
    2
    3
    4
    5
    6
    7
    8
    9
    https://open.weixin.qq.com/connect/oauth2/authorize?appid=CORPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&agentid=AGENTID&state=STATE#wechat_redirect

    # appid是第三方应用id(即ww或wx开头的suite_id)。注意与企业的网页授权登录不同
    # redirect_uri是授权后重定向的回调链接地址,请使用urlencode对链接进行处理 ,注意域名需要设置为第三方应用的可信域名
    # response_type是返回类型,此时固定为:code
    # scope是应用授权作用域。snsapi_base:静默授权,可获取成员的基础信息(UserId与DeviceId);snsapi_userinfo:静默授权,可获取成员的详细信息,但不包含头像、二维码等敏感信息;snsapi_privateinfo:手动授权,可获取成员的详细信息,包含头像、二维码等敏感信息。
    # agentid是企业应用的id。当scope是snsapi_userinfo或snsapi_privateinfo时,该参数必填注意redirect_uri的域名必须与该应用的可信域名一致。
    # state是重定向后会带上state参数,企业可以填写a-zA-Z0-9的参数值,长度不可超过128个字节
    # #wechat_redirect是固定内容

4.流程

  • 企业用户扫码授权,会向这个地址发起一个Post请求
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    https://127.0.0.1/suite/receive?msg_signature=3a7b08bb8e6dbce3c9671d6fdb69d15066227608&timestamp=1403610513&nonce=380320359

    # 包含的信息体是
    <xml>
    <SuiteId><![CDATA[ww4asffe9xxx4c0f4c]]></SuiteId>
    <AuthCode><![CDATA[AUTHCODE]]></AuthCode>
    <InfoType><![CDATA[create_auth]]></InfoType>
    <TimeStamp>1403610513</TimeStamp>
    <State><![CDATA[123]]></State>
    </xml>
  • 用得到的AuthCode去向以下网址发起Post请求
    1
    2
    3
    4
    5
    https://qyapi.weixin.qq.com/cgi-bin/service/get_permanent_code?suite_access_token=SUITE_ACCESS_TOKEN

    {
    "auth_code": "xx"
    }
    • 会返回以下信息
      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
      # permanent_code就是自建应用用的secret

      {
      "errcode":0,
      "errmsg":"ok",
      "access_token": "xxxxxx",
      "expires_in": 7200,
      "permanent_code": "xxxx",
      "dealer_corp_info":
      {
      "corpid": "xxxx",
      "corp_name": "name"
      },
      "auth_corp_info":
      {
      "corpid": "xxxx",
      "corp_name": "name",
      "corp_type": "verified",
      "corp_square_logo_url": "yyyyy",
      "corp_user_max": 50,
      "corp_full_name":"full_name",
      "verified_end_time":1431775834,
      "subject_type": 1,
      "corp_wxqrcode": "zzzzz",
      "corp_scale": "1-50人",
      "corp_industry": "IT服务",
      "corp_sub_industry": "计算机软件/硬件/信息服务"
      },
      "auth_info":
      {
      "agent" :
      [
      {
      "agentid":1,
      "name":"NAME",
      "round_logo_url":"xxxxxx",
      "square_logo_url":"yyyyyy",
      "appid":1,
      "auth_mode":1,
      "is_customized_app":false,
      "privilege":
      {
      "level":1,
      "allow_party":[1,2,3],
      "allow_user":["zhansan","lisi"],
      "allow_tag":[1,2,3],
      "extra_party":[4,5,6],
      "extra_user":["wangwu"],
      "extra_tag":[4,5,6]
      },
      "shared_from":
      {
      "corpid":"wwyyyyy",
      "share_type": 1
      }
      },
      {
      "agentid":2,
      "name":"NAME2",
      "round_logo_url":"xxxxxx",
      "square_logo_url":"yyyyyy",
      "appid":5,
      "shared_from":
      {
      "corpid":"wwyyyyy",
      "share_type": 0
      }
      }
      ]
      },
      "auth_user_info":
      {
      "userid":"aa",
      "open_userid":"xxxxxx",
      "name":"xxx",
      "avatar":"http://xxx"
      },
      "register_code_info":
      {
      "register_code":"1111",
      "template_id":"tpl111",
      "state":"state001"
      },
      "state":"state001"
      }