OAuth 2.0 应用实践

前言

有没有一种方式,不需要告诉第三方你的账号、密码,通过简单的授权,第三方就可以看到授权范围内的部分数据呢?

当然有,这就是目前盛行的 OAuth 授权流程。

OAuth 2.0 是一种用于授权的开放标准,广泛应用于互联网应用中。它允许第三方应用程序在用户授权的情况下,访问用户在某一服务提供商(如Google、Facebook等)上的资源,而不需要暴露用户的凭据(如用户名和密码)。

以下是 OAuth 2.0 的基本流程:

  1. 用户授权:用户在第三方应用程序上点击授权按钮,跳转到服务提供商的授权页面。
  2. 授权码(Authorization Code) :用户在服务提供商的授权页面上同意授权后,服务提供商会将用户重定向回第三方应用程序,并附带一个授权码。
  3. 获取访问令牌(Access Token) :第三方应用程序使用授权码向服务提供商请求访问令牌。
  4. 访问资源:第三方应用程序使用访问令牌访问用户在服务提供商上的资源。

我们以登录某第三方应用为例,其已接入微信登录能力,我们只需要通过简单的授权,即可实现正常登录:

OAuth 2.0

我们仍然以微信提供的 OAuth2.0 认证为例,微信提供了OAuth 2.0认证和授权服务,允许第三方应用在用户授权的情况下,访问用户的微信账户信息。

以下是微信 OAuth 2.0 授权码流程:

1. 注册微信开放平台应用

首先,需要在微信开放平台上注册应用,以获取 AppID 和 AppSecret。步骤如下:

  1. 访问 [微信开放平台]
  2. 注册并登录开发者账号。
  3. 创建一个新的应用,填写相关信息。
  4. 记录下 AppID 和 AppSecret。

2. 配置 OAuth 2.0 端点

微信的 OAuth 2.0 端点包括授权端点和令牌端点:

3. 登录授权流程

大致流程:

  1. 进入你的应用页面,如果是未登录状态,则从后端获取登录授权页(/login)
  2. 后端返回微信登录授权页面
  3. 你进行【点击授权】
  4. 微信将用户重定向回你在微信开放平台上配置的回调地址,并在 URL 中附带授权码。
  5. 前端携带授权码 code 请求后端,后端通过授权码 code 调用微信开放接口获取 access_token 及用户信息
  6. 授权完成,后端记录登录状态
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 授权码流程,包括前端引导用户登录、微信重定向回后端、后端处理回调、获取访问令牌和用户信息。

前端与后端的交互

在这个流程中,前端的主要任务是引导用户点击登录链接,并处理微信的重定向。后端负责处理回调请求,提取授权码,获取访问令牌,并获取用户信息。

  1. 前端引导用户登录:前端页面上有一个登录链接,用户点击后,前端将用户重定向到微信的授权页面。
  2. 微信重定向回后端:微信在用户授权后,将用户重定向回后端的回调地址,并附带授权码。
  3. 后端处理回调:后端接收到回调请求,提取授权码,并使用授权码获取访问令牌和用户信息。

微信 OAuth 2.0 授权码流程中,如何处理访问令牌的过期和刷新?

在微信 OAuth 2.0 授权码流程中,访问令牌(Access Token)是有有效期的,通常为2小时。

当访问令牌过期时,你需要使用刷新令牌(Refresh Token)来获取新的访问令牌。刷新令牌的有效期通常较长,可以是 30 天或更长。

访问令牌过期和刷新的步骤:

  1. 获取刷新令牌:在获取访问令牌 access_token 时,微信会同时返回一个刷新令牌 refresh_token。
  2. 存储令牌:后端存储令牌,并做好过期时间控制。
  3. 检测访问令牌是否过期:在每次使用访问令牌访问资源时,检测是否返回了令牌过期的错误。
  4. 使用刷新令牌获取新的访问令牌:当访问令牌过期时,使用刷新令牌获取新的访问令牌。

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 结合使用,以实现单点登录。

...

相关推荐
qq_17448285757 小时前
springboot基于微信小程序的旧衣回收系统的设计与实现
spring boot·后端·微信小程序
锅包肉的九珍7 小时前
Scala的Array数组
开发语言·后端·scala
心仪悦悦7 小时前
Scala的Array(2)
开发语言·后端·scala
2401_882727578 小时前
BY组态-低代码web可视化组件
前端·后端·物联网·低代码·数学建模·前端框架
心仪悦悦8 小时前
Scala中的集合复习(1)
开发语言·后端·scala
代码小鑫9 小时前
A043-基于Spring Boot的秒杀系统设计与实现
java·开发语言·数据库·spring boot·后端·spring·毕业设计
真心喜欢你吖9 小时前
SpringBoot与MongoDB深度整合及应用案例
java·spring boot·后端·mongodb·spring
激流丶9 小时前
【Kafka 实战】Kafka 如何保证消息的顺序性?
java·后端·kafka
uzong10 小时前
一个 IDEA 老鸟的 DEBUG 私货之多线程调试
java·后端
飞升不如收破烂~10 小时前
Spring boot常用注解和作用
java·spring boot·后端