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 });
         }
     }

    ```

 
相关推荐
间彧38 分钟前
Windows Server,如何使用WSFC+nginx实现集群故障转移
后端
间彧43 分钟前
Nginx + Keepalived 实现高可用集群(Linux下)
后端
间彧44 分钟前
在Kubernetes中如何部署高可用的Nginx Ingress Controller?
后端
间彧1 小时前
Ribbon负载均衡器和Nginx负载均衡器有什么区别
后端
间彧1 小时前
Nacos详解与项目实战
后端
间彧1 小时前
nginx、网关Gateway、Nacos、多个服务实例之间的数据链路详解
后端
间彧1 小时前
Nacos与Eureka在性能上有哪些具体差异?
后端
间彧1 小时前
详解Nacos健康状态监测机制
后端
间彧1 小时前
如何利用Nacos实现配置的灰度发布?
后端
毕业设计制作和分享1 小时前
springboot159基于springboot框架开发的景区民宿预约系统的设计与实现
java·spring boot·后端