C# vue 实现Google Oauth2.0授权

引言:

  1. 由于我第一次做这种OAuth,有人要我看官方文档,但是我发现我连基本流程都不知道,所以我认为应该从流程看起,再到官网文档。

  2. 我推荐的是看文章顺序是:

  3. 看完以上文章后,可以得到流程如下:

    1. 获取凭据 :登录谷歌控制台,配置 Google Cloud 项目和应用程序,获取凭据(ClienIdClientSecret)。
    2. 获取授权URL并请求:携带参数get请求。
    3. 重定向到回调地址 :进入谷歌授权界面,Google 提示用户同意,若正确授权后重定向到回调地址。
    4. 获取Access_Token:交换授权代码以刷新令牌和访问令牌。
    5. 获取用户资料:通过Access_Token调用谷歌api,请求访问或修改用户授权的资源。
    6. 查库?不注册:注册:查询gmail和sub字段的值是否存在数据库,若存在就注册用户再查询,若存在直接颁发JWT的token。

思路实现:

  1. 其实到现在思路差不多按照流程出来了,谷歌文档的缺点就是在这:内容很全,但是需要你东拼西凑。所以前端需要做什么呢?后端又需要做什么呢?怎么应用到代码是最为关键的。
  2. 获取凭据 :具体操作可参考此文 - [OAuth 2.0(cnblogs.com)],一直到下载json数据为止。(www.cnblogs.com/TRY0929/p/1...)
  3. 所以前端要做的工作:
    • 获取授权URL并请求.

      js 复制代码
      请求方法:Get 接口地址: https://accounts.google.com/o/oauth2/v2/auth
      必填参数:    
      client_id:google获取的client_id   
      redirect_uri:google获取的回调url 
      response_type:web授权固定填code (回调函数有code)   
      scope:后台配置的scope,如果有多个,要用空格隔开

代码实现: const clientId ="xxxxxxxxxxxxxxxx.apps.googleusercontent.com"; const redirectUri = "http://localhost:9527/login"; const scope = "www.googleapis.com/auth/userin... www.googleapis.com/auth/userin..."; const responseType = "code"; const authUrl = https://accounts.google.com/o/oauth2/v2/auth?client_id=${clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=${responseType};

window.location.href = authUrl; // 重定向到Google认证页面 - 重定向到回调地址 ![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cae307412f8f41c9976809c13ab9bb3f~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=672&h=646&s=34308&e=png&b=ffffff) 点击授权同意后: ![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f83fbd0b939a40429a3bd5857e2c63da~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1635&h=44&s=13298&e=png&b=ebeef0) http://localhost:9527/login? code=GOOGLE_RESPONSE_CODE&scope=YOUR_SCOPE&authuser=0&prompt=consent code是谷歌生成的,用于换取token scope是你传的scope(所以只有code最重要)

javascript 复制代码
    ```
- 获取`Access_Token`
   
    ```js
    请求方法:POST 接口地址:https://oauth2.googleapis.com/token 
    Content-Type: application/x-www-form-urlencoded

    请求参数:
    code:重定向到回调地址的code
    client_id:google获取的 client_id  
    client_secret:google获取的 client_secret
    redirect_uri:google获取的的回调地址   
    grant_type:authorization_code(固定)

    ---------------------------------------------------------------------
    代码实现:
  createed(){
        const code = this.getParameterByName("code"); //
        if (code) {
            try {
                const response = await axios.post(
                    "https://oauth2.googleapis.com/token", // 用自己的
                    {
                        code: code,
                        client_id:
                            "xxxxxxxxxxxxxxxx.com",
                        client_secret: "xxxxxxxxxxxxxxxx",
                        redirect_uri: "http://localhost:9527/login",
                        grant_type: "authorization_code",
                    }
                )
            }
            } catch (error) {
                // 处理错误
                console.error("Error requesting Google Token:", error);
            }
        }
  },
  methods:{
       // 辅助函数,用于提取URL参数 code
        getParameterByName(name, url = window.location.href) {
            name = name.replace(/[\[\]]/g, "\\$&");
            const regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
                results = regex.exec(url);
            if (!results) return null;
            if (!results[2]) return "";
            return decodeURIComponent(results[2].replace(/\+/g, " "));
        }
  }
    ```
 - 将token传给后端, 交付后端验证获取用户资料。
    
    ```js

                  if (response.data) {
                    const res = await getGoogleMessage({
                   // 将token传给后端, 交付后端验证获取用户资料。
                    RequestToken: response.data.access_token,
                    });
                    if (res.Success === false) {
                        this.$message({
                            type: "error",
                            message: res.Msg,
                        });
                    } else {
                        this.$message.success(this.$t("Common.Success"));
                        setToken(res.Data) // 接受后端jwt颁发的token 并且写入
                      //其他操作 重定向等等!
                    }
                }

    ```
 
  1. 后端要做的工作: - 获取到前端的token,c#去调用谷歌的api
csharp 复制代码
    ```c#
       // 接口层
    namespace Rc.IBusiness.Base_Manage
    {
        public interface IGoogleAuthBusiness
        {
            public Task<GooogleUserInfoResponse> ProcessGoogleCallback(string code); // 处理谷歌回调
        }
        public class GooogleUserInfoResponse
        {
            // 谷歌的 必须小写
            public string sub { get; set; }
            public string name { get; set; }
            public string given_name { get; set; }
            public string family_name { get; set; }
            public string picture { get; set; }
            public string email { get; set; }
            public bool email_verified { get; set; }
            public string locale { get; set; }
        }

    }
        ---------------------------------分割线----------------------------------
       // 接口实现层
      public async Task<GooogleUserInfoResponse> ProcessGoogleCallback(string access_token)
      {
          if (access_token != null)
          {
              using (var gClient = new HttpClient())
              {
                  gClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", access_token);
                  var gResponse = await gClient.GetAsync("https://www.googleapis.com/oauth2/v3/userinfo");

                  if (gResponse.IsSuccessStatusCode)
                  {
                      var gResult = await gResponse.Content.ReadAsStringAsync();

                      var userInfoData = System.Text.Json.JsonSerializer.Deserialize<GooogleUserInfoResponse>(gResult); 

                      return userInfoData;
                  }
                  else
                  {
                      // 请求失败的情况
                      var errorResponse = await gResponse.Content.ReadAsStringAsync();
                      // 抛出异常,由调用方处理
                      throw new Exception("Failed to retrieve Google user information. Details: " + errorResponse);
                  }
              }
          }
          return null;
      }

    ```
-   查库?不注册:注册
 
    ```c#
       /// <summary>
     /// 谷歌登录
     /// </summary>
     /// <param name="request"></param>
     /// <returns></returns>
     [HttpPost]
     [AllowAnonymous]
     public async Task<IActionResult> SignInGoogle(GoogleCallbackRequest request)
     {
         try
         {
             GooogleUserInfoResponse userMsg = await _googleAuthBusiness.ProcessGoogleCallback(request.RequestToken); 

             // 查询userMsg的sub是否存在数据库 存在就直接授权jwt
             LoginGoogleInputDto input = new LoginGoogleInputDto
             {
                 UserName = userMsg.name,
                 Sub = userMsg.sub
             };
             var user = await _homeBus.SubmitLoginAsync(input);

             if (user == null)
             {
                 // 如果不存在数据库,就注册一个账号
                 await _baseUserBusiness.RegisterDataAsync(userMsg); // 谷歌注册 重载

                 // 重新查询用户
                 user = await _homeBus.SubmitLoginAsync(input);
             }
             if (user.EnableState == 0)
             {
                 //账号已禁用
                 throw new BusException("Account is disabled");
             }
             CurrentUserModel currentUser = new CurrentUserModel()
             {
                 Id = user.Id,
                 RealName = user.RealName
             };
             var claims = new[]
             {
                 new Claim("userId", user.Id),
                 new Claim("RealName", user.RealName)
             };

             //构造token
             var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtOptions.Secret));
             var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
             var jwtToken = new JwtSecurityToken(
                 string.Empty,
                 string.Empty,
                 claims,
                 expires: DateTime.Now.AddHours(_jwtOptions.AccessExpireHours),
                 signingCredentials: credentials);

             var token = new JwtSecurityTokenHandler().WriteToken(jwtToken);

             if (!string.IsNullOrEmpty(token))
             {
                 //自定义日志内容:用户登录
                 var logType = UserLogType.User.ToString();
                 var logContent = $"{currentUser.RealName}LOGIN IN CMS";
                 await _userLogBusiness.AddUserLogAsync(logType, logContent, currentUser);
                 return JsonContent(new AjaxResult<string>(token)
                 {
                     Data = token,
                     Success = true,
                     Msg = "验证成功",
                 }.ToJson());
             }
             else
             {
                 return JsonContent(new AjaxResult<string>("")
                 {
                     Success = false,
                     Msg = "颁发token失败",
                 }.ToJson());
             }
         }
         catch (Exception ex)
         {
             // 处理异常,根据实际情况返回适当的状态码或错误信息
             return BadRequest(new { Error = "Authentication failed", Details = ex.Message });
         }
     }

    ```

 
相关推荐
捂月1 小时前
Spring Boot 深度解析:快速构建高效、现代化的 Web 应用程序
前端·spring boot·后端
瓜牛_gn1 小时前
依赖注入注解
java·后端·spring
Estar.Lee2 小时前
时间操作[取当前北京时间]免费API接口教程
android·网络·后端·网络协议·tcp/ip
喜欢猪猪2 小时前
Django:从入门到精通
后端·python·django
一个小坑货2 小时前
Cargo Rust 的包管理器
开发语言·后端·rust
bluebonnet272 小时前
【Rust练习】22.HashMap
开发语言·后端·rust
uhakadotcom2 小时前
如何实现一个基于CLI终端的AI 聊天机器人?
后端
Iced_Sheep3 小时前
干掉 if else 之策略模式
后端·设计模式
XINGTECODE3 小时前
海盗王集成网关和商城服务端功能golang版
开发语言·后端·golang
程序猿进阶3 小时前
堆外内存泄露排查经历
java·jvm·后端·面试·性能优化·oom·内存泄露