QQ登录的具体流程

网站授权QQ登录

  • 首先需要去QQ互联申请应用
  • 填写网站的相关信息,以及回调地址,需要进行审核。
  • 申请流程暂时不说了,百度一下挺多申请失败案例的解决方案的,你懂的现在越来越严格了,甚至一个错别字都不让有。

QQ登录的完整流程

  1. 用户点击QQ登录
    用户在你的前端页面点击QQ登录按钮,发送请求到后端。

  2. 重定向到QQ授权页面
    后端也可以直接重定向到QQ的授权页面,也可以将授权页面的地址返回给前端, 由前端将用户重定向到QQ的授权页面,授权页面的地址通常是一个URL,类似于:https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=YOUR_APP_ID&redirect_uri=YOUR_REDIRECT_URI&state=YOUR_STATE

    其中:

    • YOUR_APP_ID:你在QQ开放平台注册应用时获得的APP ID。
    • YOUR_REDIRECT_URI:你在QQ开放平台设置的回调URL。
    • YOUR_STATE:一个随机字符串,用于防止CSRF攻击。
  3. 用户授权
    用户在QQ的授权页面点击同意授权。

  4. QQ重定向到回调URL
    授权成功后,QQ会将用户重定向到你设置的redirectUri,并在回调地址的查询参数中加上一个code参数和原先的state

  5. 前端获取code并验证state
    前端从回调地址中解析出code参数。同时,验证返回的state是否与最初发送的state一致,以确保这不是一个CSRF攻击。

state参数也可以交给后端进行验证
这里的回调地址就是QQ互联上面写的回调地址,前端会在这个回调地址的页面发送请求给后端,同时携带code和state参数(这两个参数从回调地址里面取出来的)

  1. 前端向后端发送登录请求并携带code和state参数
    前端发起请求,将code和state发送到后端的/callback接口。

  2. 后端获取Access Token
    后端使用code,向QQ服务器请求访问令牌(Access Token)。这通常涉及到一个POST请求到https://graph.qq.com/oauth2.0/token,带有codeYOUR_APP_IDYOUR_APP_KEY(你的应用密钥)和YOUR_REDIRECT_URI作为参数。

  3. 后端获取OpenID
    使用Access Token,后端可以向QQ服务器请求OpenID,这是一个代表QQ用户唯一标识的值。

  4. 后端获取用户信息
    后端使用Access Token和OpenID,请求QQ服务器以获取用户的基本信息。

  5. 创建或登录用户
    后端可以使用从QQ获取的用户信息来:

    • 检查数据库中是否已经有一个与此QQ账户关联的用户。
    • 如果是,则登录该用户。
    • 如果不是,则创建一个新用户,并与该QQ账户关联。
  6. 返回结果到前端
    后端可以返回一个令牌(如JWT)或其他标识已登录用户的信息到前端。

  7. 前端处理登录状态
    前端根据后端的响应处理用户的登录状态,例如保存JWT,显示用户的信息等。

代码示例

1. 添加依赖

pom.xml中添加相关的依赖:

<!-- Spring Boot Web Starter: 提供了创建web应用所需要的所有必要依赖,包括内嵌的Tomcat服务器 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- Apache HttpClient: 一个流行的库,用于处理HTTP请求 -->
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
</dependency>

<!-- Jackson Databind: 用于将Java对象与JSON数据进行序列化和反序列化 -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>

2. 配置文件

application.properties添加:

qq.appId=YOUR_APP_ID
qq.appKey=YOUR_APP_KEY
qq.redirectUri=http://yourdomain.com/auth/qq/callback

3. 实现Service

创建一个QQAuthService用于处理与QQ的交互。

@Service  
public class QQAuthService {

    // 从application.properties中读取配置值
    @Value("${qq.appId}")
    private String appId;

    @Value("${qq.appKey}")
    private String appKey;

    @Value("${qq.redirectUri}")
    private String redirectUri;

    // 创建一个可关闭的HTTP客户端,用于发送请求
    private final CloseableHttpClient httpClient = HttpClients.createDefault();

    /**
     * 获取Access Token
     * 
     * @param code 从QQ回调URL中获得的授权码
     * @return 返回Access Token
     * @throws IOException 处理HTTP请求可能会出现的IO异常
     */
    public String getAccessToken(String code) throws IOException {
        // 构建获取Access Token的URL
        String url = "https://graph.qq.com/oauth2.0/token?grant_type=authorization_code&client_id=" 
            + appId + "&client_secret=" + appKey + "&code=" + code + "&redirect_uri=" + redirectUri;
        
        // 创建一个HttpGet请求
        HttpGet httpGet = new HttpGet(url);
        // 执行该请求,并获取响应
        CloseableHttpResponse response = httpClient.execute(httpGet);
        // 将响应内容转换为字符串
        String responseStr = EntityUtils.toString(response.getEntity());
        // 解析响应内容,提取access_token
        String accessToken = responseStr.split("&")[0].split("=")[1];
        return accessToken;
    }

    /**
     * 获取OpenID
     * 
     * @param accessToken Access Token
     * @return 返回OpenID
     * @throws IOException 处理HTTP请求可能会出现的IO异常
     */
    public String getOpenId(String accessToken) throws IOException {
        // 构建获取OpenID的URL
        String url = "https://graph.qq.com/oauth2.0/me?access_token=" + accessToken;
        
        // 创建一个HttpGet请求
        HttpGet httpGet = new HttpGet(url);
        // 执行该请求,并获取响应
        CloseableHttpResponse response = httpClient.execute(httpGet);
        // 将响应内容转换为字符串
        String responseStr = EntityUtils.toString(response.getEntity());
        // 解析响应内容,提取openid
        String openId = responseStr.substring(responseStr.lastIndexOf(":\"") + 2, responseStr.lastIndexOf("\"}"));
        return openId;
    }

    /**
     * 获取用户信息
     * 
     * @param accessToken Access Token
     * @param openId OpenID
     * @return 返回用户信息的JSON字符串
     * @throws IOException 处理HTTP请求可能会出现的IO异常
     */
    public String getUserInfo(String accessToken, String openId) throws IOException {
        // 构建获取用户信息的URL
        String url = "https://graph.qq.com/user/get_user_info?access_token=" + accessToken + "&oauth_consumer_key=" 
            + appId + "&openid=" + openId;
        
        // 创建一个HttpGet请求
        HttpGet httpGet = new HttpGet(url);
        // 执行该请求,并获取响应
        CloseableHttpResponse response = httpClient.execute(httpGet);
        // 将响应内容转换为字符串
        String responseStr = EntityUtils.toString(response.getEntity());
        // 直接返回用户信息的JSON字符串
        return responseStr;  
    }
}

4. 创建Controller

@RestController
@RequestMapping("/auth/qq")
public class QQAuthController {

    @Autowired
    private QQAuthService qqAuthService;

    /**
     * 重定向用户到QQ登录页面。
     * 
     * @param response HttpServletResponse对象,用于发送重定向。
     * @param session  HttpSession对象,用于存储state。
     * @throws IOException 如果重定向失败。
     */
    @GetMapping("/login")
    public void qqLogin(HttpServletResponse response, HttpSession session) throws IOException {
        // 生成并存储一个唯一的state值
        String state = UUID.randomUUID().toString();
        session.setAttribute("qq_oauth_state", state);

        // 构建QQ授权的URL
        String url = "https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=" 
            + appId + "&redirect_uri=" + URLEncoder.encode(redirectUri, "UTF-8") + "&state=" + state;
        
        // 当你在移动端上请求登录时,可以在请求中带上display=mobile参数
        // 系统就会跳转到移动版的QQ登录页面,为用户提供更好的体验
        if ("mobile".equals(display)) {
            url += "&display=mobile";
        }
        // 重定向到QQ授权页面
        response.sendRedirect(url);
    }

    /**
     * 处理QQ授权的回调请求。
     * 
     * @param code QQ授权返回的code。
     * @param state QQ授权返回的state。
     * @param session HttpSession对象,用于验证state。
     * @return 用户信息或错误消息。
     */
    @GetMapping("/callback")
    public String qqCallback(@RequestParam String code, @RequestParam String state, HttpSession session) {
        // 检查返回的state是否与存储的一致
        String storedState = (String) session.getAttribute("qq_oauth_state");
        if (storedState == null || !storedState.equals(state)) {
            return "Error: state does not match";
        }

        try {
            // 获取并使用AccessToken和OpenID
            String accessToken = qqAuthService.getAccessToken(code);
            String openId = qqAuthService.getOpenId(accessToken);
            String userInfo = qqAuthService.getUserInfo(accessToken, openId);

            // 这里可以进行用户注册或登录操作
            // 这里可以根据用户是新用户还是老用户决定是注册后登录还是直接登录
            // 然后返回登录用户的信息
            return userInfo;
        } catch (IOException e) {
            return "Error occurred during QQ auth: " + e.getMessage();
        }
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

书生-w

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值