本文作者:小黑黑

[微信]网页授权

小黑黑 1年前 ( 2019-04-07 ) 713 抢沙发
[微信]网页授权摘要:        在前面我们已经知道了用户怎样通过微信公众号的界面进行交互,服务器可以通过MessageHandler获取到RequestMes...

       在前面我们已经知道了用户怎样通过微信公众号的界面进行交互,服务器可以通过MessageHandler获取到RequestMessage,并且能够从RequestMessage中获取到用户的唯一标识:openId。但是实际的项目开发中,大多数的用户交互会在我们开发的网页中进行,用户通过点击按钮,扫描二维码或分享网页都能够打开我们的网页,不是必须通过微信公众号,那么在这种情况下,我们怎样知道是哪个用户的操作呢。又或者是用户同时关注了两个公众号,当用户通过微信分享的方式打开了网址,并且这个网址可能同时服务于两个微信公众号,那么我们怎样知道这个用户是属于哪个公众号的呢?怎样安全的在不泄露用户账号密码的情况下实现用户登录功能呢?OAuth授权是一个不错的选择,在微信开发中,我们可以通过OAuth获取到用户的openId,以及用户的详细信息。

一、什么是OAuth协议

       OAuth协议为用户资源的授权提供了一个安全又简易的标准。与以往的授权方式不同之处是OAuth的授权不会使用第三方触及到用户的账号信息,如:用户名和密码,即第三方无需使用用户名与密码就可以申请获得用户资源的授权,因此OAuth是安全的。

二、OAuth授权的基本流程

636350339987863357.jpg


    A:第三方请求用户授权。

    B:用户同意授权后,会从服务方获取一次性用户授权凭据(如code码)给第三方。

    C:第三方会把授权凭据以及服务方给它的身份凭据(如AppId)一起交给服务方的认证服务器申请访问令牌。

    D:认证服务器核对授权凭据等信息,确认无误后,向第三方发送访问令牌Access Token等信息。

    E:通过Access Token向资源服务器索要数据。

    F:资源服务器使用令牌向认证服务器确认令牌的正确性,确认无误后提供资源。

三、微信公众号中的OAuth回调域名的设置

      我们以微信公众号测试号为例,设置OAuth回调域名,生产环境只需要找到相对应的地方进行设置即可,需要注意的是,生产环境还需要在设置OAuth回调域名的地方下载一个文件,放到指定的位置下,具体请查看相关说明。

G{R7V(UNE4K}{75X@~8(L31.png

WJQD1OQGF$_D80SY`{MG[DU.png

四、OAuth授权的使用

      微信公众号有两种授权模式,参数名称为scope,应用授权作用域,snsapi_base:不弹出授权页面,这届跳转,如果用户未关注公众号,则只能获取到用户的openId信息,不能获取到用户的详细信息,snsapi_userinfo:弹出授权页面,可以获取到用户的详细信息,下面我们将对这两种授权模式进行演示。

/// <summary>
/// OAuth授权
/// </summary>
public class OAuthController : Controller
{
    private static readonly string appId = Config.SenparcWeixinSetting.WeixinAppId;   //AppId
    private static readonly string appSecret = Config.SenparcWeixinSetting.WeixinAppSecret;   //AppSecre

    /// <summary>
    /// 授权引导页
    /// </summary>
    /// <param name="returnUrl">
    /// 用户尝试进入需要的登录的页面
    /// 不携带returnUrl的页面会停留在CallBack页面,此页面如果刷新,会导致code过期的错误。
    /// 携带returnUrl的页面最终会跳转到returnUrl对应页面,避免刷新页面导致code的错误。
    /// </param>
    /// <returns></returns>
    public IActionResult Index(string returnUrl)
    {

        var state = "foxprow-" + Guid.NewGuid().ToString("n");   //随机数,用于识别请求可靠性
        HttpContext.Session.SetString("State", state);

        ViewData["returnUrl"] = returnUrl;

        //弹出用户授权页
        ViewData["UrlUserInfo"] = OAuthApi.GetAuthorizeUrl(appId,
            "http://wechatdemo.natapp1.cc/OAuth/UserInfoCallBack?returnUrl=" + returnUrl.UrlEncode(),
            state, Senparc.Weixin.MP.OAuthScope.snsapi_userinfo);

        //静默授权,不弹出用户授权页
        ViewData["UrlBase"] = OAuthApi.GetAuthorizeUrl(appId,
            "http://wechatdemo.natapp1.cc/OAuth/BaseCallBack?returnUrl=" + returnUrl.UrlEncode(),
            state, Senparc.Weixin.MP.OAuthScope.snsapi_base);

        return View();
    }

    /// <summary>
    /// 弹出授权页,回调地址
    /// </summary>
    /// <param name="code"></param>
    /// <param name="state"></param>
    /// <param name="returnUrl">用户最初尝试进入的页面</param>
    /// <returns></returns>
    public IActionResult UserInfoCallBack(string code, string state, string returnUrl)
    {
        if (string.IsNullOrEmpty(code))
        {
            return Content("您拒绝了授权");
        }

        if (state != HttpContext.Session.GetString("State"))
        {
            return Content("验证失败,请从正规途径进入");
        }

        OAuthAccessTokenResult result = null;

        try
        {
            //通过Code换取Access Token
            result = OAuthApi.GetAccessToken(appId, appSecret, code);
        }
        catch (Exception ex)
        {
            return Content(ex.Message);
        }

        if (result.errcode != ReturnCode.请求成功)
        {
            return Content("错误信息:" + result.errmsg);
        }

        try
        {
            //通过Access Token获取用户信息
            OAuthUserInfo userInfo = OAuthApi.GetUserInfo(result.access_token, result.openid);

            //如果尝试进入的网页URL不为空,则跳转至此网页
            //如果returnUrl为空,则页面会停留在CallBack页,用户刷新时会出现错误,因为code已被使用
            //如果returnUrl不为空,则页面将跳转至此页面,用户刷新时不会出现错误
            if (!string.IsNullOrEmpty(returnUrl))
            {
                return Redirect(returnUrl);
            }

            return View(userInfo);
        }
        catch (Exception ex)
        {
            return Content(ex.Message);
        }
    }

    /// <summary>
    /// 静默授权,回调地址
    /// </summary>
    /// <param name="code"></param>
    /// <param name="state"></param>
    /// <param name="returnUrl"></param>
    /// <returns></returns>
    public IActionResult BaseCallBack(string code, string state, string returnUrl)
    {
        if (string.IsNullOrEmpty(code))
        {
            return Content("您拒绝了授权");
        }

        if (state != HttpContext.Session.GetString("State"))
        {
            return Content("验证失败,请从正规渠道进入");
        }

        OAuthAccessTokenResult result = null;

        try
        {
            //通过code获取Access Token
            result = OAuthApi.GetAccessToken(appId, appSecret, code);
        }
        catch (Exception ex)
        {
            return Content(ex.Message);
        }

        if (result.errcode != ReturnCode.请求成功)
        {
            return Content("错误信息:" + result.errmsg);
        }

        try
        {
            //通过Access Token 获取用户信息,只有当用户关注了微信公众号才能获取到用户的详细信息,否则会出错
            OAuthUserInfo userInfo = OAuthApi.GetUserInfo(result.access_token, result.openid);

            if (!string.IsNullOrEmpty(returnUrl))
            {
                return Redirect(returnUrl);
            }

            return View(userInfo);
        }
        catch (Exception ex)
        {

            return Content("未关注,无法获取到用户详细信息");
        }
    }
}
@{
    ViewData["Title"] = "Index";
}

<h1>OAuth2.0授权测试</h1>

<p>
    <a href="@ViewData["UrlUserInfo"]">点击测试弹出授权页</a>
</p>
<p>
    将要链接到的地址:<br />
    <textarea rows="10" cols="40">@ViewData["UrlUserInfo"]</textarea>
</p>
<p>
    <a href="@ViewData["UrlBase"]">点击测试静默授权</a>
</p>
<p>
    将要链接到的地址:<br />
    <textarea rows="10" cols="40">@ViewData["UrlBase"]</textarea>
</p>
//静默授权,如果用户未关注,则获取不到用户的详细信息,微信将返回“用户未关注公众号”页面

@model Senparc.Weixin.MP.AdvancedAPIs.OAuth.OAuthUserInfo
@{
    Layout = null;
}

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>OAuth2.0授权测试授权成功</title>
</head>
<body>
    <h2>OAuth2.0授权测试授权成功!</h2>
    <p>下面是通过授权得到的您的部分个人信息:</p>
    <p>openid:@Model.openid</p>
    <p>nickname:@Model.nickname</p>
    <p>country:@Model.country</p>
    <p>province:@Model.province</p>
    <p>city:@Model.city</p>
    <p>sex:@Model.sex</p>
    @if (Model.unionid != null)
    {
        <p>unionid:@Model.unionid</p>
    }
    <p>
        头像:<br />
        <img src="@Model.headimgurl" style="width: 50%" />
    </p>
</body>
</html>
//弹出用户授权页面,能够获取到用户的详细信息

@model Senparc.Weixin.MP.AdvancedAPIs.OAuth.OAuthUserInfo
@{
    Layout = null;
}

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>OAuth2.0授权测试授权成功</title>
</head>
<body>
    <h2>OAuth2.0授权测试授权成功!</h2>
    <p>下面是通过授权得到的您的部分个人信息:</p>
    <p>openid:@Model.openid</p>
    <p>nickname:@Model.nickname</p>
    <p>country:@Model.country</p>
    <p>province:@Model.province</p>
    <p>city:@Model.city</p>
    <p>sex:@Model.sex</p>
    @if (Model.unionid != null)
    {
        <p>unionid:@Model.unionid</p>
    }
    <p>
        头像:<br />
        <img src="@Model.headimgurl" style="width: 50%" />
    </p>
</body>
</html>

我们创建菜单时,创建跳转网页按钮,链接到/OAuth/Index页面。我们可以使用微信公众号开发工具进行查看结果。

分享到: 网站分享代码

发表评论

快捷回复:

评论列表 (暂无评论,713人围观)参与讨论

还没有评论,来说两句吧...