【问题标题】:Cloud Firestore error, FirebaseApp with name [DEFAULT] doesn't existCloud Firestore 错误,名称为 [DEFAULT] 的 FirebaseApp 不存在
【发布时间】:2020-11-23 15:59:26
【问题描述】:

我正在使用 Spring boot 和 Spring security 来构建后端应用程序,我正在将用户存储到 Cloud Firestore 非关系数据库中,我正在使用 Google 在 Firebase 平台上提供的 Admin SDK 令牌。我正在通过以下方式初始化我的 Firestore。

@Service
public class UserFirestoreInitialize {
    
    @Value("classpath:static/gamingplatform-c922d-firebase-adminsdk-c25o8-06e92edfd5.json")
    Resource resourceFile;
    
    @PostConstruct
    public void initialize() {
        try {
            InputStream serviceAccount = resourceFile.getInputStream();
            GoogleCredentials cred = GoogleCredentials.fromStream(serviceAccount)
                                        .createScoped("https://www.googleapis.com/auth/datastore");
            FirebaseOptions options = FirebaseOptions.builder()
                    .setCredentials(cred)
                    .setDatabaseUrl("FIREBASE_URL")
                    .build();
            FirebaseApp.initializeApp(options);
        } 
        catch (Exception e) {
            e.printStackTrace();
        }
    }
}

我还有一个类似这样的配置类。

@Configuration
public class UserFirestoreConfiguration{
    @Bean
    public Firestore getFirestore(){
        return FirestoreClient.getFirestore();
    }
}

之后,我可以轻松地在我的 UserService 中使用这个 bean,如下所示:

@Service
public class UserService{
    
    @Autowired
    private Firestore firestore;

    public User getUser(){
        //Query for user.
    }
    //Post, Put, delete
}

这在某些时候奏效了。当我将 Spring Security 添加到我的应用程序中时,问题就来了。当我运行 Maven 安装时,应用程序没有构建,问题如下:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'jwtTokenFilter': Unsatisfied dependency expressed through field 'userDetailsService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userServiceDetailsImpl' defined in file [/Users/igorzelaya/SoftwareDev/D1Gaming-User-Back-end1/target/classes/com/d1gaming/user/security/UserServiceDetailsImpl.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userService' defined in file [/Users/igorzelaya/SoftwareDev/D1Gaming-User-Back-end1/target/classes/com/d1gaming/user/user/UserService.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.d1gaming.user.user.UserService]: Constructor threw exception; nested exception is java.lang.IllegalStateException: FirebaseApp with name [DEFAULT] doesn't exist.

我试图隔离问题,我创建了另一个项目并尝试连接到我的数据库,就像我在上面的 sn-p 中所做的那样^,一切正常,然后我一个一个地复制了我的配置类,我发现我的应用程序在我添加 JWT 令牌过滤器类的那一刻开始崩溃,这个类看起来像这样

@Component
public class JwtTokenFilter extends OncePerRequestFilter{

    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    
    @Autowired
    private UserServiceDetailsImpl userDetailsService;
    
    @Override
    //Get authorization header and validate it.
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)throws ServletException, IOException, NullPointerException {
        try {
            String jwt = parseJwt(request);
            if(jwt != null && jwtTokenUtil.validate(jwt)) {
                String username = jwtTokenUtil.getUserNameFromJwtToken(jwt);
                UserDetails userDetails = userDetailsService.loadUserByUsername(username);
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails,null,userDetails.getAuthorities());
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        } 
        catch(Exception e) {
            logger.error("Cannot set user authentication: {}", e);
        }
        chain.doFilter(request, response);
    }
    
    private String parseJwt(HttpServletRequest request) {
        String headerAuth = request.getHeader("Authorization");
        if(StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ")) {
            return headerAuth.substring(7, headerAuth.length());
        }
        return null;
    }
}

正如您在上面显示的堆栈跟踪中注意到的那样,错误开始于注入此类的依赖项、jwtTokenUtil 类和 UserDetailsImpl JwtTokenUtil 类是这样的:

@Component
public class JwtTokenUtil {

    @Value("${app.jwtSecret}")
    private String jwtSecret;
    
    @Value("${app.jwtExpirationMs}")
    private int jwtExpirationMs;
    
    
    private final Logger logger = LoggerFactory.getLogger(JwtTokenUtil.class);
    
    public String generateJwtToken(Authentication authentication) {
        UserDetailsImpl userPrincipal = (UserDetailsImpl) authentication.getPrincipal();
        return Jwts.builder()
                .setSubject(userPrincipal.getUsername())
                .setIssuedAt(new Date())
                .setExpiration(new Date((new Date()).getTime() + jwtExpirationMs))
                .signWith(SignatureAlgorithm.HS512, jwtSecret)
                .compact();
    }
    
    public String getUserNameFromJwtToken(String token) {
        return Jwts.parser().setSigningKey(jwtSecret)
                .parseClaimsJws(token).getBody().getSubject();
    }
    
    
    public String getUserId(String token) {
        Claims claims = Jwts.parser()
                .setSigningKey(jwtSecret)
                .parseClaimsJws(token)
                .getBody();
        return claims.getSubject().split(",")[0];
    }

    
    public String getUsername(String token) {
        Claims claims = Jwts.parser()
                .setSigningKey(jwtSecret)
                .parseClaimsJws(token)
                .getBody();
        return claims.getSubject().split(",")[0];
    }

    public Date getExpirationDate(String token) {
        Claims claims = Jwts.parser()
                .setSigningKey(jwtSecret)
                .parseClaimsJws(token)
                .getBody();
        return claims.getExpiration();
    }

    public boolean validate(String token) {
        try {
            Jwts.parser()
                .setSigningKey(jwtSecret).parseClaimsJws(token);
            return true;
        }
        catch(SignatureException e) {
            logger.error("Invalid JWT signature - {}",e.getMessage());
        }
        catch(MalformedJwtException e) {
            logger.error("Invalid JWT token - {}", e.getMessage());
        }
        catch(ExpiredJwtException e) {
            logger.error("Invalid JWT token - {}",e.getMessage());
        }
        catch(UnsupportedJwtException e) {
            logger.error("Invalid JWT token - {}", e.getMessage());     
        }
        catch(IllegalArgumentException e) {
            logger.error("Invalid JWT token - {}", e.getMessage());
        }
        return false;
    }
}

UserServiceDetailsImpl 类:

public class UserDetailsImpl implements UserDetails{
    
    private static final long serialVersionUID = 1L;
    
    private String userId;
    
    private String userRealName;
    
    private String userName;
    
    @JsonIgnore
    private String userPassword;
    
    private String userEmail;
    
    private UserStatus userStatusCode;
    
    private Team userTeam;
    
    private Map<String,Object> userBilling;

    private String userCountry;
    
    private int userTokens;
    
    private double userCash;
    
    private Map<String, Object> userBirthDate;

    private Collection<? extends GrantedAuthority> authorities;

    public UserDetailsImpl(String userId, String userRealName, String userName, String userPassword, String userEmail,
            UserStatus userStatusCode, Team userTeam, Map<String, Object> userBilling, String userCountry,
            int userTokens, double userCash, Map<String, Object> userBirthDate,
            Collection<? extends GrantedAuthority> authorities) {
        this.userId = userId;
        this.userRealName = userRealName;
        this.userName = userName;
        this.userPassword = userPassword;
        this.userEmail = userEmail;
        this.userStatusCode = userStatusCode;
        this.userTeam = userTeam;
        this.userBilling = userBilling;
        this.userCountry = userCountry;
        this.userTokens = userTokens;
        this.userCash = userCash;
        this.userBirthDate = userBirthDate;
        this.authorities = authorities;
    }
    
    public static UserDetailsImpl build(User user) {
        List<GrantedAuthority> authorities = user.getUserRoles().stream()
                .map(role -> new SimpleGrantedAuthority(role.getRoleType().name()))
                .collect(Collectors.toList());
        return new UserDetailsImpl(user.getUserId(),user.getUserRealName(),user.getUserName(),user.getUserPassword(),user.getUserEmail()
                        ,user.getStatusCode(),user.getUserTeam(),user.getUserBilling(),user.getUserCountry(),
                        user.getUserTokens(),user.getUserCash(),user.getUserBirthDate(),authorities
                    );
    }
//Getters and setters
}

如您所见,这些类都与 Firestore 无关,当时我很困惑,所以我做了以下操作。我注意到由于某些奇怪的原因,@PostConstruct 注释不起作用,Spring 没有在代码的第一个 sn-p 上初始化我的 initializeFirestore() 方法,所以我直接在上面放置了一个 initialize() 方法spring boot如下:

    @SpringBootApplication
    public class UserApplication(){
        public static void main(String[] args) {
            //Basically does the same as the initialize method mentioned before.
            UserFirestoreUtils.initialize(UserFirestoreUtils.getOptions());
            SpringApplication.run(D1GamingUserBackEnd1Application.class, args);
        }
    }

有趣的是,当我调试这个时,我遇到了以下错误:

    The dependencies of some of the beans in the application context form a cycle:

   jwtTokenFilter (field private com.d1gaming.user.security.UserServiceDetailsImpl com.d1gaming.user.security.JwtTokenFilter.userDetailsService)
┌─────┐
|  userServiceDetailsImpl defined in file [/Users/igorzelaya/SoftwareDev/D1Gaming-User-Back-end1/target/classes/com/d1gaming/user/security/UserServiceDetailsImpl.class]
↑     ↓
|  userService (field private org.springframework.security.crypto.password.PasswordEncoder com.d1gaming.user.user.UserService.passwordEncoder)
↑     ↓
|  userSecurityConfiguration (field com.d1gaming.user.security.UserServiceDetailsImpl com.d1gaming.user.security.UserSecurityConfiguration.userDetailsService)
└─────┘

奇怪的是,当我在其上运行 Maven 安装时,堆栈跟踪与我在问题开头显示的相同,我觉得 Cloud Firestore 上没有足够的文档,考虑到它是正常的是“新的”,但仍然很烦人,因为我找不到这样做的“正确方法”,而且谷歌文档显然还不够。很抱歉,如果我包含太多代码,我认为这是必要的,但无论如何感谢您的时间,如果有人可以帮助我,我将不胜感激。祝你有美好的一天。

【问题讨论】:

  • 您想只使用 firestore 数据库还是使用其他 firebase 产品?
  • 只是 Cloud Firestore

标签: java spring-boot spring-security google-cloud-firestore


【解决方案1】:

这看起来像是 Spring 依赖项和 Firebase Admin 之间的冲突。你可以试试Firestore client library。 Firebase admin SDK 是一个更广泛的库,部分代码可能会与 Spring 安全性发生冲突

【讨论】:

    猜你喜欢
    • 2016-09-28
    • 2016-09-17
    • 2019-12-04
    • 2016-12-07
    • 2021-10-17
    • 2017-04-25
    • 1970-01-01
    • 1970-01-01
    • 2019-07-09
    相关资源
    最近更新 更多