引言:
-
由于我第一次做这种
OAuth
,有人要我看官方文档,但是我发现我连基本流程都不知道,所以我认为应该从流程看起,再到官网文档。 -
我推荐的是看文章顺序是:
-
看完以上文章后,可以得到流程如下:
- 获取凭据 :登录谷歌控制台,配置 Google Cloud 项目和应用程序,获取凭据(
ClienId
,ClientSecret
)。 - 获取授权URL并请求:携带参数get请求。
- 重定向到回调地址 :进入谷歌授权界面,Google 提示用户同意,若正确授权后重定向到回调地址。
- 获取
Access_Token
:交换授权代码以刷新令牌和访问令牌。 - 获取用户资料:通过Access_Token调用谷歌api,请求访问或修改用户授权的资源。
- 查库?不注册:注册:查询gmail和sub字段的值是否存在数据库,若存在就注册用户再查询,若存在直接颁发JWT的token。
- 获取凭据 :登录谷歌控制台,配置 Google Cloud 项目和应用程序,获取凭据(
思路实现:
- 其实到现在思路差不多按照流程出来了,谷歌文档的缺点就是在这:内容很全,但是需要你东拼西凑。所以前端需要做什么呢?后端又需要做什么呢?怎么应用到代码是最为关键的。
- 获取凭据 :具体操作可参考此文 - [OAuth 2.0(cnblogs.com)],一直到下载json数据为止。(www.cnblogs.com/TRY0929/p/1...)
- 所以前端要做的工作:
-
获取授权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 并且写入
//其他操作 重定向等等!
}
}
```
- 后端要做的工作: - 获取到前端的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 });
}
}
```