前言
有没有一种方式,不需要告诉第三方你的账号、密码,通过简单的授权,第三方就可以看到授权范围内的部分数据呢?
当然有,这就是目前盛行的 OAuth 授权流程。
OAuth 2.0 是一种用于授权的开放标准,广泛应用于互联网应用中。它允许第三方应用程序在用户授权的情况下,访问用户在某一服务提供商(如Google、Facebook等)上的资源,而不需要暴露用户的凭据(如用户名和密码)。
以下是 OAuth 2.0 的基本流程:
- 用户授权:用户在第三方应用程序上点击授权按钮,跳转到服务提供商的授权页面。
- 授权码(Authorization Code) :用户在服务提供商的授权页面上同意授权后,服务提供商会将用户重定向回第三方应用程序,并附带一个授权码。
- 获取访问令牌(Access Token) :第三方应用程序使用授权码向服务提供商请求访问令牌。
- 访问资源:第三方应用程序使用访问令牌访问用户在服务提供商上的资源。
我们以登录某第三方应用为例,其已接入微信登录能力,我们只需要通过简单的授权,即可实现正常登录:
OAuth 2.0
我们仍然以微信提供的 OAuth2.0 认证为例,微信提供了OAuth 2.0认证和授权服务,允许第三方应用在用户授权的情况下,访问用户的微信账户信息。
以下是微信 OAuth 2.0 授权码流程:
1. 注册微信开放平台应用
首先,需要在微信开放平台上注册应用,以获取 AppID 和 AppSecret。步骤如下:
- 访问 [微信开放平台]
- 注册并登录开发者账号。
- 创建一个新的应用,填写相关信息。
- 记录下 AppID 和 AppSecret。
2. 配置 OAuth 2.0 端点
微信的 OAuth 2.0 端点包括授权端点和令牌端点:
- 授权端点(Authorization Endpoint):open.weixin.qq.com/connect/oau...
- 令牌端点(Token Endpoint):api.weixin.qq.com/sns/oauth2/...
- 用户信息端点(User Info Endpoint):api.weixin.qq.com/sns/userinf...
3. 登录授权流程
大致流程:
- 进入你的应用页面,如果是未登录状态,则从后端获取登录授权页(/login)
- 后端返回微信登录授权页面
- 你进行【点击授权】
- 微信将用户重定向回你在微信开放平台上配置的回调地址,并在 URL 中附带授权码。
- 前端携带授权码 code 请求后端,后端通过授权码 code 调用微信开放接口获取 access_token 及用户信息
- 授权完成,后端记录登录状态
java
package com.example.demo;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@WebServlet("/wechat-oauth2")
public class WeChatOAuth2Servlet extends HttpServlet {
private static final String APP_ID = "你的AppID";
private static final String APP_SECRET = "你的AppSecret";
private static final String AUTHORIZATION_URL = "https://open.weixin.qq.com/connect/oauth2/authorize";
private static final String TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/access_token";
private static final String USER_INFO_URL = "https://api.weixin.qq.com/sns/userinfo";
private static final String REDIRECT_URI = "http://localhost:8080/wechat-oauth2/callback";
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String action = req.getParameter("action");
if ("login".equals(action)) {
// 重定向到微信的授权页面
String authorizationUrl = String.format("%s?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect",
AUTHORIZATION_URL, APP_ID, REDIRECT_URI);
resp.sendRedirect(authorizationUrl);
} else if ("callback".equals(action)) { // 用户授权后,将用户重定向回你的应用的URL,并附带授权码。这个URL是你在微信开放平台上配置的回调地址(Redirect URI)
// 获取授权码
String code = req.getParameter("code");
// 使用授权码获取访问令牌
String accessToken = getAccessToken(code);
// 使用访问令牌获取用户信息
String userInfo = getUserInfo(accessToken);
resp.getWriter().write("用户信息:" + userInfo);
} else {
resp.getWriter().write("欢迎使用微信OAuth 2.0示例!<br><a href='/wechat-oauth2?action=login'>登录</a>");
}
}
private String getAccessToken(String code) throws IOException {
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
String url = String.format("%s?appid=%s&secret=%s&code=%s&grant_type=authorization_code",
TOKEN_URL, APP_ID, APP_SECRET, code);
HttpPost httpPost = new HttpPost(url);
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
String responseBody = EntityUtils.toString(response.getEntity());
Map<String, Object> responseMap = new ObjectMapper().readValue(responseBody, Map.class);
return (String) responseMap.get("access_token");
}
}
}
private String getUserInfo(String accessToken) throws IOException {
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
String url = String.format("%s?access_token=%s&openid=%s",
USER_INFO_URL, accessToken, APP_ID);
HttpPost httpPost = new HttpPost(url);
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
return EntityUtils.toString(response.getEntity());
}
}
}
}
以上主要展示处理微信的 OAuth 2.0 授权码流程,包括前端引导用户登录、微信重定向回后端、后端处理回调、获取访问令牌和用户信息。
前端与后端的交互
在这个流程中,前端的主要任务是引导用户点击登录链接,并处理微信的重定向。后端负责处理回调请求,提取授权码,获取访问令牌,并获取用户信息。
- 前端引导用户登录:前端页面上有一个登录链接,用户点击后,前端将用户重定向到微信的授权页面。
- 微信重定向回后端:微信在用户授权后,将用户重定向回后端的回调地址,并附带授权码。
- 后端处理回调:后端接收到回调请求,提取授权码,并使用授权码获取访问令牌和用户信息。
微信 OAuth 2.0 授权码流程中,如何处理访问令牌的过期和刷新?
在微信 OAuth 2.0 授权码流程中,访问令牌(Access Token)是有有效期的,通常为2小时。
当访问令牌过期时,你需要使用刷新令牌(Refresh Token)来获取新的访问令牌。刷新令牌的有效期通常较长,可以是 30 天或更长。
访问令牌过期和刷新的步骤:
- 获取刷新令牌:在获取访问令牌 access_token 时,微信会同时返回一个刷新令牌 refresh_token。
- 存储令牌:后端存储令牌,并做好过期时间控制。
- 检测访问令牌是否过期:在每次使用访问令牌访问资源时,检测是否返回了令牌过期的错误。
- 使用刷新令牌获取新的访问令牌:当访问令牌过期时,使用刷新令牌获取新的访问令牌。
OAuth2.0 经典应用场景
OAuth2.0 目前已经应用非常广泛,包括但不限于:
1. 社交登录(Social Login)
社交登录是 OAuth 2.0 最常见的应用之一。用户可以使用他们在社交媒体平台(如 微信、微博、掘金等)上的账户登录第三方应用,而无需创建新的账户。
2. API 访问授权
OAuth 2.0 常用于授权第三方应用访问用户的 API 资源。例如,用户可以授权一个应用访问他们的 巨量引擎(抖音)广告投放数据、GitHub 仓库等。
3. 单点登录(Single Sign-On, SSO)
单点登录允许用户在多个应用之间无缝切换,而无需每次都进行身份验证。OAuth 2.0 通常与 OpenID Connect 结合使用,以实现单点登录。
...