关于静默登录
静默登录用户是无感的,最明显的体验是小程序,一个用户如果曾经授权登录过你的系统,那么以后他再打开你的小程序,他都不用进行授权操作了,而公众号H5网页用户打开的时候,还是会出现连续跳转,但这个无伤大雅了。
静默登录的业务逻辑
具体的业务逻辑就是,获取code,然后通过code去请求后端系统,后端根据code就可以获取到用户的openid,然后查询系统,如果存在openid那么就生成token返回给前端(此操作相当于登录了),如果不存在此openid,那么就什么都不操作,返回一个空的数据结构给前端,那么前端根据返回的数据进行相应的操作,如果存在token,那就保存好token,相当于登录操作,如果是空数据就什么都不操作。一般系统是这样设计,一个登录的API,用于专门生成token使用,另外有一个通过token获取用户详细资料的API。所以通过静默登录操作之后,如果后端返回了token,那么前端就通过token去查询用户详细信息,如果没有返回token,那么就什么都不进行操作。
所谓公众号H5网页是指用户在微信客户端中访问第三方网页,那么公众号就可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑。
设置网页授权回调域名
在微信公众号请求用户网页授权之前,开发者需要先到公众平台官网中的“设置与开发 - 公众号设置 - 功能设置 - JS接口安全域名 - 网页授权域名 ”的配置选项中,设置授权回调域名。
网页授权流程
公众号H5网页授权其实就是访问微信提供的一个URL链接:
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
参数说明(来源于微信公众号官方)
参数 | 是否必须 | 说明 |
---|---|---|
appid | 是 | 公众号的唯一标识 |
redirect_uri | 是 | 授权后重定向的回调链接地址, 请使用 urlEncode 对链接进行处理 |
response_type | 是 | 返回类型,请填写code |
scope | 是 | 应用授权作用域,snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid),snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且, 即使在未关注的情况下,只要用户授权,也能获取其信息 ) |
state | 否 | 重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节 |
#wechat_redirect | 是 | 无论直接打开还是做页面302重定向时候,必须带此参数 |
具体而言,网页授权流程分为四步:
2 第二步:通过code换取网页授权access_token
4 第四步:拉取用户信息(需scope为 snsapi_userinfo)
公众号H5的授权登录实现
了解了基础知识之后,下面开始实现微信公众号H5的授权登录。
一般我们的业务场景大概是这样的,我们访问一个需要登录的业务动作的时候,比如商城里的下单,我们就要判断用户是否已经登录了,没有登录的话,就要跳转到一个登录页面(也有可能是在当前页面弹框),然后引导用户点击授权登录按钮,这个时候我们就需要跳到微信服务器的授权页面,授权完权之后,再跳回我们的业务系统,这个时候,一般是跳到一个中转页面,进行数据处理,处理完之后,再跳回到最初的那个业务场景页面。
流程图
那么主要的登录流程是在登录页面用户点击了授权登录按钮之后,首先我们要构造访问微信提供的授权URL链接里参数,因为需要获取用户的头像、昵称、性别、所在地等信息,所以scope为snsapi_userinfo。redirect_uri为微信服务器重定向回调的地址,什么意思呢?就是我们访问了微信服务器的那个授权地址,去到微信服务器那边之后,微信服务器还要根据我们设置的redirect_uri参数地址,跳回到我们设置的redirect_uri参数地址,那么这个就是我们前端网页应用上的一个地址,比如在上面的流程图例子中,这个地址就是中转页面的地址,而不是最初的商品详情地址。
代码实现
// 点击授权登录按钮触发的动作函数
function toAuth(){
// 参数appid
const appId = 'xxxxxxxxx'
// 参数scope
const snsapiBase = 'snsapi_base'
// 中转页面
let backUrl = `/pages/auth/index`;
let url = `${location.origin}${backUrl}`
// 参数redirect_uri
const redirect_uri = encodeURIComponent(url)
// 参数state
const state = encodeURIComponent(
('' + Math.random()).split('.')[1] + 'authorizestate'
)
const OAuthUrl = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appId}&redirect_uri=${redirect_uri}&response_type=code&scope=${snsapiBase}&state=${state}#wechat_redirect`
}
// 跳到微信授权页面
location.href = OAuthUrl
}
从微信授权页面获取用户的授权之后,微信服务器重定向到我们的中转页面,此时的URL链接中将带有code参数,我们则要获取code之后,去访问我们的后端系统。如果授权成功,后端将生成token返回,代表登录成功,前端获取到token,保存好token设置登录状态。
以uniapp项目代码为示例
在App.vue文件中
export default {
onLaunch: function(option) {
// 获取code
const {
code,
} = option.query;
if(code) {
// 通过code访问后端API
fetchLogin({code}).then(r => {
// 如果授权成功,后端将生成token返回,代表登录成功,前端获取到token,保存好token设置登录状态,再通过token去获取用户详细信息
})
}
}
}
以下为后端需要做的事情,前端也需要了解一下,加深整个流程通透性。
后端通过前端传过来code换取网页授权access_token。如果网页授权的作用域为snsapi_base,则本步骤中获取到网页授权access_token的同时,也获取到了openid。如果网页授权作用域为snsapi_userinfo,则此时开发者可以通过access_token和openid拉取用户信息了。获取到用户信息之后,那么后端需要做的事情就是注册一个新用户或者更新用户数据,并且去生成一个token返回给前端。
前端获取到token,保存好token设置登录状态,这个时候整个授权登录流程就算完成了。
这个里面在实际过程中,可能每个系统设计不一样,有些可能是在fetchLogin登录的API里面同时返回token和用户信息,有些则可能是fetchLogin只是返回token数据,获取用户信息则通过另外的API进行获取,这些就示具体情况而定了。
公众号H5的静默登录实现
在了解微信公众号H5的授权登录知识之后,下面开始实现微信公众号H5的静默登录。静默登录是用户一进入你的系统页面就首先进行的操作。
首先我们跟授权登录一样需要构造访问微信提供的URL链接里参数,因为是静默登录,所以scope为snsapi_base,那么我们在首页进行静默登录的时候,我们希望静默授权之后,又重定向回到我们的首页上来。又因为用户可能进入页面不是我们的首页,而是有可能是其他人分享的某个页面,所以我们就把redirect_uri设置为当前URL。跟上面授权登录需要一个中转页面不同,静默登录不需要中转页面,或者说中转页面就是当前页面。当然是否一定需要一个中转页面,也可以商讨的,个人认为有一个中转页面,更加方便操作,不会混乱。
实现参考代码
// 参数appid
const appId = 'xxxxxxxxx'
// 参数scope
const snsapiBase = 'snsapi_base'
// 当前URL
let backUrl = location.pathname + location.search;
let url = `${location.origin}${backUrl}`
// 参数redirect_uri
const redirect_uri = encodeURIComponent(url)
// 参数state
const state = encodeURIComponent(
('' + Math.random()).split('.')[1] + 'authorizestate'
)
const OAuthUrl = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appId}&redirect_uri=${redirect_uri}&response_type=code&scope=${snsapiBase}&state=${state}#wechat_redirect`
}
// 跳到微信授权页面
location.href = OAuthUrl
那么跳转到微信授权页面之后,微信服务器又会重定向回到我们设置的那个重定向的URL上,我们设置的重定向的URL就是我们当前的页面,所以微信服务器最终会重定向回到我们当前的页面,并且URL参数里会带有code的。我们就可以获取到这个code,然后去请求我们自己的后端服务器。
以下为后端做的事情
后端服务器根据前端提供的code,获取网页授权access_token。因为静默登录的网页授权的作用域为snsapi_base,所以在本步骤中获取到网页授权access_token的同时,也获取到了openid。因为openid是唯一的,所以后端会通过openid往用户表里面查询有没有存在此openid,如果存在则证明此用户曾经授权注册过了,然后直接生成token返回给前端,如果没有查询到,则证明此用户还没授权注册我们的系统,这个时候后端则什么都不做,而是返回一个空的数据结构给前端。
那么前端通过后端返回的数据中判断,如果存在token则,保存token设置登录状态,并且通过token去获取用户详细信息,如果不存在token则什么都不做。
具体业务代码实现
首先封装一个获取跳转授权后的地址函数
/**
* 获取跳转授权后的地址
* @param {Object} appId
*/
getAuthUrl(appId, snsapiBase, backUrl) {
let url = `${location.origin}${backUrl}`
if (url.indexOf('?') == -1) {
url = url + '?'
} else {
url = url + '&'
}
const redirect_uri = encodeURIComponent(
`${url}&back_url=` +
encodeURIComponent(
encodeURIComponent(
uni.getStorageSync('BACK_URL') ?
uni.getStorageSync('BACK_URL') :
location.pathname + location.search
)
)
)
uni.removeStorageSync(BACK_URL)
const state = encodeURIComponent(
('' + Math.random()).split('.')[1] + 'authorizestate'
)
return `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appId}&redirect_uri=${redirect_uri}&response_type=code&scope=${snsapiBase}&state=${state}#wechat_redirect`
}
/**
* 跳转自动登录
*/
toAuth(snsapiBase, backUrl) {
// 这个appId一般由后端返回
location.href = this.getAuthUrl('wx6f48a4dxxxxxxxxx', snsapiBase, backUrl);
}
我们在重定向的地址参数里设置多了一个back_url的参数,这个参数是干什么的呢?这个参数就是用来记录最初跳转到登录页面的那个页面地址,比如我们上面例子中的商品页面,我们从商品页面跳到登录页面,再到微信授权页面,而我们设计的是微信授权之后是跳转到一个中转页面,那么跳到中转页面之后,怎么再跳转回到商品页面呢?这里就巧妙地设置了这么一个参数,进行记录最初的页面,让进行一系列操作之后再跳转到商品页面。
那么我们要记录这个地址,就要在我们跳到登录页面之前就进行记录了。这个时候我们可以设置一个登录函数进行设置。也就是说如果用户需要授权登录,都需要通过这个函数进行操作。
export function toLogin() {
// 跳转到登录页面之前,先记录BACK_URL地址
path = location.pathname + location.search
uni.setStorageSync('BACK_URL', path)
uni.navigateTo({
url: '/pages/my/login'
})
}
用户在登录页面点击授权登录之后,实际是调用上面的toAuth函数就可以了。
那么是否一定要跳到一个登录页面进行授权操作呢?其实也不需要,但在实际项目中,有可能设计师设计了一个漂亮的登录页面,你不可能说做不到吧,所以就这样巧妙地设置了一个登录函数。
toAuth('snsapi_userinfo', '/pages/auth/index')
scope为snsapi_userinfo,设置中转页面地址。
当微信服务器重定向回到我们的设置页面地址之后,它都会访问App.vue里的内容,所以我们要在App.vue里设置共同处理授权登录和静默登录的逻辑代码。
// App.vue
export default {
onLaunch: function(option) {
// #ifdef H5
let snsapiBase = 'snsapi_base';
let urlData = location.pathname + location.search;
// 判断是否已经登录和是否微信环境
if (!that.$store.getters.isLogin && isWeixin()) {
const {
code,
state,
back_url
} = option.query;
if (code && location.pathname.indexOf(
'/pages/my/login') === -1) {
// 判断是静态登录,还是授权登录,这也是设置中转页面的作用之一
const curLoginType = location.pathname.indexOf('/pages/auth/index') === -1 ? loginType
.WECHAT_H5_STATIC : loginType.WECHAT_H5
fetchLogin(code, curLoginType)
.then(res => {
// 设置已经进行登录操作的标记
uni.setStorageSync('isFetchLogined', curLoginType);
location.replace(decodeURIComponent(decodeURIComponent(back_url)));
})
.catch(error => {
console.error('静默登录出错:', error)
});
} else {
// 首次静默登录会走这里
if (!uni.getStorageSync('isFetchLogined')) {
if (location.pathname.indexOf('/pages/my/login') === -1) {
oAuth(snsapiBase, urlData);
}
}
}
}
// #endif
}
}
以上就是公众号H5的授权登录和静默登录的具体实现过程了。
小程序的授权登录实现
小程序的授权登录相对公众号来说则简单很多了,不需要那么多花里胡哨的操作。当然小程序的登录有可能会受到微信官方的规则的影响,具体实现方式则需要根据最新的微信政策来实现,这则是小程序登录的一个比较烦人的地方。
// uniapp项目代码实现
getLogin() {
uni.showModal({
title: '温馨提示',
content: '亲,授权微信登录后才能正常使用小程序功能',
success(res) {
if (res.confirm) {
// 获取用户信息
uni.getUserProfile({
desc: "注册用户信息使用",
lang: "zh_CN",
success:(res) => {
// 获取code
uni.login({
provider: 'weixin',
success: function(loginRes) {
// 访问后端接口
fetchLogin({
code: loginRes.code,
avatarUrl: res.userInfo.avatarUrl,
gender: res.userInfo.gender,
nickName: res.userInfo.nickName
})
.then(res => {});
}
});
}
})
} else {
uni.showToast({
title: '您取消了授权',
duration: 2000
});
}
}
})
}
小程序跳转到登录页面也需要通过上面的封装的登录函数进行操作,因为也需要在跳转之前记录当前的页面地址。
export function toLogin() {
// 跳转到登录页面之前,先记录BACK_URL地址
// #ifdef MP
// 获取小程序的当前页面
let pages = getCurrentPages()
let prePage = pages[pages.length - 1]
const path = prePage.route
// #endif
// #ifdef H5
// 获取H5的当前页面
const path = location.pathname + location.search
// #endif
uni.setStorageSync('BACK_URL', path)
uni.navigateTo({
url: '/pages/my/login'
})
}
小程序静默登录
小程序的静默登录相对比较简单,就是通过code访问后端,后端通过code获取openid,然后去数据库查询是否已经授权过了,已经授权过了,就生成token返回给前端,如果没有授权过,那么就什么都不做,返回一个空数据结构给前端即可。前端则判断返回的数据是否存在token,进行相应的操作。有token则保存token设置登录状态,然后通过token获取用户详细信息。
// App.vue
export default {
onLaunch: function(option) {
// #ifdef MP
// 小程序静默授权
if (!this.$store.getters.isLogin) {
// 获取code
uni.login({
provider: 'weixin',
success: function(loginRes) {
// 访问后端接口
fetchLogin({
code: loginRes.code
})
.then(res => {
// 根据返回的数据进行相应的设置
});
}
});
}
// #endif
}
}
总的来说,小程序和公众号H5的授权登录和静默登录原理都是一致的,只是在实现的方式存在巨大的差异。