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

Angular+Spring Boot应用邮箱密码认证方案咨询(限定注册邮箱)

Hey there! Let's break down how to implement this email-password auth system step by step, tailored to your tech stack (Angular + Spring Boot + GAE + Cloud Datastore) and the "only authorized emails can register" requirement.

整体设计思路

We'll build a stateless JWT-based auth system (perfect for GAE's auto-scaling environment) with two core flows:

  1. Registration: Only emails in your pre-approved whitelist can create an account
  2. Login: Validated users get a JWT token to access protected routes/APIs

The key guardrail is a whitelist check before allowing any account creation, paired with secure password storage and token-based authentication.

Angular Frontend Implementation

1. Auth Components & Forms

Create two core components:

  • LoginComponent: Form with email/password fields, submits to backend /api/auth/login
  • RegisterComponent: Form with email/password fields, first calls a /api/auth/check-allowed endpoint to verify the email is whitelisted before submitting the registration request

Example snippet for the registration check:

// auth.service.ts
checkEmailAllowed(email: string): Observable<boolean> {
  return this.http.get<boolean>(`/api/auth/check-allowed?email=${email}`);
}

register(user: {email: string, password: string}): Observable<any> {
  return this.http.post('/api/auth/register', user);
}

2. Auth State Management

  • Use an AuthService to store the JWT token (preferably in an HttpOnly cookie for XSS protection, or localStorage with extra XSS safeguards)
  • Implement an Angular HTTP Interceptor to automatically attach the Authorization: Bearer {token} header to all protected requests
  • Add AuthGuard to restrict access to authenticated routes:
// auth.guard.ts
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
  if (this.authService.isLoggedIn()) {
    return true;
  }
  this.router.navigate(['/login']);
  return false;
}
Spring Boot Backend Implementation

1. Dependencies Setup

Add these to your pom.xml (or build.gradle):

  • Spring Security
  • Spring Data Cloud Datastore
  • JJWT (for JWT handling)
  • BCrypt Password Encoder

2. Core Entities

Define two Datastore entities:

// AllowedEmail.java (whitelist)
@Entity(name = "allowed_emails")
public class AllowedEmail {
  @Id
  private String email;

  // getters/setters
}

// AppUser.java (registered users)
@Entity(name = "app_users")
public class AppUser {
  @Id
  private String email;
  private String encryptedPassword;
  private LocalDateTime createdAt;

  // getters/setters
}

3. Whitelist Validation Logic

Create a service to check if an email is in the allowed list:

// AllowedEmailService.java
@Service
public class AllowedEmailService {
  private final AllowedEmailRepository repo;

  public AllowedEmailService(AllowedEmailRepository repo) {
    this.repo = repo;
  }

  public boolean isEmailAllowed(String email) {
    return repo.existsById(email.toLowerCase());
  }
}

4. Auth Controller

Implement registration and login endpoints, with whitelist checks for registration:

// AuthController.java
@RestController
@RequestMapping("/api/auth")
public class AuthController {
  private final AppUserService userService;
  private final AllowedEmailService allowedEmailService;
  private final JwtUtil jwtUtil;
  private final PasswordEncoder passwordEncoder;

  // constructor injection

  @GetMapping("/check-allowed")
  public ResponseEntity<Boolean> checkEmailAllowed(@RequestParam String email) {
    return ResponseEntity.ok(allowedEmailService.isEmailAllowed(email));
  }

  @PostMapping("/register")
  public ResponseEntity<?> register(@RequestBody RegisterRequest request) {
    if (!allowedEmailService.isEmailAllowed(request.getEmail())) {
      return ResponseEntity.status(HttpStatus.FORBIDDEN).body("Email not authorized");
    }
    if (userService.existsByEmail(request.getEmail())) {
      return ResponseEntity.badRequest().body("Email already registered");
    }
    AppUser user = new AppUser();
    user.setEmail(request.getEmail().toLowerCase());
    user.setEncryptedPassword(passwordEncoder.encode(request.getPassword()));
    user.setCreatedAt(LocalDateTime.now());
    userService.save(user);
    return ResponseEntity.ok("Account created successfully");
  }

  @PostMapping("/login")
  public ResponseEntity<AuthResponse> login(@RequestBody LoginRequest request) {
    AppUser user = userService.findByEmail(request.getEmail())
      .orElseThrow(() -> new RuntimeException("Invalid credentials"));
    if (!passwordEncoder.matches(request.getPassword(), user.getEncryptedPassword())) {
      throw new RuntimeException("Invalid credentials");
    }
    String token = jwtUtil.generateToken(user.getEmail());
    return ResponseEntity.ok(new AuthResponse(token));
  }
}

5. Spring Security Configuration

Configure stateless auth with JWT filtering:

// SecurityConfig.java
@Configuration
@EnableWebSecurity
public class SecurityConfig {
  private final JwtAuthenticationFilter jwtAuthFilter;
  private final UserDetailsService userDetailsService;

  // constructor injection

  @Bean
  public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http
      .csrf().disable()
      .authorizeHttpRequests()
        .requestMatchers("/api/auth/**").permitAll()
        .anyRequest().authenticated()
      .and()
      .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
      .and()
      .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
    return http.build();
  }

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

6. JWT Utility Class

Handle token generation and validation:

// JwtUtil.java
@Component
public class JwtUtil {
  @Value("${jwt.secret}")
  private String secretKey;
  @Value("${jwt.expirationMs}")
  private long expirationMs;

  public String generateToken(String email) {
    return Jwts.builder()
      .setSubject(email)
      .setIssuedAt(new Date())
      .setExpiration(new Date(System.currentTimeMillis() + expirationMs))
      .signWith(SignatureAlgorithm.HS256, secretKey)
      .compact();
  }

  public String extractEmail(String token) {
    return Jwts.parser().setSigningKey(secretKey)
      .parseClaimsJws(token).getBody().getSubject();
  }

  public boolean isTokenValid(String token, UserDetails userDetails) {
    final String email = extractEmail(token);
    return (email.equals(userDetails.getUsername())) && !isTokenExpired(token);
  }

  private boolean isTokenExpired(String token) {
    return Jwts.parser().setSigningKey(secretKey)
      .parseClaimsJws(token).getBody().getExpiration().before(new Date());
  }
}
GAE & Cloud Datastore Adaptations
  • Datastore Indexes: Add an index for the allowed_emails entity in datastore-indexes.xml to speed up email lookups:
    <datastore-indexes autoGenerate="true">
      <datastore-index kind="allowed_emails" ancestor="false">
        <property name="email" direction="asc"/>
      </datastore-index>
    </datastore-indexes>
    
  • GAE Environment Variables: Store sensitive values like jwt.secret in GAE's environment variables (instead of hardcoding) and access them via Spring's @Value annotation.
  • HTTPS Enforcement: Configure Spring Security to force all requests over HTTPS (GAE supports this natively):
    http.requiresChannel().anyRequest().requiresSecure();
    
Critical Security Notes
  • Password Storage: Never store plaintext passwords — always use BCrypt (or Argon2 for stronger protection).
  • JWT Security: Use a long, random secret key (store it in GAE Secrets Manager for production), set reasonable token expiration times, and use HttpOnly cookies to store tokens to mitigate XSS attacks.
  • Rate Limiting: Add rate limiting to registration/login endpoints (use GAE's built-in traffic management or a library like spring-boot-starter-rate-limiter) to prevent brute-force attacks.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 06:48:49