菜鸟怎么用.NetCore实现QQ登录
写这篇文章的目的是因为现在网上找不到比较好的、比较全的、看完一篇文章就可以搞定这个功能的文章,所以我要在这里借鉴和总结一下实现这个QQ登录遇到的坑和问题。
一、首先要知道自己要干嘛
现在我们知道要实现QQ登录,那应该怎么去做呢,当然要先去了解QQ提供给我们的一种登录方式,在这里我找到了QQ互联(QQ互联网址),在这里要吐槽一下腾讯这么大个厂,QQ互联这套系统做的是真**,真难用,我在这里遇到了很多坑,从注册填写申请信息到审核等等,差不多用了半个月的时间,给大家讲一下这里遇到的坑,希望大家可以及时的躲开,不要在这上面浪费时间。
在这里需要审核两次
1:第一次是对你个人开发者信息的审核,需要填写身份证号,上传手持身份证正面的照片(这里要注意,不能使用手机的前摄像头,会有镜像,拍出来身份证上的信息都是反的,腾讯审核不通过),联系地址一定要精确到户,腾讯要求的,我也不知道为啥,反正我写了一个模糊不给通过。
2:第二次是对应用的审核,不管你是网站,还是app,都要在这里创建相对应的应用。这里要注意,网站名称一定要写备案时的网站名称,不然审核不给通过。
创建完成后需要填写网站地址,也就是备案的地址,网站回调地址可以写多个,用分号隔开。主办单位名称,我写到这里的时候就很懵逼,这是个啥意思,随便写了个昵称审核不给通过,后来咨询客服人员才知道这里要写备案时的主办单位,如果是个人肯定是自己的真是姓名,如果是单位就写单位名称。
这些做完之后,审核需要一天时间,静静的等着就行了,如果审核不通过系统会返回原因,根据原因修改就可以了,不明白的还可以咨询腾讯客服(在网站的下方),在这里我又要吐槽下这个互联网站(开发者信息审核的时候遇到的一个坑,审核不通过在这里是看不到原因的,就很莫名其妙,不知道哪里有问题无从下手,还是我多次咨询客服后解决的,后来才知道在另一个网站也能填写,腾讯开放平台网址)
审核通过后,我们就可以看QQ互联这个接口的说明文档了( QQ互联文档地址 ),通过文档我们了解到,QQ登录用了一种协议叫OAuth2.0,这是啥?
原来如此,明白了,那么我咋用,其实文档里写的很清楚啦,这里我就不多说了,再来看看API文档,看了一遍好像有点不太聪明的样子,不过没关系,不会的东西我们百度呀,经过百度一圈后我大致发现,有两种路线可以实现这个QQ登录:第一种是通过访问API地址,完全的手撸代码,在URL里各种拼接参数,请求URL之类的,给个链接地址看两眼https://blog.csdn.net/weixin_30316097/article/details/101068005;还有一种是通过咱们的Nuget组件(这个就很灵性,我喜欢),Microsoft.AspNetCore.Authentication.QQ ,这种方式撸的代码就比较少了,而且也比较高级。下面我们就用这种方式来给大家演示如何实现QQ登录。
二、.NetCore怎么实现QQ登录(这个地方我就照抄“不落阁”的一篇文章 链接)
1.在Web项目中引用Nuget包:Microsoft.AspNetCore.Authentication.QQ,这里要注意了,组件要求2.2以上的框架。
2.在项目的配置文件appsetting.json中添加如下的配置
{
"Authentication": {
"QQ": {
"AppId": "你的AppId",
"AppKey": "你的AppKey"
}
}
}
在这里我发现在他的代码中还需要一块代码,看下图
3.重点来了,需要在Startup.cs文件中注册认证服务,
//注册认证服务
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
//这里填写一些配置信息,默认即可
}) //添加Cookie认证
.AddQQ(qqOptions =>
{
qqOptions.AppId = Configuration["Authentication:QQ:AppId"]; //QQ互联申请的AppId
qqOptions.AppKey = Configuration["Authentication:QQ:AppKey"]; //QQ互联申请的AppKey
qqOptions.CallbackPath = "/user/callback"; //QQ互联回调地址
//自定义认证声明
qqOptions.ClaimActions.MapJsonKey(MyClaimTypes.QQOpenId, "openid");
qqOptions.ClaimActions.MapJsonKey(MyClaimTypes.QQName, "nickname");
qqOptions.ClaimActions.MapJsonKey(MyClaimTypes.QQFigure, "figureurl_qq_1");
qqOptions.ClaimActions.MapJsonKey(MyClaimTypes.QQGender, "gender");
});
来自 <ASP.NET Core第三方认证之QQ登录 - 不落阁>
这块可以直接拿来用,上面的CallbackPath 一定要写上面QQ互联申请时的地址。完全的复制粘贴后我发现MyClaimTypes.QQOpenId没有引用会报错,然后我开始研究这是个什么东西,知道这个参数是啥,就要明白参数外面的方法是啥意思,我研究了半天ClaimActions.MapJsonKey,这个方法的摘要是这么说的:
从具有给定键名的json用户数据中选择一个顶级值,并将其作为声明添加。如果找不到密钥或值为空,则不执行操作。
从而我发现MyClaimTypes.QQOpenId这个的值应该是个字符串,竟然是个字符串的话,那这应该是个静态类,直接引用里面的字段。于是我就创建了MyCalimTypes类。
就很棒,目前来说不报错了。
不要忘了使用认证中间件:在Configure方法添加以下代码。
//使用验证中间件
app.UseAuthentication();
4.真正关键的时刻来了
下面我们就要开始写登录功能了(还是复制粘贴,把别人的拿来用)
创建一个UserController控制器,在UserController中需要写两个Action,一个用来触发QQ登录,一个用来处理登录成功后的逻辑。例如:
public IActionResult Login(string provider = "QQ", string returnUrl = null)
{
//第三方登录成功后跳转的地址
var redirectUrl = Url.Action(nameof(ExternalLoginCallbackAsync), new { returnUrl });
var properties = new AuthenticationProperties()
{
RedirectUri = redirectUrl
};
return Challenge(properties, provider);
}
[Authorize]
public async Task ExternalLoginCallbackAsync(string returnUrl = null)
{
//QQ认证后会默认登录,如果你想自定义登录,可以先注销第三方登录的身份
//await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
string openId = "", name = "", figure = "", gender = "";
//从当前登录用户的身份声明中获取信息(是否有些眼熟,MyClaimTypes就是在Startup里面自定义的那些)
foreach (var item in HttpContext.User.Claims)
{
switch (item.Type)
{
case MyClaimTypes.QQOpenId:
openId = item.Value;
break;
case MyClaimTypes.QQName:
name = item.Value;
break;
case MyClaimTypes.QQFigure:
figure = item.Value;
break;
case MyClaimTypes.QQGender:
gender = item.Value;
break;
default:
break;
}
}
//获取到OpenId后进行登录或者注册(以下作为示范,不要盲目复制粘贴)
if (!openId.IsNullOrEmpty())
{
//去数据库查询该QQ是否绑定用户
User user = await _dbContext.User.Where(s => s.QQOpenId == openId).FirstOrDefaultAsync();
if (user != null)
{
#region 存在则登陆
var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
identity.AddClaim(new Claim(ClaimTypes.Sid, user.Id.ToString()));
identity.AddClaim(new Claim(ClaimTypes.Name, user.Name));
identity.AddClaim(new Claim(MyClaimTypes.Avator, user.Avatar));
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(identity), new AuthenticationProperties
{
IsPersistent = true,
ExpiresUtc = DateTimeOffset.Now.Add(TimeSpan.FromDays(int.Parse(Configuration["AppSettings:LoginExpires"]))) // 有效时间
});
user.LastLoginIP = HttpContext.GetUserIP();
user.LastLoginTime = DateTime.Now;
//更新登录信息
_dbContext.User.Update(user);
await _dbContext.SaveChangesAsync();
#endregion
if (returnUrl != null)
return Redirect(returnUrl);
else
return RedirectToAction("index", "home");
}
else
{
User userModel = new User();
userModel.QQOpenId = openId;
userModel.Name = name;
userModel.Avatar = figure;
userModel.Gender = gender;
userModel.CreateTime = DateTime.Now;
//注册
await _dbContext.User.AddAsync(userModel);
if (await _dbContext.SaveChangesAsync() > 0)
{
#region 注册后自动登陆
var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
identity.AddClaim(new Claim(ClaimTypes.Sid, userModel.Id.ToString()));
identity.AddClaim(new Claim(ClaimTypes.Name, userModel.Name));
identity.AddClaim(new Claim(MyClaimTypes.Avator, userModel.Avatar));
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(identity), new AuthenticationProperties
{
IsPersistent = true,
ExpiresUtc = DateTimeOffset.Now.Add(TimeSpan.FromDays(int.Parse(Configuration["AppSettings:LoginExpires"]))) // 有效时间
});
userModel.LastLoginIP = HttpContext.GetUserIP();
userModel.LastLoginTime = DateTime.Now;
//更新登录信息
_dbContext.User.Update(userModel);
await _dbContext.SaveChangesAsync();
#endregion
if (returnUrl != null)
return Redirect(returnUrl);
else
return RedirectToAction("index", "home");
}
else
throw new Exception("Add User failed");
}
}
else
throw new Exception("OpenId is null");
}
来自 <ASP.NET Core第三方认证之QQ登录 - 不落阁>
这里的代码就比较复杂了,还需要我们耐心研究下,因为这里好多东西我都没有接触过。就比如这块东东:
AuthenticationProperties这是个啥,new了一下,说明是个对象,return Challenge 又是个啥,返回了个啥?咱也没见过,那就百度吧。百度了半天也没找到满意的解释,就去bing一下吧,后来我发现有趣的事情来了,上链接:https://www.cnblogs.com/OpenCoder/p/10310839.html,这是一篇国外的文章,国人翻译的,就有些内容不好懂,这个地方就只能意会了。毕竟我是一只菜鸟。
通过研究发现,大概就是个重定向的东西,一会儿把赋值粘贴的代码的错误都解决了,调试下看看怎么走的。
在这里这坨静态类的字段又出来了,后来发现这个地方不能用静态变量,我又把变量改成了常量const,错误接触,Nice。
接下来就是各种引用,引用完了之后发现还是一堆错,认真的看看代码,这块代码是人家自己插入修改用户的方法,这里可以先删掉,后面用我们自己的就可以了
这块代码大概的逻辑就是,用户登录后,先根据OpenId去库里查看看有没有这个用户,如果没有的话,先把用户的信息存到库里,再把用户的信息写到缓存里并且登录,再把用户当前的登录IP和登录时间记录到库里; 如果有的话,把用户的信息写到缓存里并且登录,接着记录下用户当前的登录IP和登录时间 。这块没啥难的,我们继续。
后台的代码写的差不多了,我们给前台View一个链接,请求我们刚写的这个方法。
4.开始使用
在页面上放置触发QQ登录的按钮,如:
<a href="/user/login?provider=QQ&returnUrl=@returnUrl" >
<i class="fa fa-qq"></i>
</a>
来自 <ASP.NET Core第三方认证之QQ登录 - 不落阁>
returnUrl最好给用户操作的当前页面,这样登录之后还是返回到用户浏览的页面,用户体验比较好,我是这么写的。
5.结语
该案例使用的是微软的认证组件,上文提到的provider需要注意,QQ登录必须写QQ。
微软自身提供了Microsoft账号、Google账号等国外账号的认证组件,用法应该和上述类似,有兴趣的去Github下载asp.net源码看看呗。
此外Microsoft.AspNetCore.Authentication.QQ这个包在Github上也有源码,大家可以看看别人是怎么实现的,也能够知晓provider必须写QQ的原因,顺便自己封装个微博、微信登录也是可以的。
在这里不落阁的文章就结束了,我们也开发的差不多了,调试一把。
点击QQ图标,跳转到QQ的登录页面,就很Nice,我们成功了。
登录后返回到当然页面,应该是登录功能了,用户数据也写到缓存里了,但是页面没有任何变化,那是当然的啦,我们还没有开发呀!
我们可以把登录用户的QQ昵称和头像显示在右上角,就像这样。
嗯~不错,高端大气上档次,上代码。
@if (User.Identity.Name != null)
{
<li class="layui-nav-item">
<a class="fly-nav-avatar" href="~/user/out?returnUrl=@this.Context.Request.Path.Value">
@User.Identity.Name
<cite class="layui-hide-xs">
</cite>
<img src="@User.Claims.Where(p => p.Type == "avator").FirstOrDefault()?.Value">
</a>
<dl class="layui-nav-child">
<dd style="color:#5FB878;text-align:center;"><span>点击头像注销</span></dd>
</dl>
</li>
}
else
{
<li class="layui-nav-item">
<a href="~/user/login?provider=QQ&returnUrl=@this.Context.Request.Path.Value"><i class="layui-icon layui-icon-login-qq" style="font-size:30px;"></i></a>
</li>
}
因为我是个菜鸟,所以我不知道怎么从缓存里拿到用户登录的数据,后来我发现有个User属性,那就试试吧,User里还可以点出Identity,还可以点出Name,那就很完美了,用户的名字我们不就有了吗,那头像呢?咋搞?
不急,我们继续看代码,发现这个Claims很熟悉,因为后台代码里我们把用户的数据通过这种方式写到了缓存里,所以我猜想应该从这里能拿到头像,那就试试。再调试一下。
Nice,我们又成功了。
登录后我发现没有退出啊,上退出代码
public async Task<IActionResult> Out(string returnUrl = null)
{
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
if (returnUrl != null)
return Redirect(returnUrl);
else
return RedirectToAction("index", "home");
}
如果你足够细心的话,前台代码其实刚刚我已经放到上面了,就在这里
最后调试一下,注销功能也做好了。
到这里我们.Net Core框架下实现QQ登录就已经做好了。其实你会发现,解决问题的思路很重要,代码不可能一下子就写完,当我们触碰到新领域的时候,只能跌跌撞撞的不断去摸索,不断去尝试,我也是第一次接触这个QQ登录,但经过我的不懈努力,不也是做出来了,而且自己还会有些许的小成就呢(臭美)。
在这里总结了下遇到的问题,希望可以帮助到大家。