大家在自己做项目的时候有没有想过实现一个第三方应用登录呢?类似这种:
本篇文章就来聊一聊该如何实现第三方应用登录。
什么是OAuth2.0
OAuth是一项协议,它为用户资源的授权提供了一个安全、开放而简易的标准,OAuth的授权不会使第三方触及到用户的账号信息(比如密码),因此OAuth是相对安全的。而OAuth2.0就是OAuth的延续,不过2.0更加关注客户端开发者的简易性。
申请网站接入
常见的第三方应用都支持第三方登录,比如:QQ、微信、微博、GitHub、Gitee等,要想申请第三方登录权限,就需要去到对应的平台,比如QQ,搜索QQ开放平台:
进入应用管理,并创建应用即可:
不过对于QQ、微信、微博等的网站接入都需要身份认证,过程比较繁琐,所以我们使用Gitee作为第三方应用进行接入。
首先打开Gitee,选择设置:
在设置中选择第三方应用:
点击创建应用并填写相关信息:
应用名称可以随意填写,但是下面的两个地址就有用途了,对于应用主页,它需要填写的是当前应用的主页,而应用回调接口填写的是当登录成功后需要跳转的页面。
创建SpringBoot应用
申请完成后,就可以来创建SpringBoot应用,并新建一个index.html页面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form>
用户名:<input type="text"/><br/>
密码:<input type="password"/><br/>
<a href="">Gitee登录</a>
<input type="submit" value="登录"/><br/>
</form>
</body>
</html>
效果如下:
页面很丑,但不重要,实现功能即可。
现在我们的需求是点击Gitee登录链接,会跳转至一个第三方的登录页面,就像这样:
那么该如何实现呢?
回到Gitee中,在创建完应用后,将页面拖动到下方,会看到模拟请求按钮:
点击一下该按钮,会进行一个模拟登录:
地址栏中的地址就是我们需要跳转的页面,将地址复制下来:
https://gitee.com/oauth/authorize?client_id=52908197466cd3008db76a6018de66c8d222656056fa78b26dd58d1f4fa0a606&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Fsuccess&response_type=code
里面总共有3个参数,分别是:
- client_id
- redirect_uri
- response_type
其中client_id在创建完应用之后便会提供给我们:
也就是地址上的内容,而redirect_uri是在点击了同意授权按钮之后需要跳转的页面,即:登录成功之后,需要跳转至成功后的页面,最后是response_type,它表示响应类型为一个授权码。
来了解一下授权码的作用,当用户点击了同意授权按钮之后,Gitee服务器会给redirect_uri指向的页面传递一个授权码,此时就可以接收到这个授权码,再去换取AccessToken,只有获取到了AccessToken,才能够获取到Gitee中当前用户的某些信息。
点击了同意授权按钮后:
得到了授权码后,通过该地址能够换取AccessToken:
https://gitee.com/oauth/token?grant_type=authorization_code&code={code}&client_id={client_id}&redirect_uri={redirect_uri}&client_secret={client_secret}
该地址要求以Post形式请求,使用PostMan测试一下,将参数按要求设置好:
得到相应结果:
{
"access_token": "cd2c33c3fe548a23188159f87da70110",
"token_type": "bearer",
"expires_in": 86400,
"refresh_token": "c95a38ab2357638ffc4dc6f09c623f2333e0930a37dec8e2f191a40d7afd3514",
"scope": "user_info",
"created_at": 1627974370
}
得到了AccessToken之后,就可以获取到用户在Gitee中已经授权的任何信息了。
整个授权认证的过程如下图所示:
实现登录流程
熟悉了整合流程之后,我们用代码来实现一下,首先修改页面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form>
用户名:<input type="text"/><br/>
密码:<input type="password"/><br/>
<a href="https://gitee.com/oauth/authorize?client_id=52908197466cd3008db76a6018de66c8d222656056fa78b26dd58d1f4fa0a606&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Fsuccess&response_type=code">Gitee登录</a>
<input type="submit" value="登录"/><br/>
</form>
</body>
</html>
现在超链接的地址经过修改后就能成功跳转至Gitee授权页面了,因为授权成功后跳转的地址为 http://localhost:8080/success
,所以需要来处理一下这个请求,创建一个控制器:
@Controller
public class LoginController {
@GetMapping("/success")
public String login(@RequestParam("code") String code){
System.out.println(code);
return "success";
}
@GetMapping("/")
public String index(){
return "index";
}
}
当授权成功后我们让其跳转至success.html页面,并接收Gitee传递过来的授权码,所以创建success.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>登录成功!</h1>
</body>
</html>
启动项目测试一下:
点击同意授权后确实登录成功了,控制台也输出了授权码:
e907fd92d8392ebcd72dff321da45115ff0fba2dec0e6918b233ec7d03b76e5d
其实到这里登录流程还没有结束,因为如果使用第三方应用登录的话,平台会使用第三方应用的一些信息,比如账号、头像等直接作为当前平台的登录名和头像,所以我们还需要获取用户在Gitee中的一些信息。
此处参考Gitee的API文档:
比如获取授权用户的资料信息:
它需要传递一个access_token作为参数,那我们要做的就是使用授权码去得到access_key:
@Controller
public class LoginController {
@GetMapping("/success")
public String login(@RequestParam("code") String code, Map<String,String> map) {
// 获取accesskey
String accessKey = getAccessKey(code);
System.out.println(accessKey);
// 通过accessKey获取用户信息
String userInfo = getUserInfo(accessKey);
// 取出用户名
String name = (String) JSONObject.parseObject(userInfo).get("name");
// 取出头像
String avatar_url = (String) JSONObject.parseObject(userInfo).get("avatar_url");
// 放入请求域
map.put("name",name);
map.put("avatar_url",avatar_url);
return "success";
}
/**
* 获取用户信息
*/
private String getUserInfo(String accessKey) {
String json = "";
OkHttpClient client = new OkHttpClient();
// 通过该地址能够获取到用户信息
String url = "https://gitee.com/api/v5/user?access_token=" + accessKey;
Request request = new Request.Builder()
.get()
.url(url).build();
try {
Response response = client.newCall(request).execute();
json = response.body().string();
} catch (IOException e) {
e.printStackTrace();
}
return json;
}
/**
* 获取AccessKey
*/
private String getAccessKey(String code) {
OkHttpClient client = new OkHttpClient();
// 通过该地址能够获取到access_token
String url = "https://gitee.com/oauth/token";
// 封装请求参数
RequestBody requestBody = new FormBody.Builder()
.add("grant_type", "authorization_code")
.add("code", code)
.add("client_id", "52908197466cd3008db76a6018de66c8d222656056fa78b26dd58d1f4fa0a606")
.add("redirect_uri", "http://localhost:8080/success")
.add("client_secret", "7e84401a9752e88d22d5450c1687ca6a19bc34f45fe3452cefd33312d8153978")
.build();
Request request = new Request.Builder()
.post(requestBody)
.url(url).build();
String accessKey = "";
try {
Response response = client.newCall(request).execute();
String json = response.body().string();
// 获取json串中的access_token属性
accessKey = (String) JSONObject.parseObject(json).get("access_token");
} catch (IOException e) {
e.printStackTrace();
}
return accessKey;
}
@GetMapping("/")
public String index() {
return "index";
}
}
success.html页面需要显示用户信息:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>登录成功!</h1>
<h1>用户名:<span th:text="${#request.getAttribute('name')}"></span></h1>
<img th:src="${#request.getAttribute('avatar_url')}">
</body>
</html>
效果如下:
事实上,登录流程远没有这么简单,当用户取消授权时就不能让其登录,程序里还没有加上这类判断,对于从未注册过的用户来说,这次登录就相当于一次注册,所以还需要以Gitee中用户的某些信息作为注册信息进行登录,当已经注册过的用户使用第三方登录时,要让其正常登录。