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

Spring Boot:如何将OAuth服务商获取的用户信息持久化到数据库

问题:Spring Boot + Auth0 OAuth2登录后用户信息未存入数据库

我正在使用Spring Boot搭建基于OAuth2认证服务提供商的应用,后台采用Auth0实现登录流程。应用已正确配置OAuth客户端相关信息(在application.properties中定义),目前可以通过谷歌账号无缝注册并登录应用,但登录后用户信息并未存入数据库,无法实现角色关联、路由权限控制等业务需求。

以下是我的相关代码:

CustomOAuth2User类

package com.interco.reconciliation.model;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.core.user.OAuth2User;

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

public class CustomOAuth2User extends User implements OAuth2User {

    private final Map<String, Object> attributes;

    @Override
    public Map<String, Object> getAttributes() {
        return this.attributes;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return getRoles();
    }

    @Override
    public String getName() {
        return getProviderId();
    }

    public CustomOAuth2User(User user, Map<String, Object> attributes) {
        super(user.getUsername(), user.getEmail(), user.getProviderId(), user.getRoles());
        this.attributes = attributes;
    }
}

CustomOAuth2UserService类

package com.interco.reconciliation.service;

import java.util.Optional;
import java.util.logging.Logger;

import com.interco.reconciliation.model.CustomOAuth2User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;

import com.interco.reconciliation.model.User;
import com.interco.reconciliation.repository.UserRepository;


@Service
public class CustomOAuth2UserService extends DefaultOAuth2UserService {

    @Autowired
    private UserRepository userRepository;

    private static final Logger LOGGER = Logger.getLogger(CustomOAuth2UserService.class.getName());


    @Override
    public OAuth2User loadUser(OAuth2UserRequest oAuth2UserRequest) {
        OAuth2User oAuth2User = super.loadUser(oAuth2UserRequest);
        LOGGER.info("OAuth2User loaded: " + oAuth2User.getAttributes().toString());
        String providerId = oAuth2User.getAttribute("id");
        User user = this.userRepository.findByProviderId(providerId).orElseGet(() -> {
            User newUser = new User();
            newUser.setUsername(oAuth2User.getAttribute("username"));
            newUser.setEmail(oAuth2User.getAttribute("email"));
            newUser.setProviderId(providerId);
            return this.userRepository.save(newUser);
        });

        LOGGER.info("Returning CustomOAuth2User");
        return new CustomOAuth2User(user, oAuth2User.getAttributes());
    }

}

WebSecurityConfig类

package com.interco.reconciliation.config;

import static org.springframework.security.config.Customizer.withDefaults;

import com.interco.reconciliation.service.CustomOAuth2UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
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.web.SecurityFilterChain;

import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

import java.io.IOException;


@Configuration
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig {

    @Value("${okta.oauth2.issuer}")
    private String issuer;
    @Value("${okta.oauth2.client-id}")
    private String clientId;
    private final CustomOAuth2UserService customOAuth2UserService;

    @Autowired
    public WebSecurityConfig(CustomOAuth2UserService customOAuth2UserService) {
        this.customOAuth2UserService = customOAuth2UserService;
    }

    @Bean
    public SecurityFilterChain configure(HttpSecurity http) throws Exception {
        http
                .csrf(AbstractHttpConfigurer::disable) // remove later or configure appropriately in production
                .cors(withDefaults()) // remove later or configure appropriately in production
                .authorizeHttpRequests(authorize -> authorize
                        .requestMatchers("/").permitAll()
                        .anyRequest().authenticated()
                )
                .oauth2Login(oauth2 -> oauth2
                        .userInfoEndpoint(userInfo ->
                                userInfo.userService(customOAuth2UserService)))
                .logout(logout -> logout
                        .addLogoutHandler(logoutHandler()));
        return http.build();
    }

    private LogoutHandler logoutHandler() {
        return (request, response, authentication) -> {
            try {
                String baseUrl = ServletUriComponentsBuilder.fromCurrentContextPath().build().toUriString();
                response.sendRedirect(issuer + "v2/logout?client_id=" + clientId + "&returnTo=" + baseUrl);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        };
    }

}

排查与解决方案

1. 修正Auth0用户属性字段名

Auth0返回的用户唯一标识字段不是id,而是sub;另外username字段Auth0默认返回的是name或nickname,而非username。这是导致无法正确获取用户信息、无法存入数据库的核心问题。

修改CustomOAuth2UserService中的字段获取逻辑:

@Override
public OAuth2User loadUser(OAuth2UserRequest oAuth2UserRequest) {
    OAuth2User oAuth2User = super.loadUser(oAuth2UserRequest);
    LOGGER.info("OAuth2User loaded: " + oAuth2User.getAttributes().toString());
    // 替换id为sub,Auth0的用户唯一标识是sub
    String providerId = oAuth2User.getAttribute("sub");
    User user = this.userRepository.findByProviderId(providerId).orElseGet(() -> {
        User newUser = new User();
        // 替换username为name,Auth0返回的用户名称字段是name
        newUser.setUsername(oAuth2User.getAttribute("name"));
        newUser.setEmail(oAuth2User.getAttribute("email"));
        newUser.setProviderId(providerId);
        return this.userRepository.save(newUser);
    });

    LOGGER.info("Returning CustomOAuth2User");
    return new CustomOAuth2User(user, oAuth2User.getAttributes());
}

2. 确保事务管理生效

DefaultOAuth2UserService的loadUser方法默认没有事务支持,可能导致userRepository.save(newUser)操作未提交到数据库。在CustomOAuth2UserService类上添加@Transactional注解:

@Service
@Transactional // 添加事务注解
public class CustomOAuth2UserService extends DefaultOAuth2UserService {
    // ... 原有代码
}

3. 验证User实体与Repository正确性

  • 确认User类的字段与数据库表字段正确映射,比如providerId、username、email对应的数据库列名无误。
  • 检查UserRepository的findByProviderId方法是否符合Spring Data JPA的命名规范,示例如下:
    public interface UserRepository extends JpaRepository<User, Long> {
        Optional<User> findByProviderId(String providerId);
    }
    

4. 检查Auth0控制台配置

  • 登录Auth0控制台,进入你的应用,在Applications > [你的应用] > User Management > Profiles中,确保已开启返回email、name等用户属性的权限。
  • 在Applications > [你的应用] > Settings > OAuth中,确认Allowed Callback URLs、Allowed Logout URLs配置正确。

5. 数据库配置验证

检查application.properties中的数据库配置,确保Hibernate能正确创建/更新表:

spring.datasource.url=jdbc:mysql://localhost:3306/your_db_name
spring.datasource.username=db_user
spring.datasource.password=db_password
spring.jpa.hibernate.ddl-auto=update # 确保设置为update或create,用于自动建表
spring.jpa.show-sql=true # 开启SQL日志,验证save操作是否执行

内容的提问来源于stack exchange,提问作者Abdou Seye

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 23:35:54