You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Spring Boot的JWT认证项目中集成OAuth2

在现有JWT认证的Spring Boot房产项目中集成OAuth2支持

我正在开发一个房产列表的Spring Boot项目,已经基于JWT实现了认证机制并做了自定义扩展。现在希望集成OAuth2来提升安全性与灵活性,支持社交登录等OAuth2流程。以下是我的现有代码:

User实体类

package com.marvi.realestatelisting.Entity;

import java.util.Collection;
import java.util.List;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;

@Data
@Entity
@Table(name = "users")
public class User implements UserDetails {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String email;
    private String password;
    private String role;

    @Override
    public String getUsername() {
        return email;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return List.of(new SimpleGrantedAuthority(role));
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

安全配置类

package com.marvi.realestatelisting.Config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
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.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import com.marvi.realestatelisting.Service.UserService;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Autowired
    private UserService userService;

    @Autowired
    private JWTAuthFilter jwtAuthFilter;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.csrf(AbstractHttpConfigurer::disable)
            .authorizeHttpRequests(request -> request
                .requestMatchers("/auth/**", "/public/**", "/uploads/**").permitAll()
                .requestMatchers("/agent/**").hasAuthority("AGENT")
                .requestMatchers("/user/**").hasAuthority("USER")
                .anyRequest().authenticated())
            .sessionManagement(manager -> manager.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .authenticationProvider(authenticationProvider())
            .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);

        return httpSecurity.build();
    }

    @Bean
    public AuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        provider.setUserDetailsService(userService);
        provider.setPasswordEncoder(passwordEncoder());
        return provider;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }
}

JWT认证过滤器

package com.marvi.realestatelisting.Config;

import java.io.IOException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import com.marvi.realestatelisting.Service.JwtUtils;
import com.marvi.realestatelisting.Service.UserService;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

@Component
public class JWTAuthFilter extends OncePerRequestFilter {

    @Autowired
    private JwtUtils jwtUtils;

    @Autowired
    private UserService userService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        final String authHeader = request.getHeader("Authorization");
        final String jwtToken;
        final String userEmail;

        if (authHeader == null || !authHeader.startsWith("Bearer ")) {
            filterChain.doFilter(request, response);
            return;
        }

        jwtToken = authHeader.substring(7);
        userEmail = jwtUtils.extractUsername(jwtToken);

        if (userEmail != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            UserDetails userDetails = userService.loadUserByUsername(userEmail);

            if (jwtUtils.isTokenValid(jwtToken, userDetails)) {
                UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(authToken);
            }
        }

        filterChain.doFilter(request, response);
    }
}

之前参考的YouTube教程没覆盖OAuth2集成内容,恳请指导如何在现有JWT认证架构中添加OAuth2支持。


解决方案:集成OAuth2与现有JWT架构

1. 添加依赖

在pom.xml中加入Spring Security OAuth2客户端与资源服务器依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>

2. 扩展User实体

添加OAuth2相关字段,用于存储第三方登录的标识:

@Data
@Entity
@Table(name = "users")
public class User implements UserDetails {
    // 原有字段...
    
    // OAuth2相关字段
    private String oauth2Provider;
    private String oauth2ProviderId;

    // 构造方法、原有方法保持不变...
}

3. 配置OAuth2客户端

在application.yml中配置社交登录提供商(以Google为例):

spring:
  security:
    oauth2:
      client:
        registration:
          google:
            client-id: YOUR_GOOGLE_CLIENT_ID
            client-secret: YOUR_GOOGLE_CLIENT_SECRET
            scope: profile, email
        provider:
          google:
            authorization-uri: https://accounts.google.com/o/oauth2/v2/auth
            token-uri: https://www.googleapis.com/oauth2/v4/token
            user-info-uri: https://www.googleapis.com/oauth2/v3/userinfo
            user-name-attribute: sub

4. 修改SecurityConfig

更新安全配置,添加OAuth2登录支持,并在登录成功后生成自定义JWT:

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Autowired
    private UserService userService;

    @Autowired
    private JWTAuthFilter jwtAuthFilter;
    
    @Autowired
    private JwtUtils jwtUtils;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.csrf(AbstractHttpConfigurer::disable)
            .authorizeHttpRequests(request -> request
                .requestMatchers("/auth/**", "/public/**", "/uploads/**", "/login/oauth2/code/**").permitAll()
                .requestMatchers("/agent/**").hasAuthority("AGENT")
                .requestMatchers("/user/**").hasAuthority("USER")
                .anyRequest().authenticated())
            .sessionManagement(manager -> manager.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .authenticationProvider(authenticationProvider())
            .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class)
            // 添加OAuth2登录配置
            .oauth2Login(oauth2 -> oauth2
                .successHandler((request, response, authentication) -> {
                    // 获取OAuth2用户信息
                    OAuth2User oAuth2User = (OAuth2User) authentication.getPrincipal();
                    String email = oAuth2User.getAttribute("email");
                    String provider = authentication.getAuthorities().stream()
                            .filter(grantedAuthority -> grantedAuthority.getAuthority().startsWith("SCOPE_"))
                            .findFirst()
                            .map(grantedAuthority -> grantedAuthority.getAuthority().substring(6))
                            .orElse("google");
                    String providerId = oAuth2User.getAttribute("sub");

                    // 查找或创建本地用户
                    User user = userService.findOrCreateOAuth2User(email, provider, providerId);
                    
                    // 生成自定义JWT
                    String jwtToken = jwtUtils.generateToken(user);
                    
                    // 返回JWT给前端(可根据需求调整返回方式,比如重定向或JSON响应)
                    response.sendRedirect("/public/login-success?token=" + jwtToken);
                })
            );

        return httpSecurity.build();
    }

    // 原有Bean保持不变...
}

5. 扩展UserService

添加方法处理OAuth2用户的查找与创建:

@Service
public class UserService implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;
    
    @Autowired
    private PasswordEncoder passwordEncoder;

    // 原有方法...

    public User findOrCreateOAuth2User(String email, String provider, String providerId) {
        User user = userRepository.findByEmail(email);
        if (user == null) {
            user = new User();
            user.setEmail(email);
            // OAuth2用户无需密码,设置随机加密值
            user.setPassword(passwordEncoder.encode(UUID.randomUUID().toString()));
            user.setRole("USER"); // 默认角色,可按需调整
            user.setOauth2Provider(provider);
            user.setOauth2ProviderId(providerId);
            userRepository.save(user);
        }
        return user;
    }
}

6. 前端适配

前端添加OAuth2登录入口(如"用Google登录"按钮),点击后跳转至/oauth2/authorization/google,登录成功后从回调中获取JWT,后续请求沿用原有JWT认证流程即可。


内容的提问来源于stack exchange,提问作者Marvin Lewis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 21:42:09