Oauth2

0. OAuth2简介

OAuth(开放授权)是一个开放标准,允许用户授权第三方网站访问他们存储在另外的服务提供者上的信息(如QQ、微信等),而不需要将用户名和密码提供给第三方网站.

应用场景:使用依赖于外部的第三方认证提供者.当用户登录站点时,会看到连接到第三方提供商的入口(如使用微信登录).用户点击以后被重定向到对应的认证服务商网站,获得用户的授权后就可以访问到需要的信息,然后重定向回来.

四种授权方式

  • 授权码(authorization-code)
  • 隐藏式(implicit)
  • 密码式(password):
  • 客户端凭证(client credentials)

最常用的为授权码方式.因此下面的文章介绍该种方式.

授权码(authorization code)方式,指的是第三方应用先申请一个授权码,再用该码获取令牌.使用令牌换取数据.如下图所示:

1
2
3
4
5
6
7
graph TD
A[客户端] -->|1.请求授权| B[资源拥有者]
B[资源拥有者] -->|2.授权许可| A[客户端]
A[客户端] -->|3.授权许可| C[验证服务器]
C[验证服务器] -->|4.访问令牌| A[客户端]
A[客户端] -->|5.访问令牌| D[资源服务器]
D[资源服务器] -->|6.受保护的资源|A[客户端]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
+--------+                               +---------------+
| |--(A)- Authorization Request ->| Resource |
| | | Owner |
| |<-(B)-- Authorization Grant ---| |
| | +---------------+
| |
| | +---------------+
| |--(C)-- Authorization Grant -->| Authorization |
| Client | | Server |
| |<-(D)----- Access Token -------| |
| | +---------------+
| |
| | +---------------+
| |--(E)----- Access Token ------>| Resource |
| | | Server |
| |<-(F)--- Protected Resource ---| |
+--------+ +---------------+

1. 网站实现QQ登录(未实操)

1.1 申请appid和appkey

appid:应用的唯一标识.在OAuth2.0认证过程中,appid的值即为oauth_consumer_key的值.

appkey:appid对应的密钥,访问用户资源时用来验证应用的合法性.在OAuth2.0认证过程中,appkey的值即为oauth_consumer_secret的值.

1.2 获取Authorization Code

请求网址: https://graph.qq.com/oauth2.0/authorize

请求方法:GET

请求参数:

参数 是否必须 含义
response_type 必须 授权类型,此值固定为“code”.
client_id 必须 申请QQ登录成功后,分配给应用的appid.
redirect_uri 必须 成功授权后的回调地址,必须是注册appid时填写的主域名下的地址,建议设置为网站首页或网站的用户中心.注意需要将url进行URLEncode.
state 必须 client端的状态值.用于第三方应用防止CSRF攻击,成功授权后回调时会原样带回.请务必严格按照流程检查用户与state参数状态的绑定.
scope 可选 请求用户授权时向用户显示的可进行授权的列表.可填写的值是API文档中列出的接口,以及一些动作型的授权(目前仅有:do_like),如果要填写多个接口名称,请用逗号隔开.例如:scope=get_user_info,list_album,upload_pic,do_like.不传则默认请求对接口get_user_info进行授权.建议控制授权项的数量,只传入必要的接口名称,因为授权项越多,用户越可能拒绝进行任何授权.
display 可选 仅PC网站接入时使用.用于展示的样式.不传则默认展示为PC下的样式.如果传入“mobile”,则展示为mobile端下的样式.

a). 如果用户成功登录并授权,则会跳转到指定的回调地址,并在redirect_uri地址后带上Authorization Code和原始的state值.如:
PC网站:http://graph.qq.com/demo/index.jsp?code=9A5F...06AF&state=test

b). code的有效期是10分钟

1.3 通过Authorization Code获取Access Token

请求网址:https://graph.qq.com/oauth2.0/token

请求方法:GET

请求参数:

参数 是否必须 含义
grant_type 必须 授权类型,此值固定为“authorization_code”
client_id 必须 申请QQ登录成功后,分配给网站的appid
client_secret 必须 申请QQ登录成功后,分配给网站的appkey
code 必须 上一步返回的authorization code
redirect_uri 必须 与上面一步中传入的redirect_uri保持一致

成功返回得到返回包,有如下信息:

参数说明 描述
access_token 授权令牌
expires_in 该access token的有效期,单位为秒
refresh_token 在授权自动续期步骤中,获取新的Access_Token时需要提供的参数

1.4 换取用户信息

请求网址:https://graph.qq.com/oauth2.0/me

请求方法:GET

请求参数:

参数 是否必须 含义
access_token 必须 上一步获取到的access_token

将返回用户OpenID.

1.5 获取到access_token和OpenID后,就可以调用QQ的API获取用户信息(如头像、说说等).API列表

2. 以微信小程序登录为例,使用OAuth2标准

如图示小程序的登录流程(图片来源:微信官方文档):

2.1 用户点击登录,获取code

调用wx.login()函数,获取code

1
2
3
4
5
wx.login({
success: res => {
// 发送 res.code 到后台换取 openId, sessionKey, unionId
}
})

如图所示:

2.2 wx.request()发送code给开发者服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
if (res.code) {
wx.request({
url: 'http://localhost:8080/WeChatMiniProgram_war_exploded/servlet/login',
data: {
code: res.code
},
// 成功后返回res.data即为session_key
success: function (res) {
// 控制台打印session_key
console.log(res.data)
// 存入缓存
wx.setStorage({
key: "session_key",
data: res.data
})
}
})
} else {
console.log('登录失败!' + res.errMsg)
}

2.3开发者服务器接收code,并向验证服务器发送请求获取access_token

servlet中接收code,调用getAccessToken()方法向验证服务器发送请求获取access_token

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package servlet;

import services.Services;

import java.io.IOException;
import java.io.PrintWriter;

/**
* @author Sheng WenZeng
* @Date 2019/7/11 19:09
* @Version 1.0
*/
public class LoginServlet extends javax.servlet.http.HttpServlet {

@Override
protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {

}

@Override
protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
String code = request.getParameter("code");
System.out.println("接收到code:" + code + "\n准备获取accessToken...");
//调用getAccessToken()方法,将code传给该方法,获取accessToken
String accessToken = Services.getAccessToken(code);
System.out.println("获取accessToken成功,accessToken为:" + accessToken);
PrintWriter out = response.getWriter();
out.write(accessToken);
out.flush();
out.close();
}
}

getAccessToken()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package services;/*
* @author Sheng WenZeng
* @Date 2019/7/11 20:35
* @Version 1.0
*/

import net.sf.json.JSONObject;
import users.Users;

/**
* @author Sheng Wenzeng
* @ClassName Services
* @Description TODO 实现功能
* @Date 2019/7/11 20:35
* @Version 1.0
*/
public class Services {
//填入微信后台的AppID和AppSecret
private static final String APP_ID = "";
private static final String APP_SECRET = "";
private static String url = "https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code";

public static String getAccessToken(String code) {
//拼装完整的url
url = url.replace("APPID", APP_ID).replace("SECRET", APP_SECRET).replace("JSCODE", code);
//发起get请求
JSONObject jsonObject = utils.utils.get(url);
//构造users对象(users是数据库中users表的实体类)
Users users = new Users();

//省略存入数据库操作

//返回access_token
return users.getAccessToken();
}
}

这样就获取到了access_token,可以使用access_token调用相应的API换取用户信息.

评论

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×