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

Spring Boot移动端API:自定义JWT与OAuth第三方登录整合方案问询

混合认证实现:自定义JWT + OAuth第三方登录(Spring Boot 移动端API)

一、移动端运作逻辑

  • 集成Google/Facebook官方SDK,引导用户完成第三方登录,获取第三方平台颁发的access_token
  • 调用后端专属接口(如/api/auth/oauth/exchange),携带access_token和第三方标识(provider=google/facebook)
  • 后端验证通过后,返回自定义JWT给移动端
  • 后续所有API请求,移动端在请求头中携带Authorization: Bearer {自定义JWT},后端通过JWT过滤器完成身份校验

二、后端实现步骤

1. 依赖引入(Maven)

<dependencies>
    <!-- Spring Security 核心 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <!-- OAuth2 客户端支持 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-oauth2-client</artifactId>
    </dependency>
    <!-- JJWT 用于JWT生成/解析 -->
    <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>
    <!-- Web 依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

2. 第三方OAuth配置(application.yml)

spring:
  security:
    oauth2:
      client:
        registration:
          google:
            client-id: YOUR_GOOGLE_CLIENT_ID
            client-secret: YOUR_GOOGLE_CLIENT_SECRET
            scope: email,profile
          facebook:
            client-id: YOUR_FACEBOOK_CLIENT_ID
            client-secret: YOUR_FACEBOOK_CLIENT_SECRET
            scope: email,public_profile
        provider:
          google:
            user-info-uri: https://www.googleapis.com/oauth2/v3/userinfo
          facebook:
            user-info-uri: https://graph.facebook.com/me?fields=id,name,email,picture

3. 核心业务实现

3.1 第三方Token验证服务

@Service
public class OAuthTokenValidationService {
    private final WebClient webClient;
    private final OAuth2ClientRegistrationRepository clientRegistrationRepository;

    public OAuthTokenValidationService(WebClient.Builder webClientBuilder, OAuth2ClientRegistrationRepository clientRegistrationRepository) {
        this.webClient = webClientBuilder.build();
        this.clientRegistrationRepository = clientRegistrationRepository;
    }

    public Optional<OAuth2UserInfo> validateToken(String provider, String accessToken) {
        OAuth2ClientRegistration registration = clientRegistrationRepository.findByRegistrationId(provider);
        if (registration == null) {
            return Optional.empty();
        }

        try {
            Map<String, Object> userInfo = webClient.get()
                    .uri(registration.getProviderDetails().getUserInfoEndpoint().getUri())
                    .headers(headers -> headers.setBearerAuth(accessToken))
                    .retrieve()
                    .bodyToMono(Map.class)
                    .block();

            if (userInfo == null) return Optional.empty();

            // 统一封装用户信息(适配不同平台字段差异)
            OAuth2UserInfo userInfoDto = switch (provider) {
                case "google" -> new OAuth2UserInfo(
                        userInfo.get("sub").toString(),
                        userInfo.get("email").toString(),
                        userInfo.get("name").toString(),
                        userInfo.get("picture").toString()
                );
                case "facebook" -> new OAuth2UserInfo(
                        userInfo.get("id").toString(),
                        userInfo.get("email").toString(),
                        userInfo.get("name").toString(),
                        ((Map<String, String>) userInfo.get("picture")).get("url")
                );
                default -> null;
            };
            return Optional.ofNullable(userInfoDto);
        } catch (Exception e) {
            return Optional.empty();
        }
    }

    // 统一用户信息DTO
    public record OAuth2UserInfo(String externalId, String email, String name, String avatar) {}
}

3.2 自定义JWT工具类

@Component
public class JwtTokenProvider {
    @Value("${jwt.secret}")
    private String secretKey;
    @Value("${jwt.expiration-ms}")
    private long expirationMs;

    // 生成自定义JWT
    public String generateToken(OAuth2UserInfo userInfo) {
        Date now = new Date();
        Date expiryDate = new Date(now.getTime() + expirationMs);

        return Jwts.builder()
                .setSubject(userInfo.email())
                .claim("externalId", userInfo.externalId())
                .claim("name", userInfo.name())
                .claim("avatar", userInfo.avatar())
                .setIssuedAt(now)
                .setExpiration(expiryDate)
                .signWith(SignatureAlgorithm.HS512, secretKey)
                .compact();
    }

    // 解析JWT获取用户邮箱
    public String getEmailFromToken(String token) {
        return Jwts.parserBuilder()
                .setSigningKey(secretKey)
                .build()
                .parseClaimsJws(token)
                .getBody()
                .getSubject();
    }

    // 验证JWT有效性
    public boolean validateToken(String token) {
        try {
            Jwts.parserBuilder().setSigningKey(secretKey).build().parseClaimsJws(token);
            return true;
        } catch (JwtException | IllegalArgumentException e) {
            return false;
        }
    }
}

3.3 OAuth转自定义JWT接口

@RestController
@RequestMapping("/api/auth")
public class AuthController {
    private final OAuthTokenValidationService validationService;
    private final JwtTokenProvider jwtTokenProvider;

    public AuthController(OAuthTokenValidationService validationService, JwtTokenProvider jwtTokenProvider) {
        this.validationService = validationService;
        this.jwtTokenProvider = jwtTokenProvider;
    }

    @PostMapping("/oauth/exchange")
    public ResponseEntity<?> exchangeToken(@RequestParam String provider, @RequestParam String accessToken) {
        return validationService.validateToken(provider, accessToken)
                .map(userInfo -> {
                    String customJwt = jwtTokenProvider.generateToken(userInfo);
                    return ResponseEntity.ok(Map.of("accessToken", customJwt, "tokenType", "Bearer"));
                })
                .orElse(ResponseEntity.badRequest().body("Invalid OAuth token or provider"));
    }
}

3.4 JWT认证过滤器

@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    private final JwtTokenProvider jwtTokenProvider;

    public JwtAuthenticationFilter(JwtTokenProvider jwtTokenProvider) {
        this.jwtTokenProvider = jwtTokenProvider;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String token = getJwtFromRequest(request);

        if (token != null && jwtTokenProvider.validateToken(token)) {
            String email = jwtTokenProvider.getEmailFromToken(token);
            // 构建认证对象,这里可根据业务添加角色信息
            UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(
                    email, null, Collections.emptyList()
            );
            auth.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            SecurityContextHolder.getContext().setAuthentication(auth);
        }

        filterChain.doFilter(request, response);
    }

    private String getJwtFromRequest(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authorization");
        if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7);
        }
        return null;
    }
}

3.5 Security配置类

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    private final JwtAuthenticationFilter jwtAuthenticationFilter;

    public SecurityConfig(JwtAuthenticationFilter jwtAuthenticationFilter) {
        this.jwtAuthenticationFilter = jwtAuthenticationFilter;
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.csrf(csrf -> csrf.disable())
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/api/auth/**").permitAll()
                        .anyRequest().authenticated()
                )
                .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }
}

4. 补充配置(application.yml)

添加JWT相关配置:

jwt:
  secret: YOUR_SECRET_KEY_HERE_至少32位字符串
  expiration-ms: 86400000 # 24小时过期

内容的提问来源于stack exchange,提问作者Ivan Danilov

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 21:40:51