Skip to content
.NET 开发者指北.NET 开发者指北
CMS
.NET指北
FreeKit
Docker
关于
博客
github icon
    • 开发起步
      • Newtonsoft.Json基础问题
        • 依赖注入scrutor
          • 认证与授权
            • 获取控制器及方法特性标签
              • 认证鉴权状态
                • 全局敏感词处理
                  • GitHub第三方授权登录
                    • QQ第三方授权登录
                      • 安装包
                        • 接口介绍
                          • 使用Authorization_Code获取Access_Token
                            • 获取用户OpenID_OAuth2.0
                              • 获取用户的其他信息
                                • 代码
                                  • lin_user表
                                    • lin_user_identity表
                                    • 项目源码
                                    • CAP实现EventBus
                                      • Scriban 模板语言

                                      QQ第三方授权登录

                                      calendar icon2020年5月13日timer icon大约 4 分钟word icon约 1330 字

                                      此页内容
                                      • 安装包
                                      • 接口介绍
                                      • 使用Authorization_Code获取Access_Token
                                      • 获取用户OpenID_OAuth2.0
                                      • 获取用户的其他信息
                                      • 代码
                                        • lin_user表
                                        • lin_user_identity表
                                      • 项目源码

                                      # QQ第三方授权登录

                                      # 安装包

                                      dotnet add package AspNet.Security.OAuth.QQ
                                      
                                      1
                                      • 接上文GitHub第三方授权登录open in new window

                                      申请过程不介绍了,申请者资料open in new window,个人也是可以申请成功的。

                                      这时候有二个参数就是clientid clientsecret

                                      APP ID:xxxx
                                      APP Key:xxxxxx
                                      
                                      1
                                      2

                                      其中平台信息,这个申请审核通过后,不要修改,千万不要随便修改,一修改就要重新审核。

                                      网站回调域:可以随便修改,并且可以写多个,中间用英文逗号分隔即可。 比如,网站地址填的:https://api.igeekfan.cn,下面如果是localhost,是可以的,但如果是域名,便只能是https://api.igeekfan.cnopen in new window这个域名下的路径。

                                      网站回调域配置,后台是运行在https://localhost:5001open in new window端口上。

                                      https://api.igeekfan.cn/signin-qq;https://localhost:5001/signin-qq
                                      
                                      1

                                      # 接口介绍

                                      server-side模式,是OAuth2.0认证的一种模式,又称Web Server Flow;

                                      image

                                      获取Authorization Code https://graph.qq.com/oauth2.0/authorizeopen in new window

                                      通过Authorization Code获取Access Token https://graph.qq.com/oauth2.0/tokenopen in new window

                                      获取用户OpenID_OAuth2.0 https://graph.qq.com/oauth2.0/meopen in new window

                                      获取用户个人信息 https://graph.qq.com/user/get_user_infoopen in new window

                                      # 使用Authorization_Code获取Access_Tokenopen in new window

                                      接入流程如下:

                                      1. 先获取Authorization Code;
                                      2. 通过Authorization Code获取Access Token

                                      1.Step1:获取Authorization Code

                                      GET

                                      https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=client_id&redirect_uri=https://localhost:5001/signin-qq&state=123abcopen in new window

                                      具体参数可查看官网。

                                      state由用户自己创建一个随机数,以防止CSRF攻击。

                                      如果用户成功登录并授权,则会跳转到指定的回调地址,并在redirect_uri地址后带上Authorization Code和原始的state值。如:

                                      https://localhost:5001/signin-qq?code=B6D497755EACE4635115FC82BE24F280&state=123abcopen in new window

                                      后台先根据state验证是自己发出的请求,判断是否相同,不相同,则代表非本项目发出的授权登录请求。

                                      1. 根据code获取access_token

                                      GET

                                      https://graph.qq.com/oauth2.0/token?grant_type=authorization_code&client_id=client_id&client_secret=client_secret&code=B6D497755EACE4635115FC82BE24F280&redirect_uri=https://localhost:5001/signin-qqopen in new window

                                      这时候你会得到

                                      access_token=1B6E45FA99BA3D6B347713440C9BCEFE&expires_in=7776000&refresh_token=8DB1D48D95C85D3EF593936B8ACE5EE0
                                      
                                      1

                                      # 获取用户OpenID_OAuth2.0

                                      GET

                                      https://graph.qq.com/oauth2.0/me?access_token=1B6E45FA99BA3D6B347713440C9BCEFEopen in new window

                                      openid是此网站上唯一对应用户身份的标识

                                      callback( {"client_id":"101867513","openid":"951560F5C7A5AA9E5E599CF9B4ECFFB2"} );
                                      
                                      1

                                      # 获取用户的其他信息

                                      用户信息

                                      https://graph.qq.com/user/get_user_info?access_token=1B6E45FA99BA3D6B347713440C9BCEFE&oauth_consumer_key=YOUR_APP_ID&openid=951560F5C7A5AA9E5E599CF9B4ECFFB2open in new window

                                      {
                                      "ret": 0, 
                                      "msg": "", 
                                      "is_lost":0, 
                                      "nickname": "、天上有木月OvO", 
                                      "gender": "xxx", 
                                      "gender_type": 1, 
                                      "province": "xxx", 
                                      "city": "xxx", 
                                      "year": "2019", 
                                      "constellation": "", 
                                      "figureurl": "http:\/\/qzapp.qlogo.cn\/qzapp\/101867513\/951560F5C7A5AA9E5E599CF9B4ECFFB2\/30", 
                                      "figureurl_1": "http:\/\/qzapp.qlogo.cn\/qzapp\/101867513\/951560F5C7A5AA9E5E599CF9B4ECFFB2\/50", 
                                      "figureurl_2": "http:\/\/qzapp.qlogo.cn\/qzapp\/101867513\/951560F5C7A5AA9E5E599CF9B4ECFFB2\/100", 
                                      "figureurl_qq_1": "http://thirdqq.qlogo.cn/g?b=oidb&k=bjXoWmdlu8fk1m80MCkibMg&s=40&t=1559108425", 
                                      "figureurl_qq_2": "http://thirdqq.qlogo.cn/g?b=oidb&k=bjXoWmdlu8fk1m80MCkibMg&s=100&t=1559108425", "figureurl_qq": "http://thirdqq.qlogo.cn/g?b=oidb&k=bjXoWmdlu8fk1m80MCkibMg&s=640&t=1559108425"
                                      }
                                      
                                      1
                                      2
                                      3
                                      4
                                      5
                                      6
                                      7
                                      8
                                      9
                                      10
                                      11
                                      12
                                      13
                                      14
                                      15
                                      16
                                      17

                                      # 代码

                                      services.AddAuthentication(xxx)
                                      .AddGitHub(xxx)
                                      加上AddQQ的配置项
                                      .AddQQ(options =>
                                      {
                                         options.ClientId = Configuration["Authentication:QQ:ClientId"];
                                         options.ClientSecret = Configuration["Authentication:QQ:ClientSecret"];
                                      })
                                      
                                      1
                                      2
                                      3
                                      4
                                      5
                                      6
                                      7
                                      8

                                      appsettings.json中配置项

                                        "Authentication": {
                                         //下面为新增项
                                         "QQ": {
                                           "ClientId": "xx",
                                           "ClientSecret": "xxx"
                                         }
                                       }
                                      
                                      1
                                      2
                                      3
                                      4
                                      5
                                      6
                                      7

                                      对,没错,QQ登录,已经结束了。接下来就是把这些用户的信息保存到数据库,生成token的过程。

                                      这里

                                      [HttpGet("signin-callback")]
                                      public async Task<IActionResult> Home(string provider, string redirectUrl = "")
                                      {
                                      
                                         AuthenticateResult authenticateResult = await _contextAccessor.HttpContext.AuthenticateAsync(provider);
                                         if (!authenticateResult.Succeeded) return Redirect(redirectUrl);
                                         var openIdClaim = authenticateResult.Principal.FindFirst(ClaimTypes.NameIdentifier);
                                         if (openIdClaim == null || string.IsNullOrWhiteSpace(openIdClaim.Value))
                                             return Redirect(redirectUrl);
                                             
                                             
                                         ClaimsPrincipal principal=authenticateResult.Principal;
                                         //根据provider,处理用户的基础信息,
                                         
                                         long id =SaveQQAsync(principal, openIdClaim.Value)
                                         
                                         //xxx
                                         
                                      }       
                                      
                                      1
                                      2
                                      3
                                      4
                                      5
                                      6
                                      7
                                      8
                                      9
                                      10
                                      11
                                      12
                                      13
                                      14
                                      15
                                      16
                                      17
                                      18
                                      19

                                      openIdClaimopenIdClaim是唯一值

                                      # lin_user表

                                      字段类型备注
                                      Idlong主键
                                      Usernamevarchar(50)用户名
                                      Avatarvarchar(50)头像

                                      # lin_user_identity表

                                      用户授权信息表,用于存储不同登录类型的用户信息,如手机号、邮件、用户名、第三方应用(微信、QQ、GitHub)的登录

                                      字段类型备注
                                      Idlong主键
                                      IdentityTypevarchar(50)认证类型,如 Password,GitHub、QQ、WeiXin等
                                      Identifiervarchar(24)认证者,例如 用户名(PassWord认证类型),授权得到的昵称(QQ),授权得到的用户名(唯一,GitHub)
                                      Credentialvarchar(50)凭证,例如 密码,存OpenId、Id,同一IdentityType的OpenId的值是唯一的
                                      CreateUserIdlong绑定的用户Id

                                      根据openId,判断lin_user_identity表中是否存在这一第三方授权信息,如果存在,则返回当前用户lin_user表中的id,如果不存在,则创建一个新的用户信息,插入lin_user、lin_user_identity表中。

                                      
                                      public async Task<long> SaveQQAsync(ClaimsPrincipal principal, string openId)
                                      {
                                         string nickname = principal.FindFirst(ClaimTypes.Name)?.Value;
                                         string gender = principal.FindFirst(ClaimTypes.Gender)?.Value;
                                         string picture = principal.FindFirst(QQAuthenticationConstants.Claims.PictureUrl)?.Value;
                                         string picture_medium = principal.FindFirst(QQAuthenticationConstants.Claims.PictureMediumUrl)?.Value;
                                         string picture_full = principal.FindFirst(QQAuthenticationConstants.Claims.PictureFullUrl)?.Value;
                                         string avatar = principal.FindFirst(QQAuthenticationConstants.Claims.AvatarUrl)?.Value;
                                         string avatar_full = principal.FindFirst(QQAuthenticationConstants.Claims.AvatarFullUrl)?.Value;
                                         
                                          Expression<Func<LinUserIdentity, bool>> expression = r => 
                                                     r.IdentityType == LinUserIdentity.QQ&& r.Credential == openId;
                                      
                                         LinUserIdentity linUserIdentity =await _userIdentityRepository.Where(expression).FirstAsync();
                                      
                                         long userId = 0;
                                         if (linUserIdentity == null)
                                         {
                                             LinUser user = new LinUser
                                             {
                                                 Avatar = avatar_full,
                                                 Nickname = nickname,
                                                 Username = "",
                                                 LinUserIdentitys = new List<LinUserIdentity>()
                                                 {
                                                     new LinUserIdentity
                                                     {
                                                         CreateTime = DateTime.Now,
                                                         Credential = openId,
                                                         IdentityType = LinUserIdentity.GitHub,
                                                         Identifier = nickname,
                                                     }
                                                 }
                                             };
                                             await _userRepository.InsertAsync(user);
                                             userId = user.Id;
                                         }
                                         else
                                         {
                                             userId = linUserIdentity.CreateUserId;
                                         }
                                         return userId;
                                      }
                                      
                                      
                                      1
                                      2
                                      3
                                      4
                                      5
                                      6
                                      7
                                      8
                                      9
                                      10
                                      11
                                      12
                                      13
                                      14
                                      15
                                      16
                                      17
                                      18
                                      19
                                      20
                                      21
                                      22
                                      23
                                      24
                                      25
                                      26
                                      27
                                      28
                                      29
                                      30
                                      31
                                      32
                                      33
                                      34
                                      35
                                      36
                                      37
                                      38
                                      39
                                      40
                                      41
                                      42
                                      43
                                      44
                                      45

                                      上文中的CreateToken,直接将 authenticateResult.Principal.Claims.ToList(),生成token值,会缺少一些系统需要的值,比如键为ClaimTypes.NameIdentifier,应为用户的id,用户的其他信息,如角色/分组,昵称。不同平台的授权登录,键有所不同,所以这里需要二次处理。

                                      [HttpGet("signin-callback")]
                                      public async Task<IActionResult> Home(string provider, string redirectUrl = "")
                                      {
                                       
                                          //xxx
                                              
                                              
                                          ClaimsPrincipal principal=authenticateResult.Principal;
                                          
                                          List<Claim> authClaims = principal.Claims.ToList();
                                          
                                          long id =SaveQQAsync(principal, openIdClaim.Value)
                                          
                                          LinUser user =await _userRepository.Select.IncludeMany(r => r.LinGroups)
                                              .WhereCascade(r => r.IsDeleted == false).Where(r => r.Id == id).FirstAsync();
                                      
                                          List<Claim> claims = new List<Claim>()
                                          {
                                              new Claim(ClaimTypes.NameIdentifier,user.Id.ToString()),
                                              new Claim(ClaimTypes.GivenName,user.Nickname??""),
                                              new Claim(ClaimTypes.Name,user.Username??""),
                                          };
                                              
                                          user.LinGroups?.ForEach(r =>
                                          {
                                              claims.Add(new Claim(LinCmsClaimTypes.Groups, r.Id.ToString()));
                                          });
                                      
                                          claims.AddRange(authClaims);
                                          string token = this.CreateToken(claims);
                                          return Redirect($"{redirectUrl}?token={token}#login-result");
                                       }       
                                      
                                      1
                                      2
                                      3
                                      4
                                      5
                                      6
                                      7
                                      8
                                      9
                                      10
                                      11
                                      12
                                      13
                                      14
                                      15
                                      16
                                      17
                                      18
                                      19
                                      20
                                      21
                                      22
                                      23
                                      24
                                      25
                                      26
                                      27
                                      28
                                      29
                                      30
                                      31
                                      32

                                      前台login-result路由,解析到token值,并保存起来,与用户密码登录后的流程相同。

                                      # 项目源码open in new window

                                      edit icon在 GitHub 上编辑此页open in new window
                                      上次编辑于: 2023/3/12 13:50:00
                                      贡献者: igeekfan,luoyunchong
                                      上一页
                                      GitHub第三方授权登录
                                      下一页
                                      CAP实现EventBus
                                      MIT Licensed | Copyright © 2021-present luoyunchong
                                      苏ICP备16046457号-1

                                      该应用可以安装在你的 PC 或移动设备上。这将使该 Web 应用程序外观和行为与其他应用程序相同。它将在出现在应用程序列表中,并可以固定到主屏幕,开始菜单或任务栏。此 Web 应用程序还将能够与其他应用程序和你的操作系统安全地进行交互。

                                      详情