什么是社交登录?
QQ、微博、github等网站的用户量非常大,别的网站为了简化自我网站的登陆与注册逻辑,引入社交登陆功能:
步骤:
- 用户点击QQ按钮
- 引导跳转到QQ授权页
- 用户主动点击授权,跳回之前网页
什么是OAuth2.0
OAuth2.0是一个开放标准,允许用户授权第三方网站访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方网站或分享他们数据的所有内容。
对于用户相关的OpenAPI(例如获取用户信息,动态同步,照片,日志,分享等),为了保护用户数据的安全和隐私,第三方网站访问用户数据前都需要显式的向用户征求授权。
授权流程:
(1)用户打开客户端以后,客户端要求用户给予授权。
(2)用户同意给予客户端授权。
(3)客户端使用上一步获得的授权,向认证服务器申请令牌。
(4)认证服务器对客户端进行认证以后,确认无误,同意发放令牌。
(5)客户端使用令牌,向资源服务器申请获取资源。
(6)资源服务器确认令牌无误,同意向客户端开放资源。
使用Gitee为例,实现社交登录
一. Gitee登录准备工作
二. 使用授权码模式,实现Gitee授权
client指第三方应用(本地项目),Resource Owner指用户,Authonzation Server指授权服务器(gitee,微博),Resource Server指API服务器。授权流程如图:
1. Client向用户申请请求认证(本地项目跳转到Gitee登录界面),Gitee登录按钮的链接是下方的 URL:
<a href="https://gitee.com/oauth/authorize?client_id=585a1bf1ad0f40e534dd4a3dde62a947c5b4e9e734d71eafbb071cfde96d8ee5&redirect_uri=http://localhost:8152/oauth2.0/gitee/success">
</a>
URL 中至少包含以下参数:
(1)client_id:在Gitee开放平台申请的应用 ID
(2)redirect_uri:授权成功后要跳转到的地址
2. 用户授权,点击以上链接后跳转到Gitee的授权页面如下图:
3. 用户使用上步授权,向授权服务器(Gitee)进行认证
实际上这里就是用户输入自己的账号、密码,向Gitee进行登录。
4. 权服务器(Gitee)认证通过,返回访问令牌给Client
页面自动跳转到初始参数中redirect_uri 定义的那个URL,并自动在 URL 末尾添加一个 code 参数,地址形如:
http://localhost:8152/oauth2.0/gitee/success?code=e05ddd8fd73dc65cd505158457cd115067536300d99d703344dcbcc4f2e7ccdf
5. Client使用访问令牌,向API服务器获取开放保护信息
通过上一步获取的 code 参数换取 Token,Token 就是前文中说到的钥匙。请求如下的接口获取 Token:
@GetMapping(value = "/oauth2.0/gitee/success")
public String gitee(@RequestParam("code") String code, HttpSession session) throws Exception {
Map<String, String> map = new HashMap<>();
map.put("client_id", "585a1bf1ad0f40e534dd4a3dde62a947c5b4e9e734d71eafbb071cfde96d8ee5");
map.put("client_secret", "b4a76312fb6ffd951421ba6860118f89ee93d8ddc4e1ad2b91087e1ac6e199b0");
map.put("grant_type", "authorization_code");
map.put("redirect_uri", "http://localhost:8152/oauth2.0/gitee/success");
map.put("code", code);
//1、根据用户授权返回的code换取access_token
HttpResponse response = HttpUtils.doPost("https://gitee.com", "/oauth/token", "post", new HashMap<>(), map, new HashMap<>());
//2、处理
if (response.getStatusLine().getStatusCode() == 200) {
// todo 授权成功处理
} else {
// todo 授权失败处理
}
}
要包含以下参数:
(1)client_id:在Gitee开放平台申请的应用 ID
(2)client_secret:在Gitee开放平台申请时提供的APP Secret
(3)grant_type:需要填写authorization_code
(4)code:上一步获得的 code
(5)redirect_uri:回调地址,需要与注册应用里的回调地址以及第一步的 redirect_uri 参数一致
注:获取token十分重要,采用POST方式比较安全。
6. API服务器认证令牌,返回Client受保护信息
在第四步中获取了access_token ,使用它,就可以去获取用户的资源了,要获取用户昵称和头像,请求如下接口:
public MemberEntity login(SocialUser socialUser) throws Exception {
//具有登录和注册逻辑
String accessToken = socialUser.getAccess_token();
//1、判断当前社交用户是否已经登录过系统
MemberEntity memberEntity = this.baseMapper.selectOne(new QueryWrapper<MemberEntity>().eq("access_token", accessToken));
if (memberEntity != null) {
// todo 这个用户已经注册过
} else {
//2、没有查到当前社交用户对应的记录我们就需要注册一个
MemberEntity register = new MemberEntity();
//3、查询当前社交用户的社交账号信息(昵称、性别等)
Map<String,String> query = new HashMap<>();
query.put("access_token",socialUser.getAccess_token());
HttpResponse response = HttpUtils.doGet("https://gitee.com", "/api/v5/user", "get", new HashMap<String, String>(), query);
if (response.getStatusLine().getStatusCode() == 200) {
//查询成功
String json = EntityUtils.toString(response.getEntity());
JSONObject jsonObject = JSON.parseObject(json);
String name = jsonObject.getString("name");
String id = jsonObject.getString("id");
String profileImageUrl = jsonObject.getString("avatar_url");
// todo 把用户信息插入到数据库中
}
}
}
携带参数:
(1)access_token:上一步获取的access_token
(2)uid:用户的账号 id,上一步的接口有返回
注:access_token使用时间有限
至此Gitee登陆调试完成。
总结
OAuth2.0 中最常用的当属授权码模式,也就是我们上文讲述的实现,除此之外还有简化模式、密码模式、客户端模式等,由于其他三种模式并不常用,这里不做演示。
为什么用户在第三方认证完成后使用返回的 Code 换取 Token,而不是直接使用 Code 进行后续的步骤呢?
- 首先当然是安全,一般 Code 只能兑换一次 token,如果你获取 Code 后,无法授权,则系统自然会发现被黑客攻击了,会重新授权,那么之前的 token 就无效了。
- 其次还是为了安全,Code 是服务端生成的,防止 Code 被拿到后多次请求被认为是恶意请求,而 token 每次请求后都会变化,且有过期时间。