程序员scholar 程序员scholar
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Python 基础

    • Python基础
  • Python 进阶

    • 装饰器与生成器
    • 异常处理
    • 标准库精讲
    • 模块与包
    • pip包管理工具
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
前端 (opens new window)
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Python 基础

    • Python基础
  • Python 进阶

    • 装饰器与生成器
    • 异常处理
    • 标准库精讲
    • 模块与包
    • pip包管理工具
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
前端 (opens new window)
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
npm

(进入注册为作者充电)

  • SpringSecurity

    • Spring Security是什么
    • 认证与授权的基本概念
    • Spring Security 的默认配置
    • SpringSecurity的默认登录页
    • Spring Security 的 Filter 机制
    • HttpSecurity 与自定义登录页面
    • Spring Security 的核心组件与扩展
    • Spring Security 中的用户认证与角色管理
    • Spring Security 的授权机制与安全表达式
    • Security 的 Session 与 Token 管理
    • Spring Security 集成第三方登录
    • Spring Security 集成 QQ 登录与 JWT 认证
      • 1. 项目依赖配置
      • 2. 配置 QQ 登录的 OAuth2 客户端
      • 3. MyBatis 配置与数据库设计
      • 4. 自定义 OAuth2UserService:处理用户信息与数据库交互
      • 5. 登录成功处理:生成 JWT 并返回给前端
      • 6. Spring Security 配置:集成 QQ 登录与 JWT 认证
      • 7. JWT 工具类的实现
      • 8. 完整流程总结
    • Spring Security 登录认证源码
    • Spring Security - JWT认证实战
    • Spring Security - JWT授权实战
    • Spring Security 异常处理与自定义逻辑
  • SpringSecurity
  • SpringSecurity
scholar
2024-08-20
目录

Spring Security 集成 QQ 登录与 JWT 认证

# Spring Security 集成 QQ 登录与 JWT 认证

前言

在基于 Spring Boot 的项目中,集成 QQ 登录和 JWT 认证可以实现更加现代化的无状态认证流程。本节将详细介绍如何配置 QQ 登录、保存用户信息到数据库,以及生成 JWT 返回给前端。实现过程中我们将使用 Spring Security、OAuth2、MyBatis 和 MySQL。

# 1. 项目依赖配置

在 Spring Boot 项目中引入以下依赖,包括 Spring Security OAuth2 客户端、JWT 库、MyBatis 和 MySQL 驱动。

pom.xml 配置:

<dependencies>
    <!-- Spring Security OAuth2 Client,用于集成 OAuth2 登录功能 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-oauth2-client</artifactId>
    </dependency>

    <!-- JWT 库,用于生成和解析 Token -->
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.9.1</version>
    </dependency>

    <!-- Spring Security 依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>

    <!-- MyBatis 依赖 -->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.1.4</version>
    </dependency>

    <!-- MySQL 驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
</dependencies>
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

# 2. 配置 QQ 登录的 OAuth2 客户端

在 Spring Boot 中,OAuth2 客户端的配置通常放在 application.yml 或 application.properties 中。

application.yml 配置:

spring:
  security:
    oauth2:
      client:
        registration:
          qq:
            client-id: your-qq-client-id       # QQ 应用的 Client ID,需要替换为你自己的
            client-secret: your-qq-client-secret # QQ 应用的 Client Secret,需要替换为你自己的
            redirect-uri: "{baseUrl}/login/oauth2/code/qq" # 回调地址,填写你的应用地址
            authorization-grant-type: authorization_code  # 授权类型,通常为 authorization_code
            client-name: QQ
            scope: get_user_info              # QQ 登录需要的权限
        provider:
          qq:
            authorization-uri: https://graph.qq.com/oauth2.0/authorize
            token-uri: https://graph.qq.com/oauth2.0/token
            user-info-uri: https://graph.qq.com/user/get_user_info
            user-name-attribute: openid   # 从用户信息中获取 OpenID 的字段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

参数说明:

  • client-id:QQ 应用的 Client ID,需要在 QQ 互联平台上注册应用后获取。
  • client-secret:QQ 应用的 Client Secret,同样需要在 QQ 互联平台上获取。
  • redirect-uri:回调地址,QQ 登录成功后跳转回你应用的路径,通常格式为 http://your-app-url/login/oauth2/code/qq。
  • scope:定义了 QQ 登录所需的权限,get_user_info 是必须的。

# 3. MyBatis 配置与数据库设计

配置 MyBatis,并设计用户表来存储 QQ 登录返回的用户信息。

application.yml MyBatis 配置:

mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.example.project.model
1
2
3

SQL 示例:创建用户表

CREATE TABLE users (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    openid VARCHAR(50) NOT NULL UNIQUE,
    nickname VARCHAR(100),
    avatar VARCHAR(255),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
1
2
3
4
5
6
7
8

User 实体类:

public class User {
    private Long id;
    private String openid;
    private String nickname;
    private String avatar;
    // Getters and Setters
}
1
2
3
4
5
6
7

UserMapper.xml MyBatis 映射文件:

<mapper namespace="com.example.project.mapper.UserMapper">
    <select id="findByOpenId" parameterType="String" resultType="com.example.project.model.User">
        SELECT * FROM users WHERE openid = #{openid}
    </select>

    <insert id="insertUser" parameterType="com.example.project.model.User">
        INSERT INTO users (openid, nickname, avatar) VALUES (#{openid}, #{nickname}, #{avatar})
    </insert>

    <update id="updateUser" parameterType="com.example.project.model.User">
        UPDATE users SET nickname = #{nickname}, avatar = #{avatar} WHERE openid = #{openid}
    </update>
</mapper>
1
2
3
4
5
6
7
8
9
10
11
12
13

UserMapper 接口:

import com.example.project.model.User;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper {
    User findByOpenId(String openid);
    void insertUser(User user);
    void updateUser(User user);
}
1
2
3
4
5
6
7
8
9

# 4. 自定义 OAuth2UserService:处理用户信息与数据库交互

自定义 OAuth2UserService 实现,用于获取 QQ 登录返回的用户信息并与数据库交互。

代码示例:自定义 OAuth2UserService

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;

import java.util.Map;

@Service
public class CustomOAuth2UserService implements OAuth2UserService<OAuth2UserRequest, OAuth2User> {

    @Autowired
    private UserMapper userMapper;

    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
        OAuth2UserService<OAuth2UserRequest, OAuth2User> delegate = new DefaultOAuth2UserService();
        OAuth2User oAuth2User = delegate.loadUser(userRequest);

        // 从 QQ 返回的用户信息中提取必要的信息
        Map<String, Object> attributes = oAuth2User.getAttributes();
        String openId = (String) attributes.get("openid");
        String nickname = (String) attributes.get("nickname");
        String avatar = (String) attributes.get("figureurl");

        // 检查用户是否存在
        User user = userMapper.findByOpenId(openId);
        if (user == null) {
            // 如果用户不存在,创建新用户并保存到数据库
            user = new User();
            user.setOpenid(openId);
            user.setNickname(nickname);
            user.setAvatar(avatar);
            userMapper.insertUser(user);
        } else {
            // 如果用户存在,更新用户信息
            user.setNickname(nickname);
            user.setAvatar(avatar);
            userMapper.updateUser(user);
        }

        // 返回封装的 OAuth2User 对象
        return new DefaultOAuth2User(oAuth2User.getAuthorities(), attributes, "openid");
    }
}
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
37
38
39
40
41
42
43
44
45
46
47

重要的 API 说明:

  • DefaultOAuth2UserService:Spring Security 提供的默认 OAuth2UserService 实现,用于加载用户信息。
  • loadUser(OAuth2UserRequest userRequest):自定义的用户加载逻辑,通过此方法获取并处理第三方登录的用户信息。
  • getAttributes():获取第三方返回的用户信息 Map,其中包含了 openid、nickname、avatar 等关键信息。

# 5. 登录成功处理:生成 JWT 并返回给前端

用户登录成功后,生成 JWT 并返回给前端。

代码示例:自定义登录成功处理器

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.databind.ObjectMapper;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@Component
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    @Autowired
    private JwtTokenProvider jwtTokenProvider;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
        OAuth2AuthenticationToken oAuth2AuthenticationToken = (OAuth2AuthenticationToken) authentication;
        String username = oAuth2AuthenticationToken.getPrincipal().getAttribute("nickname");

        // 生成 JWT
        String token = jwtTokenProvider.generateToken(username);

        // 构建返回的 JSON 数据
        Map<String, String> tokenResponse = new HashMap<>();
        tokenResponse.put("token", token);

        // 返回 JSON 数据
        response.setContentType("application/json;charset=UTF-8");
        response.setStatus(HttpServletResponse.SC_OK);
        response.get

Writer().write(new ObjectMapper().writeValueAsString(tokenResponse));
    }
}
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
37
38
39

重要的 API 说明:

  • AuthenticationSuccessHandler:Spring Security 提供的接口,用于处理认证成功后的操作。
  • OAuth2AuthenticationToken:封装了 OAuth2 用户认证信息的对象。
  • ObjectMapper:Jackson 提供的工具类,用于将对象转换为 JSON 格式。

# 6. Spring Security 配置:集成 QQ 登录与 JWT 认证

配置 Spring Security 来整合 QQ 登录和 JWT 认证。

代码示例:Spring Security 配置

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .authorizeRequests()
                .antMatchers("/login", "/oauth2/**").permitAll()
                .anyRequest().authenticated()
            .and()
            .oauth2Login()
                .loginPage("/login")  // 配置自定义登录页面
                .userInfoEndpoint()
                .userService(new CustomOAuth2UserService())  // 配置自定义的 OAuth2UserService
            .and()
            .successHandler(new CustomAuthenticationSuccessHandler());  // 配置自定义的成功处理器,返回 JWT
    }
}
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

重要的配置项说明:

  • csrf().disable():禁用 CSRF 防护,因为我们使用的是无状态的 Token 认证。
  • oauth2Login():启用 OAuth2 登录功能,用于处理第三方登录(如 QQ 登录)。
  • loginPage("/login"):指定自定义登录页面的路径。如果未配置,自带的登录页面会自动加载。
  • userService(new CustomOAuth2UserService()):配置自定义的 OAuth2UserService,用于加载和处理用户信息。
  • successHandler(new CustomAuthenticationSuccessHandler()):配置自定义的成功处理器,用于登录成功后生成 JWT 并返回给前端。

# 7. JWT 工具类的实现

代码示例:JWT 工具类

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;
import java.util.Date;

@Component
public class JwtTokenProvider {

    private final String jwtSecret = "mySecretKey";
    private final long jwtExpirationMs = 86400000;  // 24小时

    public String generateToken(String username) {
        return Jwts.builder()
                   .setSubject(username)
                   .setIssuedAt(new Date())
                   .setExpiration(new Date((new Date()).getTime() + jwtExpirationMs))
                   .signWith(SignatureAlgorithm.HS512, jwtSecret)
                   .compact();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

重要的 API 说明:

  • setSubject(String subject):设置 JWT 的主体,通常是用户名或用户 ID。
  • setIssuedAt(Date issuedAt):设置 JWT 的签发时间。
  • setExpiration(Date expiration):设置 JWT 的过期时间。
  • signWith(SignatureAlgorithm algorithm, String secret):使用指定的签名算法和密钥对 JWT 进行签名。

# 8. 完整流程总结

  1. 用户点击 QQ 登录按钮,跳转到 QQ 授权页面。
  2. 用户在 QQ 页面进行授权,授权成功后跳转到回调页面。
  3. Spring Security 通过 OAuth2 客户端获取到用户信息,并将其保存到数据库。
  4. 登录成功后,生成 JWT 并以 JSON 格式返回给前端。
  5. 前端将 JWT 存储在本地(如 localStorage),并在后续请求中携带 JWT 进行身份验证。
编辑此页 (opens new window)
上次更新: 2024/12/28, 18:32:08
Spring Security 集成第三方登录
Spring Security 登录认证源码

← Spring Security 集成第三方登录 Spring Security 登录认证源码→

Theme by Vdoing | Copyright © 2019-2025 程序员scholar
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式