使用Spring Boot + Angular构建安全的登录注册系统

发布于:2025-08-08 ⋅ 阅读:(51) ⋅ 点赞:(0)

现代全栈开发的完美实践

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
图:系统架构示意图

在当今的Web应用开发中,用户认证是核心功能之一。本文将手把手教你使用Spring Boot作为后端、Angular作为前端,构建一个完整的登录注册系统。


技术栈概览
后端 (Spring Boot) 前端 (Angular)
Spring Security Angular Router
Spring Data JPA Reactive Forms
JWT 认证 HTTP Client
H2 数据库 (开发环境) Auth Guard
Lombok Interceptor

第一部分:Spring Boot后端实现

1. 项目初始化

使用 Spring Initializr 创建项目,选择:

  • Spring Web
  • Spring Security
  • Spring Data JPA
  • H2 Database
  • Lombok
2. 用户实体类
@Entity
@Data
@NoArgsConstructor
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(unique = true)
    private String email;
    
    private String password;
    private String role = "USER";
}
3. JWT工具类
@Component
public class JwtUtil {
    private final String SECRET_KEY = "your-secret-key";
    
    public String generateToken(UserDetails userDetails) {
        return Jwts.builder()
                .setSubject(userDetails.getUsername())
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10)) // 10小时
                .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
                .compact();
    }
    
    public Boolean validateToken(String token, UserDetails userDetails) {
        final String username = extractUsername(token);
        return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    }
}
4. 安全配置
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Autowired
    private UserDetailsServiceImpl userDetailsService;
    
    @Autowired
    private JwtUtil jwtUtil;
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeRequests()
            .antMatchers("/api/auth/**").permitAll()
            .anyRequest().authenticated()
            .and()
            .addFilter(new JwtAuthenticationFilter(authenticationManager(), jwtUtil))
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
5. 认证控制器
@RestController
@RequestMapping("/api/auth")
public class AuthController {
    
    @Autowired
    private AuthenticationManager authenticationManager;
    
    @Autowired
    private UserRepository userRepository;
    
    @Autowired
    private JwtUtil jwtUtil;
    
    @PostMapping("/register")
    public ResponseEntity<?> register(@RequestBody RegisterRequest request) {
        if (userRepository.existsByEmail(request.getEmail())) {
            return ResponseEntity.badRequest().body("Email already exists");
        }
        
        User user = new User();
        user.setEmail(request.getEmail());
        user.setPassword(new BCryptPasswordEncoder().encode(request.getPassword()));
        userRepository.save(user);
        
        return ResponseEntity.ok("User registered successfully");
    }
    
    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody LoginRequest request) {
        try {
            Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(
                    request.getEmail(),
                    request.getPassword()
                )
            );
            
            UserDetails userDetails = (UserDetails) authentication.getPrincipal();
            String jwt = jwtUtil.generateToken(userDetails);
            
            return ResponseEntity.ok(new AuthResponse(jwt));
        } catch (BadCredentialsException e) {
            return ResponseEntity.status(401).body("Invalid credentials");
        }
    }
}

第二部分:Angular前端实现

1. 项目初始化
ng new auth-frontend
ng add @angular/material
ng g s services/auth
ng g guard guards/auth
2. 认证服务
@Injectable({ providedIn: 'root' })
export class AuthService {
  private apiUrl = 'http://localhost:8080/api/auth';
  private currentUserSubject = new BehaviorSubject<any>(null);
  
  constructor(private http: HttpClient) {
    const token = localStorage.getItem('token');
    if (token) {
      this.currentUserSubject.next(this.decodeToken(token));
    }
  }

  register(credentials: { email: string; password: string }) {
    return this.http.post(`${this.apiUrl}/register`, credentials);
  }

  login(credentials: { email: string; password: string }) {
    return this.http.post<{ token: string }>(`${this.apiUrl}/login`, credentials)
      .pipe(tap(res => {
        localStorage.setItem('token', res.token);
        this.currentUserSubject.next(this.decodeToken(res.token));
      }));
  }

  logout() {
    localStorage.removeItem('token');
    this.currentUserSubject.next(null);
  }

  private decodeToken(token: string): any {
    try {
      return JSON.parse(atob(token.split('.')[1]));
    } catch (e) {
      return null;
    }
  }
}
3. HTTP拦截器
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  intercept(req: HttpRequest<any>, next: HttpHandler) {
    const token = localStorage.getItem('token');
    
    if (token) {
      const cloned = req.clone({
        headers: req.headers.set('Authorization', `Bearer ${token}`)
      });
      return next.handle(cloned);
    }
    
    return next.handle(req);
  }
}
4. 路由守卫
@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {
  constructor(private authService: AuthService, private router: Router) {}
  
  canActivate(): boolean {
    if (this.authService.isAuthenticated()) {
      return true;
    }
    
    this.router.navigate(['/login']);
    return false;
  }
}
5. 登录组件模板
<mat-card>
  <mat-card-header>
    <mat-card-title>Login</mat-card-title>
  </mat-card-header>
  
  <mat-card-content>
    <form [formGroup]="loginForm" (ngSubmit)="onSubmit()">
      <mat-form-field>
        <input matInput placeholder="Email" formControlName="email">
        <mat-error>Valid email required</mat-error>
      </mat-form-field>
      
      <mat-form-field>
        <input matInput type="password" placeholder="Password" formControlName="password">
      </mat-form-field>
      
      <button mat-raised-button color="primary" type="submit">Login</button>
    </form>
  </mat-card-content>
</mat-card>

关键功能亮点

  1. JWT无状态认证
    使用JSON Web Token实现无状态会话管理

  2. 密码安全存储
    BCrypt算法加密存储密码

  3. 路由守卫
    防止未授权用户访问受保护路由

  4. 响应式表单
    实时表单验证和错误处理

  5. HTTP拦截器
    自动附加JWT到请求头


部署注意事项

  1. 生产环境切换MySQL数据库:
spring.datasource.url=jdbc:mysql://localhost:3306/auth_db
spring.datasource.username=root
spring.datasource.password=yourpassword
  1. 配置CORS策略:
@Bean
public WebMvcConfigurer corsConfigurer() {
    return new WebMvcConfigurer() {
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**")
                .allowedOrigins("http://your-angular-app.com")
                .allowedMethods("*");
        }
    };
}
  1. 环境变量管理敏感信息

完整项目结构

backend/
├─ src/
│  ├─ main/
│  │  ├─ java/
│  │  │  ├─ controller/
│  │  │  ├─ security/
│  │  │  ├─ model/
│  │  │  ├─ repository/
│  │  ├─ resources/
│  │  │  ├─ application.properties

frontend/
├─ src/
│  ├─ app/
│  │  ├─ components/
│  │  │  ├─ login/
│  │  │  ├─ register/
│  │  ├─ services/
│  │  ├─ guards/
│  ├─ environments/

总结

通过这个教程,我们实现了:
✅ 基于Spring Security的安全认证
✅ JWT令牌的生成与验证
✅ Angular的响应式表单处理
✅ 路由权限控制
✅ 前后端分离架构

扩展建议

  • 添加密码重置功能
  • 实现第三方登录(OAuth2)
  • 增加双因素认证
  • 集成验证码机制

GitHub完整代码
后端代码仓库
前端代码仓库


通过这个全栈解决方案,你可以快速构建安全可靠的用户认证系统,为你的应用奠定坚实的安全基础。欢迎在评论区交流遇到的问题或优化建议!