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

Spring Boot3+Security6自定义认证与Google OAuth2登录JWT实现咨询

Spring Boot 3 + Spring Security 6: 自定义表单+Google OAuth2登录统一返回JWT实现方案

核心思路

不需要自定义OAuth2LoginAuthenticationFilter,Spring Security提供了足够的扩展点来实现你的需求:

  • 自定义表单登录:通过UserDetailsService完成密码校验,登录成功后用AuthenticationSuccessHandler生成JWT返回。
  • Google OAuth2登录:利用默认的OAuth2LoginAuthenticationFilter完成授权码交换、用户信息获取,然后通过自定义OAuth2AuthenticationSuccessHandler拦截认证成功事件,完成用户创建/查询、生成JWT并重定向到UI。

步骤实现

1. 依赖准备

在pom.xml(Maven)中添加必要依赖:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-oauth2-client</artifactId>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-api</artifactId>
        <version>0.11.5</version>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-impl</artifactId>
        <version>0.11.5</version>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-jackson</artifactId>
        <version>0.11.5</version>
        <scope>runtime</scope>
    </dependency>
</dependencies>

2. 统一JWT工具类

创建JwtUtil,确保两种登录方式生成的令牌格式一致:

@Component
public class JwtUtil {
    private final String secretKey = "your-strong-secret-key"; // 建议从配置文件读取
    private final long expirationMs = 86400000; // 24小时

    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        return Jwts.builder()
                .setClaims(claims)
                .setSubject(userDetails.getUsername())
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + expirationMs))
                .signWith(SignatureAlgorithm.HS256, secretKey)
                .compact();
    }

    // 令牌解析、有效性验证等方法按需实现
}

3. 自定义表单登录实现

3.1 实现UserDetailsService

从数据库查询用户信息,完成密码校验:

@Service
public class CustomUserDetailsService implements UserDetailsService {
    @Autowired
    private UserRepository userRepository; // 自定义用户数据访问层

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(username)
                .orElseThrow(() -> new UsernameNotFoundException("用户不存在: " + username));
        return User.withUsername(user.getUsername())
                .password(user.getPassword())
                .authorities(user.getAuthorities())
                .build();
    }
}

3.2 表单登录成功处理器

登录成功后生成JWT并返回JSON响应:

@Component
public class FormLoginSuccessHandler implements AuthenticationSuccessHandler {
    @Autowired
    private JwtUtil jwtUtil;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
        UserDetails userDetails = (UserDetails) authentication.getPrincipal();
        String token = jwtUtil.generateToken(userDetails);
        
        response.setContentType("application/json");
        response.getWriter().write("{\"token\":\"" + token + "\"}");
        response.getWriter().flush();
    }
}

4. Google OAuth2登录实现

4.1 配置OAuth2客户端信息

在application.yml中添加Google OAuth2配置:

spring:
  security:
    oauth2:
      client:
        registration:
          google:
            client-id: your-google-client-id
            client-secret: your-google-client-secret
            redirect-uri: "{baseUrl}/login/oauth2/code/google"
            scope: openid, email, profile
        provider:
          google:
            authorization-uri: https://accounts.google.com/o/oauth2/auth
            token-uri: https://oauth2.googleapis.com/token
            user-info-uri: https://www.googleapis.com/oauth2/v3/userinfo
            user-name-attribute: sub

4.2 自定义OAuth2登录成功处理器

拦截认证成功事件,完成用户创建/查询、生成JWT并重定向到UI:

@Component
public class OAuth2LoginSuccessHandler implements AuthenticationSuccessHandler {
    @Autowired
    private JwtUtil jwtUtil;
    @Autowired
    private UserRepository userRepository;
    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
        OAuth2AuthenticationToken oauthToken = (OAuth2AuthenticationToken) authentication;
        OAuth2User oauthUser = oauthToken.getPrincipal();
        
        // 提取Google用户信息
        String email = oauthUser.getAttribute("email");
        String username = oauthUser.getAttribute("name");

        // 查找或创建本地用户
        User user = userRepository.findByEmail(email)
                .orElseGet(() -> {
                    User newUser = new User();
                    newUser.setUsername(username);
                    newUser.setEmail(email);
                    // 生成随机密码(OAuth2用户无需密码登录)
                    newUser.setPassword(passwordEncoder.encode(UUID.randomUUID().toString()));
                    newUser.setAuthorities(Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")));
                    return userRepository.save(newUser);
                });

        // 生成统一格式的JWT
        UserDetails userDetails = User.withUsername(user.getUsername())
                .password(user.getPassword())
                .authorities(user.getAuthorities())
                .build();
        String token = jwtUtil.generateToken(userDetails);

        // 重定向到UI应用,将JWT作为参数或存入Cookie(推荐Cookie更安全)
        String redirectUrl = "http://your-ui-domain.com?token=" + token;
        response.sendRedirect(redirectUrl);
    }
}

5. 配置SecurityFilterChain

整合所有组件,实现无状态认证:

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Autowired
    private CustomUserDetailsService userDetailsService;
    @Autowired
    private FormLoginSuccessHandler formLoginSuccessHandler;
    @Autowired
    private OAuth2LoginSuccessHandler oAuth2LoginSuccessHandler;

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

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .csrf(csrf -> csrf.disable()) // 前后端分离场景关闭CSRF
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // 无状态,不创建会话
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/login", "/oauth2/**", "/register").permitAll() // 开放登录、OAuth2回调、注册接口
                        .anyRequest().authenticated()
                )
                .formLogin(form -> form
                        .loginProcessingUrl("/login") // 表单登录提交地址
                        .successHandler(formLoginSuccessHandler) // 自定义表单登录成功逻辑
                )
                .oauth2Login(oauth2 -> oauth2
                        .successHandler(oAuth2LoginSuccessHandler) // 自定义OAuth2登录成功逻辑
                )
                .oauth2ResourceServer(oauth2 -> oauth2
                        .jwt(jwt -> jwt.decoder(jwtDecoder())) // 配置JWT校验逻辑
                );

        return http.build();
    }

    @Bean
    public JwtDecoder jwtDecoder() {
        SecretKey secretKey = Keys.hmacShaKeyFor("your-strong-secret-key".getBytes());
        return NimbusJwtDecoder.withSecretKey(secretKey).build();
    }
}

关键说明

  • 为什么不用自定义OAuth2LoginAuthenticationFilter:Spring Security的OAuth2LoginAuthenticationFilter已经封装了授权码交换、令牌验证、用户信息获取等核心逻辑,我们只需要在认证成功后插入自定义逻辑,AuthenticationSuccessHandler是官方提供的标准扩展点,完全满足需求,避免了修改过滤器带来的耦合问题。
  • 无状态配置:通过SessionCreationPolicy.STATELESS关闭会话,确保所有请求都通过JWT验证,符合RESTful无状态设计。
  • 安全性:将JWT存入Cookie时,建议设置HttpOnly和Secure属性防止XSS攻击;如果放在URL参数中,UI端需尽快将其存入安全存储,避免泄露。

内容的提问来源于stack exchange,提问作者Samet Baskıcı

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 11:52:44