以前做了一个小程序,但好久没用了,前段时间做网站登录的时候,写了一个文章 ➤ 系统支付宝、QQ、微博、Github三方登录接入 还剩微信登录没做,就又手痒了,加了一个微信登录。当然不是微信认证的那种登录,微信认证对于这种博客网站是没有必要的。所以就使用曲线救国计划,使用小程序登录。
一、前期准备
(1)申请一个微信小程序,这里需要准备一个没有用过的邮箱。
(2)学习小程序开发,对于前端开发来说应该不难,会使用css,js,html这些就可以看懂了。
(3)后端开发,我使用C#开发的,所以示例代码可能就是用C#代码,java原理也一样。
(4)小程序认证,这个认证要30元,可以选择不认证(如果只是简单实践就不需要认证了),不认证在微信里面就搜索不出你的小程序,哎,以前可不是这样的。
原理
使用微信小程序二维码,扫码时跳转小程序获得用户的授权码,使用授权码,查询openId,有了openId就可以获得用户信息,登录网站,为了更好理解画了一个图,虽然画的不好但方便理解吧。
打开网站登录页面的电脑很多,扫码后要登录哪一个呢。这就必须能识别当前登录页面显示的二维码是哪一个。微信小程序二维码(不限制的小程序码)获取的接口
这个接口可以带一个参数 scene,使用guid,去掉“-”,刚好32位。
最大32个可见字符,只支持数字,大小写英文以及部分特殊字符:!#$&'()*+,/:;=?@-._~,其它字符请自行编码为合法字符(因不支持%,中文无法使用 urlencode 处理,请使用其他编码方式)
获取小程二维码时还需要指定跳转到小程序的哪个页面(必须已经提交过的代码里存在此页面,如果不存在会接口报异常,可以建一个空页面先提交审核后面慢慢实现,不然又要等着),可以单独写一个登录页面,模仿微信授权登录页面。下面就是本站登录扫码登录时的页面(为啥叫小占时光呢?因为取了很多其他名字备案都不能通过,气死个人)。
二、代码实现
实现的话分为三个部分,web前端页面,web服务端,微信小程序。
web前端页面
前端页面实现的话,比较简单,显示获取获取二维码,显示出来。后面就是不断的请求服务端,看看登录了没。由于使用的是后端渲染,获取到图片就直接放到img标签中了。其他就是循环请求服务端了,如果已经登录则直接跳转到首页。
var url = $('#url').val();
var timer = setInterval(function() {
abp.ajax({
type: 'get',
url: url,
success: function(data) {
if (data.status == 2) {
window.location.href = "/";
}
},
});
},500);
登录页面效果
web服务端
服务端第一个事情就是 获取二维码 登录页面每次都会获取新的二维码,更具文档要求,添加参数请求即可,这里的scene就是自己给的唯一参数了 ,scene是唯一标识,需要保存在服务端,由于不是长期数据,服务端我使用缓存将其保存起来,前端来请求时就从缓存中获取。获取小程序二维码时,page 一定是要存在的,如果提示异常,记得先检查页面是否已经提交了,没有就先把页面提交上去。
下面是参考代码,代码没有考虑错误的情况,可以按自己需求添加判断
public async Task<byte[]?> GetUnlimitedQRCode(string env_version, string scene, string page)
{
var token = await GetAccessTokenAsync();
var url = string.Format(MiniprogramApi.GetUnlimitedQRCode_Url, token.AccessTokenStr);
var client = httpClientFactory.CreateClient(WeChatConsts.HttpClientName);
///请求消息
var requestMessage = new HttpRequestMessage(HttpMethod.Post, url);
var paras = JsonConvert.SerializeObject(new
{
page = page,
scene = scene,
check_path = true,
env_version = env_version
});
// 请求的form内容
var postContent = new StringContent(paras);
requestMessage.Content = postContent;
var respons = await client.SendAsync(requestMessage);
var result = await respons.Content.ReadAsStringAsync();
var qrCodeBytes = await respons.Content.ReadAsByteArrayAsync();
return qrCodeBytes;
}
第二件事就添加一个api接口,让小程序可以将授权码传过来,有了授权码,就可以得到openid,可以获取用户相关信息,但我只要了openId,因为openid就可以作为唯一标识,其他用户信息也没必要。代码只展示了实现逻辑具体细节自己现实吧:
[HttpGet]
[Route("openid")]
public async Task<string> GetMiniUserOpenId(string code, string scene = null)
{
if (!string.IsNullOrEmpty(scene))
{
// code,是授权码,scene 是缓存中此处登录扫码的唯一id
var openId = await miniAppService.GetOpenIdAsync(code, scene);
if (string.IsNullOrEmpty(openId))
{
return null;
}
// 第三方登录
var userId = await thirdPartyServices.GetUserAsync(ThirdPartyProvider.WinXin, openId);
// 得到openid 同时登录系统,放回是带上一个状态判断是否登录成功
var scanLogin = _cache.Get(scene);
if (scanLogin != null)
{
scanLogin.Status = 2;
scanLogin.UserId = userId;
_cache.Set(scene, scanLogin);
}
else
{
// 扫码已经过期,请重新扫码
return null;
}
return openId;
}
return null;
}
同时附上获取openid和获取token的文档地址,获取openid前需要现获取token
获取token 地址:➤ https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-access-token/getAccessToken.html
获取openId的地址:➤ https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/user-login/code2Session.html
微信小程序
微信小程序实现,就比较简单了,获取授权码——> 请求服务端获取openId,如果需要用户其他信息,则根据用户openid就可以获取到,但这样在小程序发布时就需要添加 “隐私条例“,用户信息我不需要,所以只用了openId做唯一标识。服务端获取到openid后,就直接修改了登录状态,web页面来查询时,已经是登录它就跳转首页,小程序同样跳转收即可(也可以显示登录成功字样)。下面就是示例代码了。
login()
{
let self =this;
wx.login({
success: (res) => {
if (res.code) {
console.log("Login code: ", res.code);
api.getOpenId(res.code,self.data.scene)
.then((res)=>{
console.log('登录成功,openId:' + res)
wx.switchTab({url:"/pages/index/index"});
});
} else {
console.log('登录失败!' + res.errMsg);
}
}
});
}