【www.shanpow.com--科学/班会/信息】
(1) [用户信息页]3用户信息展示
上一篇完成了AdminLTE的整合,接下来就要把页面中的逻辑一一填充进来了,先从展示用户信息开始吧。
我们需要用户点击账户信息按钮后被导航到账户信息页。所以需要给账户信息按钮添加router-link,点击时调用router进行页面跳转。
第一步:在账户信息的HTML代码处添加事件
<router-link to="/userProfile/travelCount">
<button href="#" class="btn btn-primary btn-flat ch">账户信息</button>
</router-link>
1
2
3
1
2
3
第二步:新建一个userProfile.vue和一个travelCount.vue。userProfile用来展示用户的基本信息,travelCount用来展示用户的出差记录。travelCount是被嵌套在userProfile中的。
userProfile.vue:
<template>
<!-- Main content -->
<section class="content">
<div class="row">
<div class="col-md-3">
<!-- Profile Image -->
<div class="box box-primary">
<div class="box-body box-profile">
<img class="profile-user-img img-responsive img-circle" src="../assets/img/avatar5.png" alt="User profile picture">
<h3 class="profile-username text-center ch">{{displayName}}</h3>
<p class="text-muted text-center ch">{{duty}}</p>
<ul class="list-group list-group-unbordered">
<li class="list-group-item">
<b class="ch">用户名:</b> <b class="pull-right ch">{{displayName}}</b>
</li>
<li class="list-group-item">
<b class="ch">登录名:</b> <b class="pull-right ch">{{name}}</b>
</li>
<li class="list-group-item">
<b class="ch">邮箱地址:</b> <b class="pull-right ch">{{email}}</b>
</li>
<li class="list-group-item">
<b class="ch">所属部门:</b> <b class="pull-right ch">{{department}}</b>
</li>
<li class="list-group-item">
<b class="ch">职务:</b> <b class="pull-right ch">{{duty}}</b>
</li>
<li class="list-group-item">
<b class="ch">办公地点:</b> <b class="pull-right ch">{{location}}</b>
</li>
<li class="list-group-item">
<b class="ch">办公电话:</b> <b class="pull-right ch">{{tel}}</b>
</li>
<li class="list-group-item">
<b class="ch">手机:</b> <b class="pull-right ch">{{phone}}</b>
</li>
<li class="list-group-item">
<b class="ch">上级领导:</b> <b class="pull-right ch">{{superior}}</b>
</li>
</ul>
<strong class="ch"><i class="fa fa-pencil margin-r-5"></i>技能标签</strong>
<p style="padding-top:5px">
<button v-for="skill in skills" class="label btn-primary ch" style="margin:2px; color:white">{{skill}}</button>
</p>
</div>
<!-- /.box-body -->
</div>
<!-- /.box -->
</div>
<!-- /.col -->
<div class="col-md-9">
<div class="nav-tabs-custom">
<ul class="nav nav-tabs">
<li class="ch active" data-toggle="tab">
<router-link to="/userProfile/travelCount">
<span>出差统计</span>
</router-link>
</li>
<li class="ch" data-toggle="tab">
<router-link to="/userProfile/workCircle">
<span>我的工作圈</span>
</router-link>
</li>
</ul>
<div class="tab-content">
<router-view></router-view><!--这里是要显示travelCount内容的地方-->
</div>
<!-- /.content -->
</div>
</div>
</div>
</section>
</template>
<script>
export default {
data() {//这里对应用户的基本信息
return {
displayName: null,
duty: null,
name: null,
email: null,
department: null,
location: null,
tel: null,
phone: null,
superior: null,
skills: null
}
},
mounted (){//使用mounted在挂在DOM时通过restful api获取用户基本信息并填充到data中。这个之后详细说明。
this.$http.get(
"https://192.168.227.1:8443/userInfo" ,
{
headers: {"token" : localStorage.token}//在requestHeader中携带之前产生的token用来在后端验证用户权限。
}
)
.then(
//success
response => {
this.displayName = response.data.displayName;
this.duty = response.data.duty;
this.name = response.data.name;
this.email = response.data.email;
this.department = response.data.department;
this.location = response.data.location;
this.tel = response.data.tel;
this.phone = response.data.phone;
this.superior = response.data.superior;
this.skills = response.data.skills;
},
//error
response => {
}
)
}
}
</script>
<style scoped>
@font-face
{
font-family: yaHeiFont;
src: url("../assets/font/yaHei.ttf")
}
.ch
{
font-family:yaHeiFont;
color: black;
}
</style>
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
126
127
128
129
130
131
132
133
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
126
127
128
129
130
131
132
133
第三步,在main.js中引入userProfile.vue文件并且为userProfile添加路由。这里因为userProfile内容要显示的地方是在红框区域内,如下图:
而红框区域内的路由出口是在index.vue中定义的,所以如果想要userProfile的内容正确渲染到红框区域内,则需要把userProfile嵌套在index中,需要用到vue-router的嵌套路由。
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from "vue"
import VueRouter from "vue-router"
import VueResource from "vue-resource"
import store from "./store/store"
//bootstrap
import "bootstrap/dist/css/bootstrap.css"
import "bootstrap/dist/js/bootstrap.min.js"
//AdminLTE
import "./assets/css/skins/_all-skins.min.css"
import "./assets/css/AdminLTE.min.css"
import "./assets/js/app.min.js"
//font-awesome
import "font-awesome/css/font-awesome.min.css"
//echarts
import echarts from "echarts"
//components
import App from "./App"
import Login from "./components/login"
import Index from "./components/index"
import DeviceCatalog from "./components/deviceCatalog"
import UserProfile from "./components/userProfile"
import TravelCount from "./components/travelCount"
import WorkCircle from "./components/workCircle"
Vue.use(VueRouter)
Vue.use(VueResource)
//注册echarts的一种方法
// Object.defineProperties(Vue.prototype, {
// $echarts: { get: () => echarts }
// });
//Vue-Resource提交方式设置
Vue.http.options.emulateJSON = true;
const routes = [
//登录页
{
path: "/login",
component : Login
},
//导航页
{
path: "/index",
component: Index,
//导航页子页面,children中的component将被渲染到之前说的红色区域内
children: [
//设备目录页
{
path: "/deviceCatalog",
component: DeviceCatalog
},
//账户信息页
{
path: "/userProfile",
component: UserProfile,
//账户信息子页面
children: [
//出差统计页
{
path: "/userProfile/travelCount",
component: TravelCount
},
//工作圈子页
{
path: "/userProfile/workCircle",
component: WorkCircle
}
]
},
]
},
]
const router = new VueRouter({
routes
})
//默认导航到登录页
// router.push("/login")
/*
全局路由钩子
访问资源时需要验证localStorage中是否存在token
以及token是否过期
验证成功可以继续跳转
失败返回登录页重新登录
*/
router.beforeEach((to, from, next) => {
if(to.path == "/login"){
next()
}
if(localStorage.token && new Date().getTime() < localStorage.tokenExpired){
next()
}
else{
next("/login")
}
})
new Vue({
el: "#app",
template: "<App/>",
components: { App },
router:router,
store: store,
echarts: echarts//注册echarts的另一种方法
}
})
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
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
做完之后点击用户信息按钮应该就可以跳转到用户信息页了,看下效果
第四步出差统计信息,这里想使用echarts图表来进行展示(这里我跳了一个坑)。首先在项目中使用npm install echarts –save将echarts下载到项目中。然后在main.js中引用,对应第三步main.js中
import echarts from "echarts"
1
1
在travelCount中使用,travelCount.vue。这里有几点需要注意
1.echarts挂载的dom必须设置一个高度,否则不能显示出来。
2.echarts的配置需要放在mounted中。
3.使用echarts的resize方法配合js中onresize重绘图表以动态适应屏幕尺寸。
<template>
<div id="main" style="height:730px"></div>
</template>
<script>
export default {
mounted() {
var myChart = this.$root.$options.echarts.init(document.getElementById("main"));//这里注意
var option = {
grid : {
left : "1%",
right : "2%",
bottom : "1%",
containLabel : true
},
xAxis : {
type : "value",
boundaryGap : [ 0, 0.01 ]
},
yAxis : {
type : "category",
axisLabel : {
inside : true,
textStyle : {
fontWeight : "bold"
},
},
z : 100,
data : ["A国:2016-01-01至2016-02-01" , "B国:2016-03-01至2016-04-01" , "C国:2016-06-01至2016-06-01"]
},
series : [ {
type : "bar",
barGap : "10%",
itemStyle : {
normal : {
color : "LightSkyBlue"
}
},
data : [100 , 200 , 300]
} ]
}
myChart.setOption(option);
//窗口尺寸变化时重新绘制chart
window.onresize = () => {
myChart.resize()
}
}
}
</script>
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
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
这里要说下我跳的坑,引入echarts时,上面的代码使用了
this.$root.$options.echarts
1
1
如果这样引用的话,需要在main.js的Vue根实例中注册echarts,见第三步main.js配置。还有一种方法可以使用this.$echarts的方式引用(这个方法是Vue论坛中tomi-li老师指点的,谢谢老师)。
这种方法需要使用js的Object.defineProperties将echarts手动添加到Vue对象中,见第三步main.js配置。使用这种方法时不需要在根实例中注册echarts。
Object.defineProperties(Vue.prototype, {
$echarts: { get: () => echarts }
});
1
2
3
1
2
3
如果没有手动添加的话,在其他Vue文件中使用echarts时会报错,告诉你echarts没有定义过。
看下效果
OK,前端的部分完成了,现在出差信息是静态数据,因为来处理、记录这些数据的服务可能还要依赖其他服务,我们先用静态数据代替。但是用户基本信息是从Ldap中取出来的,之前第二步中不是使用了vur-resourse到这个地址获取用户数据么。
this.$http.get(
"https://192.168.227.1:8443/userInfo" ,
{
headers: {"token" : localStorage.token}
}
)
1
2
3
4
5
6
1
2
3
4
5
6
后端处理这个请求的controller。因为在分布式架构下这个controller通过restful对外提供获取用户信息的服务,所以需要使用@CrossOrigin注解满足跨域的需求。
getUserInfo方法做这几件事情
1.拿到header中的用户token,解密后判断用户凭证是否过期、是否拥有某种权限角色。
2.如果判断没有问题,使用token中的用户名作为入参,调用SpringLdap取得用户信息,并使用EmployeeAttributesMapper 填充我们自己的POJO类。返回response信息。
3.如果判断有问题,返回403表示认证没有通过。
package an.userinfo;
import static org.springframework.ldap.query.LdapQueryBuilder.query;
import java.util.Date;
import javax.naming.directory.Attributes;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.ldap.NamingException;
import org.springframework.ldap.core.AttributesMapper;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import an.entity.Employee;
import io.jsonwebtoken.Jwts;
@RestController("/userInfo")
public class UserInfoWeb {
//jwt加密密匙
@Value("${jwt.key}")
private String jwtKey;
//ldap模板
@Autowired
private LdapTemplate ldapTemplate;
/**
* 将域用户属性通过EmployeeAttributesMapper填充到Employee类中,返回一个填充信息的Employee实例
*/
private class EmployeeAttributesMapper implements AttributesMapper<Employee> {
public Employee mapFromAttributes(Attributes attrs) throws NamingException, javax.naming.NamingException {
Employee employee = new Employee();
employee.setName((String) attrs.get("sAMAccountName").get());
employee.setDisplayName((String) attrs.get("displayName").get());
employee.setEmail((String) attrs.get("userprincipalname").get());
employee.setDuty((String) attrs.get("title").get());
employee.setDepartment((String) attrs.get("department").get());
employee.setSuperior((String) attrs.get("manager").get());
employee.setLocation((String) attrs.get("physicaldeliveryofficename").get());
employee.setTel((String) attrs.get("homephone").get());
employee.setPhone((String) attrs.get("mobile").get());
String skill = (String) attrs.get("description").get();
String skills[] = skill.split(";");
employee.setSkills(skills);
return employee;
}
}
/**
* 使用用户凭证取得用户信息
* @param token 用户凭证
* @return
*/
@CrossOrigin
@RequestMapping(method = RequestMethod.GET)
public ResponseEntity<String> getUserInfo(@RequestHeader("token") String token){
//解析token
String username = Jwts.parser().setSigningKey(jwtKey).parseClaimsJws(token).getBody().getSubject();
String roles = Jwts.parser().setSigningKey(jwtKey).parseClaimsJws(token).getBody().getAudience();
long expiration = Jwts.parser().setSigningKey(jwtKey).parseClaimsJws(token).getBody().getExpiration().getTime();
long current = new Date().getTime();
//验证token过期时间、用户权限
if(current > expiration || roles.indexOf("ROLE_USER") == -1) {
return new ResponseEntity<String>(HttpStatus.FORBIDDEN);
}
//查询并产生用户信息
Employee employee = ldapTemplate
.search(query().where("objectclass").is("person").and("sAMAccountName").is(username),
new EmployeeAttributesMapper())
.get(0);
return new ResponseEntity<String>(JSON.toJSONString(employee , SerializerFeature.DisableCircularReferenceDetect) , HttpStatus.OK);
}
}
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
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
写完收工,文字表达能力实在是差,各位费眼了。
(2) [用户信息页]网页授权获取用户基本信息
网页授权获取用户基本信息
如果用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑。
关于网页授权回调域名的说明
1、在微信公众号请求用户网页授权之前,开发者需要先到公众平台官网中的开发者中心页配置授权回调域名。请注意,这里填写的是域名(是一个字符串),而不是URL,因此请勿加http://等协议头;
2、授权回调域名配置规范为全域名,比如需要网页授权的域名为:www.qq.com,配置以后此域名下面的页面http://www.qq.com/music.html 、 http://www.qq.com/login.html 都可以进行OAuth2.0鉴权。但http://pay.qq.com 、 http://music.qq.com 、 http://qq.com无法进行OAuth2.0鉴权
3、如果公众号登录授权给了第三方开发者来进行管理,则不必做任何设置,由第三方代替公众号实现网页授权即可
关于网页授权的两种scope的区别说明
1、以snsapi_base为scope发起的网页授权,是用来获取进入页面的用户的openid的,并且是静默授权并自动跳转到回调页的。用户感知的就是直接进入了回调页(往往是业务页面)
2、以snsapi_userinfo为scope发起的网页授权,是用来获取用户的基本信息的。但这种授权需要用户手动同意,并且由于用户同意过,所以无须关注,就可在授权后获取该用户的基本信息。
3、用户管理类接口中的“获取用户基本信息接口”,是在用户和公众号产生消息交互或关注后事件推送后,才能根据用户OpenID来获取用户基本信息。这个接口,包括其他微信接口,都是需要该用户(即openid)关注了公众号后,才能调用成功的。
关于网页授权access_token和普通access_token的区别
1、微信网页授权是通过OAuth2.0机制实现的,在用户授权给公众号后,公众号可以获取到一个网页授权特有的接口调用凭证(网页授权access_token),通过网页授权access_token可以进行授权后接口调用,如获取用户基本信息;
2、其他微信接口,需要通过基础支持中的“获取access_token”接口来获取到的普通access_token调用。
关于UnionID机制
1、请注意,网页授权获取用户基本信息也遵循UnionID机制。即如果开发者有在多个公众号,或在公众号、移动应用之间统一用户帐号的需求,需要前往微信开放平台(open.weixin.qq.com)绑定公众号后,才可利用UnionID机制来满足上述需求。
2、UnionID机制的作用说明:如果开发者拥有多个移动应用、网站应用和公众帐号,可通过获取用户基本信息中的unionid来区分用户的唯一性,因为同一用户,对同一个微信开放平台下的不同应用(移动应用、网站应用和公众帐号),unionid是相同的。
关于特殊场景下的静默授权
1、上面已经提到,对于以snsapi_base为scope的网页授权,就静默授权的,用户无感知;
2、对于已关注公众号的用户,如果用户从公众号的会话或者自定义菜单进入本公众号的网页授权页,即使是scope为snsapi_userinfo,也是静默授权,用户无感知。
具体而言,网页授权流程分为四步:
1、引导用户进入授权页面同意授权,获取code
2、通过code换取网页授权access_token(与基础支持中的access_token不同)
3、如果需要,开发者可以刷新网页授权access_token,避免过期
4、通过网页授权access_token和openid获取用户基本信息(支持UnionID机制)
目录
1 第一步:用户同意授权,获取code
2 第二步:通过code换取网页授权access_token
3 第三步:刷新access_token(如果需要)
4 第四步:拉取用户信息(需scope为 snsapi_userinfo)
5 附:检验授权凭证(access_token)是否有效
第一步:用户同意授权,获取code
在确保微信公众账号拥有授权作用域(scope参数)的权限的前提下(服务号获得高级接口后,默认拥有scope参数中的snsapi_base和snsapi_userinfo),引导关注者打开如下页面:
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
若提示“该链接无法访问”,请检查参数是否填写错误,是否拥有scope参数对应的授权作用域权限。
尤其注意:由于授权操作安全等级较高,所以在发起授权请求时,微信会对授权链接做正则强匹配校验,如果链接的参数顺序不对,授权页面将无法正常访问
参考链接(请在微信客户端中打开此链接体验)
Scope为snsapi_base
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx520c15f417810387&redirect_uri=https%3A%2F%2Fchong.qq.com%2Fphp%2Findex.php%3Fd%3D%26c%3DwxAdapter%26m%3DmobileDeal%26showwxpaytitle%3D1%26vb2ctag%3D4_2030_5_1194_60&response_type=code&scope=snsapi_base&state=123#wechat_redirect
Scope为snsapi_userinfo
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxf0e81c3bee622d60&redirect_uri=http%3A%2F%2Fnba.bluewebgame.com%2Foauth_response.php&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect
尤其注意:跳转回调redirect_uri,应当使用https链接来确保授权code的安全性。
参数说明
参数
是否必须
说明
appid
是
公众号的唯一标识
redirect_uri
是
授权后重定向的回调链接地址,请使用urlencode对链接进行处理
response_type
是
返回类型,请填写code
scope
是
应用授权作用域,snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid),snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息)
state
否
重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节
#wechat_redirect
是
无论直接打开还是做页面302重定向时候,必须带此参数
下图为scope等于snsapi_userinfo时的授权页面:
用户同意授权后
如果用户同意授权,页面将跳转至 redirect_uri/?code=CODE&state=STATE。若用户禁止授权,则重定向后不会带上code参数,仅会带上state参数redirect_uri?state=STATE
code说明 :
code作为换取access_token的票据,每次用户授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期。
第二步:通过code换取网页授权access_token
首先请注意,这里通过code换取的是一个特殊的网页授权access_token,与基础支持中的access_token(该access_token用于调用其他接口)不同。公众号可通过下述接口来获取网页授权access_token。如果网页授权的作用域为snsapi_base,则本步骤中获取到网页授权access_token的同时,也获取到了openid,snsapi_base式的网页授权流程即到此为止。
尤其注意:由于公众号的secret和获取到的access_token安全级别都非常高,必须只保存在服务器,不允许传给客户端。后续刷新access_token、通过access_token获取用户信息等步骤,也必须从服务器发起。
请求方法
获取code后,请求以下链接获取access_token:
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
返回说明
正确时返回的JSON数据包如下:
{
"access_token":"ACCESS_TOKEN",
"expires_in":7200,
"refresh_token":"REFRESH_TOKEN",
"openid":"OPENID",
"scope":"SCOPE",
"unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
}
参数
描述
access_token
网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
expires_in
access_token接口调用凭证超时时间,单位(秒)
refresh_token
用户刷新access_token
openid
用户唯一标识,请注意,在未关注公众号时,用户访问公众号的网页,也会产生一个用户和公众号唯一的OpenID
scope
用户授权的作用域,使用逗号(,)分隔
unionid
只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段。详见:获取用户个人信息(UnionID机制)
错误时微信会返回JSON数据包如下(示例为Code无效错误):
{"errcode":40029,"errmsg":"invalid code"}
全局返回码说明
第三步:刷新access_token(如果需要)
由于access_token拥有较短的有效期,当access_token超时后,可以使用refresh_token进行刷新,refresh_token拥有较长的有效期(7天、30天、60天、90天),当refresh_token失效的后,需要用户重新授权。
请求方法
获取第二步的refresh_token后,请求以下链接获取access_token:
https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=APPID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN
参数
是否必须
说明
appid
是
公众号的唯一标识
grant_type
是
填写为refresh_token
refresh_token
是
填写通过access_token获取到的refresh_token参数
返回说明
正确时返回的JSON数据包如下:
{
"access_token":"ACCESS_TOKEN",
"expires_in":7200,
"refresh_token":"REFRESH_TOKEN",
"openid":"OPENID",
"scope":"SCOPE"
}
参数
描述
access_token
网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
expires_in
access_token接口调用凭证超时时间,单位(秒)
refresh_token
用户刷新access_token
openid
用户唯一标识
scope
用户授权的作用域,使用逗号(,)分隔
错误时微信会返回JSON数据包如下(示例为Code无效错误):
{"errcode":40029,"errmsg":"invalid code"}
全局返回码说明
第四步:拉取用户信息(需scope为 snsapi_userinfo)
如果网页授权作用域为snsapi_userinfo,则此时开发者可以通过access_token和openid拉取用户信息了。
请求方法
http:GET(请使用https协议)
https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
参数说明
参数
描述
access_token
网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
openid
用户的唯一标识
lang
返回国家地区语言版本,zh_CN 简体,zh_TW 繁体,en 英语
返回说明
正确时返回的JSON数据包如下:
{
"openid":" OPENID",
" nickname": NICKNAME,
"sex":"1",
"province":"PROVINCE"
"city":"CITY",
"country":"COUNTRY",
"headimgurl":"http://wx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/46",
"privilege":[
"PRIVILEGE1"
"PRIVILEGE2"
],
"unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
}
参数
描述
openid
用户的唯一标识
nickname
用户昵称
sex
用户的性别,值为1时是男性,值为2时是女性,值为0时是未知
province
用户个人资料填写的省份
city
普通用户个人资料填写的城市
country
国家,如中国为CN
headimgurl
用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空。若用户更换头像,原有头像URL将失效。
privilege
用户特权信息,json 数组,如微信沃卡用户为(chinaunicom)
unionid
只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段。详见:获取用户个人信息(UnionID机制)
错误时微信会返回JSON数据包如下(示例为openid无效):
{"errcode":40003,"errmsg":" invalid openid "}
全局返回码说明
附:检验授权凭证(access_token)是否有效
请求方法
http:GET(请使用https协议)
https://api.weixin.qq.com/sns/auth?access_token=ACCESS_TOKEN&openid=OPENID
参数说明
参数
描述
access_token
网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
openid
用户的唯一标识
返回说明
正确的Json返回结果:
{ "errcode":0,"errmsg":"ok"}
错误时的Json返回示例:
{ "errcode":40003,"errmsg":"invalid openid"}
(3) [用户信息页]「落地页」是如何帮助用户运营实现用户增长的?
用户获取的成本越来越高,用户获取越来越困难,不妨使用落地页,轻松实现用户增长。
一.落地页是什么
在探讨落地页是什么之前,先来看一下,落地页投放的一个流程。
在图中我们可以看到,将官网、APP等平台中最希望用户看到的内容,通过不同的渠道分发出去,由渠道带来流量,用户点击任意渠道链接/内容,进入的第一个页面即称为落地页。落地页能够为商家带来用户/客户。
从上边的分析我们可以知道,落地页有两个用途:承接流量和转化用户。对这两个用途进行展开,又可以分为有价值的商品展示和收集用户资料。用户看到商家投放的信息之后,对信息感兴趣才会点击,进入落地页后,如果对落地页展示的内容感兴趣,则会再进一步进行操作。而有的落地页,除了展示商品,还肩负着收集用户信息的重任。将用户信息收集起来,有助于下一步营销活动的开展,将潜在用户变成自己真正的用户。
二.常见的落地页有哪些
我们已经知道落地页有两个作用:承接流量和转化用户。因此从类型上分,落地页可分为点击落地页和收集线索落地页。
1.点击落地页(1)营销广告
以上是眼镜投放在QQ聊天页面的广告,点击之后,直接到达该眼镜店铺的网站。“专为女性定制的近视眼镜”、“预售价399”简洁精准的描述语,吸引有需求的用户点击,这个眼镜店铺网站即是落地页,通过广告投放,将用户带到自己的网站中。完成销售转化。
(2)用户获取
盒子菌点开朋友从微信分享的ofo集卡活动,来到了活动的落地页。落地页为盒子菌介绍了ofo的集卡活动,只需要集齐5种国庆卡即可兑换10.1元的国庆大奖。点击“立即去集卡”就跳转到APP的下载页面。通过落地页为用户展示ofo活动的内容,然后再鼓励用户去参加活动,以此来刺激用户下载APP,为APP拉来更多用户。如果用户已经下载过APP,通过落地页对活动进行展示,让更多参与到活动中来,还可以提高APP的用户活跃。
点击落地页多为用户商品展示和活动告知,用户通过点击落地页认识到你的产品和服务,如果用户有需要,则会进行下一步操作,落地页的目的也就达到了。
2.收集线索落地页(1)优惠券发放
优惠券发放的落地页,多见于O2O、电商,用户在线上完成商品的下单,然后再分享给好友,好友通过打开链接,输入自己的电话号码,以获取优惠券。
Enjoy这款精选美食电商APP也制作了领券的落地页,盒子菌在购买商品之后,禁不住礼券的诱惑,将链接分享给好友。最后盒子菌与好友同时获得了礼券。用户输入自己的手机号码领券,商家就获得了用户信息,而用户领取到优惠,但这个优惠只能在Enjoy使用,领取过礼券的用户比未领取礼券用户的消费意愿更高。如果是比较新的APP,可以用社交关系派发优惠券的方式提高自己的知名度。
(2)直接注册
团贷网APP的邀请有礼活动落地页为用户提供了几千元的新手礼包,看到这,谁能不动心呢,但如果用户要想把这几千块的礼包收入囊中,就需要完成新用户注册,在邀请有礼的页面底部, 有一个注册的表单,当用户完成注册之后,就可以领取到新手礼包。
以上提到的团贷网APP的邀请有礼活动是用户的社交关系分享出去的,这在一定程度增加了其他用户的信任感,用户点击分享链接,抵达落地页,被落地页的信息所吸引,完成注册, 对团贷网APP而言,通过落地页的方式,获取到了新的用户。而用户,则是从落地页得到了团贷网APP提供的新手礼包。
(3)预约报名
自从微信朋友圈接入了广告之后,盒子菌经常在朋友圈看到收集线索落地页的广告。比如这个投放在朋友圈的古风摄影广告,用户点击查看详情,查看活动的详情页。页面还强调这个活动是微信报名专享,仅限99个名额,用这种限时优惠的方式刺激用户报名。在落地页的最后,为用户提供报名表单的填写,用户只需要填写上自己的信息,就会有工作人员与用户联系。
收集线索的落地页多用于收集用户信息,通过页面的文案和图片,为用户展示自己的活动或商品,再用优惠的价格和赠品刺激用户报名。商家获取到用户的个人信息之后,可通过后续营销活动或者各个渠道的消息告知,将这些潜在的用户变成真正的用户。
三.怎样制作一个落地页
图片、文案是落地页最基本的组成,且大部分落地页有表单元素。在创建落地页时,怎样才能让用户喜欢呢。
1.图片
落地页的图片一般选取较为精美的图片,整体页面设计一致,不会显得突兀。用户进入落地页之后,第一印象就是整体的风格。设想一下,如果图片不清晰,整个页面的风格很LOW,用户会不会以为自己误入了什么诈骗页面,第一时间关闭页面,别说什么在表单填入个人信息了。
2.文案
落地页的文案越精简越好,标题能够让用户知道这个落地页是活动报名、优惠券领取、还是特价商品展示。用户一进入落地页眼球就被标题紧紧吸引住,跳出率也就降低了。至于阐述的文案,只需要让用户知道这个落地页是干嘛的,具体怎么操作就好。过多的文字也会提高跳出率。
3.表单
表单是用于记录用户信息,也需要精简,外卖APP的落地页表单只需填入手机号码,同城活动只需填入用户名称和手机号码.....在表单的设计上,只需要让用户填入商家营销必备的信息就好。用户参与成本过高的话,只会造成用户流失。
APP活动运营工具活动盒子最近就上线了落地页功能,运营人员可以根据自己需要,直接拖拽左方的部件库,完成自己的落地页设计,以下是盒子菌制作的APP用户拉新的落地页的示意图。用户点击立即下载,即可到达该APP的应用商店页面。
总结:用户通过落地页,在第一时间获取到有用的信息,并根据页面提示完成操作,得到了想要的东西,商家则获取到用户信息。赶紧着手为自己的APP创建一个落地页吧。





![[2019普通话成绩什么时候出]2019普通话成绩查询入口:畅言网和全国普通话培训测试信息资源网](https://img.wykw.com/uploadfile/images/2018/0911/15366028286056562.jpg)

