C#开发微信门户及应用(41)--基于微信开放平台的扫码登录处理

在现今很多网站里面,都使用了微信开放平台的扫码登录认证处理,这样做相当于把身份认证交给较为权威的第三方进行认证,在应用网站里面可以不需要存储用户的密码了。本篇介绍如何基于微信开放平台的扫码进行网站的登陆处理。

1、开放平台的认证

要使用网站的扫码登录处理,就需要先进行微信开放平台帐号的开发者资质认证,提交相关的资料,以及交付每年300元的认证费用。



认证后,建立相关的网站应用后,就有相关的APPID和APPSecret了,这些关键的参数就可以用来获取相关的用户信息了。
网站应用的应用详情界面如下所示。



整个开放平台感觉没有多少东西,不过需要收费认证才能使用这些功能,感觉不是很爽。
我们采用的扫码登录,需要通过开放平台获取用户的信息,因此还需要设置获取用户基本信息接口的域名,否则无法获取信息,从而会导致重定向出错。

设置域名在【接口权限】【网页账号】【网页授权获取用户基本信息】的修改入口,如下图所示。



然后在弹出的对话框里面输入授权回调的域名即可。

这样设置就可以确保获取到用户信息了。

2、扫码登录的说明和具体使用

网站应用微信登录是基于OAuth2.0协议标准构建的微信OAuth2.0授权登录系统。
在进行微信OAuth2.在进行微信OAuth2.0授权登录接入之前,在微信开放平台注册开发者帐号,并拥有一个已审核通过的网站应用,并获得相应的AppID和AppSecret,申请微信登录且通过审核后,可开始接入流程。
微信OAuth2.0授权登录让微信用户使用微信身份安全登录第三方应用或网站,在微信用户授权登录已接入微信OAuth2.0的第三方应用后,第三方可以获取到用户的接口调用凭证(access_token),通过access_token可以进行微信开放平台授权关系接口调用,从而可实现获取微信用户基本开放信息和帮助用户实现基础开放功能等。
微信OAuth2.0授权登录目前支持authorization_code模式,适用于拥有server端的应用授权。该模式整体流程为:

  1. 第三方发起微信授权登录请求,微信用户允许授权第三方应用后,微信会拉起应用或重定向到第三方网站,并且带上授权临时票据code参数;
  2. 通过code参数加上AppID和AppSecret等,通过API换取access_token;
  3. 通过access_token进行接口调用,获取用户基本数据资源或帮助用户实现基本操作。

获取access_token时序图:


从上图我们可以大概了解整个扫码登陆的处理过程。

3、扫码登录的各个步骤处理

1)用户身份的绑定
为了实现二维码扫码登录,我们需要在现有系统里面绑定用户的微信,这样才能在用户扫码的时候,判断用户的身份从而实现自动登录的过程。
我们可以在用户管理里面进行统一设置,也可以在常规用户登录(用户名+密码)后进行设置,这个主要看我们是否需要保留用户名密码登陆这种方式。
例如可以在用户管理里面统一绑定,也就是在创建用户的时候,让用户绑定下微信,获取微信的唯一标识。


也可以在保留用户名密码的登陆方式外,让用户登陆系统后自行进行绑定微信即可。

上面的界面,就是在一个页面里面弹出一个层,然后请求二维码显示即可,如下界面代码所示。

<div id="divWechat" class="easyui-dialog" style="width:450px;height:350px;padding:10px 20px"
     closed="true" resizable="true" modal="true" iconcls="icon-setting">
    <div>
        <h4>扫描用户二维码,进行绑定</h4>
    </div>
    <div align="center">
        <img id="imgQRcode" alt="使用微信扫码进行绑定" style="height:200px;width:auto" />
    </div>

    <div align="right">
        <a href="javascript:void(0)" class="easyui-linkbutton" iconcls="icon-cancel" onclick="javascript: $('#divWechat').dialog('close')">关闭</a>
    </div>
</div>

上面的层在打开的时候,我们使用JS来动态获取二维码进行显示,具体JS代码如下所示。

//绑定微信登陆
function BindWechat() {
    var url = "http://www.iqidi.com/H5/BindWechat?id=@Session["UserID"]";
    url = encodeURIComponent(url);
    $("#imgQRcode").attr("src", "/H5/QR?url=" + url);
    //打开绑定窗口
    $("#divWechat").dialog('open').dialog('setTitle', '使用微信扫码进行绑定');
}

上面的JS只是做前端的数据请求和显示,具体的QR动作Action其实就是生成扫描二维码的过程,这个二维码其实就是采用通用的方式,来构建一个指向我们绑定账号的地址,从而实现我们绑定账号的判断,二维码的生成过程如下所示。

/// <summary>
/// 转换二维码连接为图片格式
/// </summary>
/// <param name="url">二维码连接</param>
/// <returns></returns>
[HttpGet]
public ActionResult QR(string url)
{
    //初始化二维码生成工具
    QRCodeEncoder qrCodeEncoder = new QRCodeEncoder();
    qrCodeEncoder.QRCodeEncodeMode = QRCodeEncoder.ENCODE_MODE.BYTE;
    qrCodeEncoder.QRCodeErrorCorrect = QRCodeEncoder.ERROR_CORRECTION.M;
    qrCodeEncoder.QRCodeVersion = 0;
    qrCodeEncoder.QRCodeScale = 4;

    //将字符串生成二维码图片
    var image = qrCodeEncoder.Encode(url, Encoding.Default);
    //保存为PNG到内存流  
    MemoryStream ms = new MemoryStream();
    image.Save(ms, ImageFormat.Png);
    image.Dispose();

    return File(ms.ToArray(), "image/Png");
}

为了实现用户的绑定,我们需要获取当前用户的身份信息,因此需要在BindWeChat的操作里面做一个转向处理,如下接口所示。

/// <summary>
/// 生成绑定微信的地址
/// </summary>
/// <returns></returns>
public ActionResult BindWechat()

这个函数处理里面,我们需要重新定向处理,我们把它定向到BindAccount函数里面,方便获取用户的openid和其他必要的信息。
另外我们基于微信开放平台的应用,建立了一个和微信账号信息的联系,因此创建数据库信息如下所示。



也就是一个具体的开放平台应用对应着一个具体的微信账号,这样我们就可以充分利用配置进行处理了。
上面提到的BindAccount的处理的逻辑就是获取必要的信息,然后在数据库层面对身份信息进行验证,具体代码如下所示。

/// <summary>
/// 绑定用户微信号
/// </summary>
/// <param name="id">账号ID</param>
/// <returns></returns>
public ActionResult BindAccount()
{
    WebAppInfo appInfo = GetWebApp(ConfigData.WebAppId);
    AccountInfo accountInfo = GetAccount(appInfo.AccountNo);

    var htResult = GetOpenIdAndUnionId(accountInfo.UniteAppId, accountInfo.UniteAppSecret);//存储openid方便使用
    string openid = htResult["openid"].ToString();
    var unionid = htResult["unionid"].ToString();
    var userid = Request.QueryString["id"];
    var state = Request.QueryString["state"];

    if (!string.IsNullOrEmpty(openid) && !string.IsNullOrEmpty(userid))
    {
        CommonResult result = BLLFactory<User>.Instance.BindUser(openid, unionid, userid.ToInt32());
        if (result.Success)
        {
            return BindSuccess();
        }
        else
        {
            return BindFail();
        }
    }
    else
    {
        throw new WeixinException("无法获取openid" + string.Format(", openid:{0}, userid:{1}", openid, userid));
    }
}

在绑定的过程,我们需要考虑绑定正确账号,重复绑定其他账号,无效绑定几种情况,如果成功绑定正确账号(可多次处理结果一样),那么得到界面如下所示(这个界面的样式采用了weui的样式)。


2)用户的扫码登录处理
上面绑定了账号后,就可以通过扫码进行登录了,扫码回调的时候我们有自己的判断处理,扫码界面如下所示(我们在保留用户名密码登陆的方式外,增加了一个扫码登录的处理)。
如果是Bootstrap的界面效果



如果是EasyUI的界面效果



这个和前面的二维码显示规则差不多,不过他们的连接地址是不同的,这个地方用到了开放平台的接口,也就是我们前面提到开放平台认证的接口了。
上面的扫码登录的界面代码如下所示。
<!--二维码扫描登陆的界面层-->
<div id="divWechat" class="easyui-dialog" style="width:550px;height:500px;padding:10px 20px"
     closed="true" resizable="true" modal="true" iconcls="icon-setting">
    <div id="login_container" align="center">
    </div>

    <div align="right">
        <a href="javascript:void(0)" class="easyui-linkbutton" iconcls="icon-cancel" onclick="javascript: $('#divWechat').dialog('close')">关闭</a>
    </div>
</div>

上面代码需要引入JS文件,并使用微信JSSDK的API进行显示的。

<!--使用微信扫码进行登陆-->
<script src="http://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js"></script>
<script language="javascript">

    function OpenJSLogin() {
        var obj = new WxLogin({
            id: "login_container",
            appid: "@ViewBag.appid",
            scope: "snsapi_login",
            redirect_uri: "@ViewBag.redirect_uri",
            state: "@ViewBag.state",
            style: "black",
            href: ".impowerBox .qrcode {width: 200px;}"
        });

        //打开绑定窗口
        $("#divWechat").dialog('open').dialog('setTitle', '使用微信扫码进行登陆');
    }
</script>

这个里面的参数,如APPID就是来源我们认证后的开放平台参数。

这些信息我们在MVC控制器后面获取后绑定在ViewBag,方便界面前端的使用。

//使用JSLogin登陆
WebAppInfo appInfo = BLLFactory<WebApp>.Instance.FindByID(ConfigData.WebAppId);
ArgumentValidation.CheckForNullReference(appInfo, "Web应用程序appInfo");

if (appInfo != null)
{
    ViewBag.appid = appInfo.OpenAppID;
    ViewBag.redirect_uri = appInfo.LoginCallBackUrl;
    ViewBag.state = ConfigData.AuthState;
}

其中的redirect_uri是通过数据库获取的LoginCallBackUrl地址,这个地址类似如下格式:http://www.iqidi.com/H5/callback?uid=iqidiSoftware
也就是我们在开放平台处理返回后进行的回调处理。
通过开放平台的APPID和APPSecret,我们可以获取到对应的接口调用凭证,然后根据接口凭证,以及openid,获得用户的公众平台统一的UnionID,这个标识是我们用户的唯一标识,代码如下所示。

var result = baseApi.GetAuthToken(appid, appsecret, code);
if (result != null && !string.IsNullOrEmpty(result.openid))
{
    openid = result.openid;
    var unionResult = baseApi.GetSnsapiUserInfo(result.access_token, result.openid);

    ht.Add("openid", openid);
    ht.Add("unionid", unionResult != null ? unionResult.unionid : "");
}

有了unionid我们就可以根据这个标识在我们的用户数据库里面查找对应的用户,如下代码所示。

//开放平台的OpenID,不是公众号的OpenID,需要转换为unionid
if (!string.IsNullOrEmpty(openid) && !string.IsNullOrEmpty(unionid))
{
    UserInfo userInfo = BLLFactory<User>.Instance.FindByUnionId(unionid);

然后判断我们去到的用户信息是否正确,如下代码所示

if (userInfo != null)
{
    CommonResult loginResult = CheckLogin(userInfo.Name);
    if (!loginResult.Success)
    {
        LogHelper.Info(string.Format("用户登陆不成功,{0}", loginResult.ErrorMessage));
    }

    //登陆成功后的重定向地址
    var url = appInfo.HomeUrl;  //例如:http://www.iqidi.com/Home
    return Redirect(url);
}

如果不成功,那么我们定向到指定的界面即可。

//如不成功,最后都统一提示信息
ViewBag.Error = "获取信息失败,登陆错误";
return View("LoginError");

如果我们登陆成功后,需要设置一些Session信息或者Cookie信息,那么就可以通过CheckLogin函数进行处理即可。
以上就是我们结合微信开放平台实现微信扫码登录的过程,其中整个过程就是用到了下面几个过程。
1)使用JSSDK的脚本实现扫码获取code
JS微信登录主要用途:网站希望用户在网站内就能完成登录,无需跳转到微信域下登录后再返回,提升微信登录的流畅性与成功率。 网站内嵌二维码微信登录JS实现办法:
步骤1:在页面中先引入如下JS文件(支持https):

<script src="http://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js"></script>

步骤2:在需要使用微信登录的地方实例以下JS对象:

var obj = new WxLogin({
  id:"login_container", 
  appid: "", 
  scope: "", 
  redirect_uri: "",
  state: "",
  style: "",
  href: ""
});
  1. 第二步:通过code获取access_token
    通过code获取access_token

https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

3)第三步:通过access_token调用接口
获取access_token后,进行接口调用,
对于接口作用域(scope),能调用的接口有以下:


  1. 第二步:通过code获取access_token
    通过code获取access_token

https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

3)第三步:通过access_token调用接口
获取access_token后,进行接口调用,
对于接口作用域(scope),能调用的接口有以下:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容