1. 项目概述
这个前后端分离的登录项目是现代化Web开发的典型入门案例。我最近在团队内部做技术培训时,专门用这个案例演示了如何构建一个符合当前行业标准的认证系统。相比传统单体应用,分离架构让前端React/Vue与后端Spring Boot/Django各司其职,通过RESTful API进行通信,既提升了开发效率又便于独立部署。
登录功能看似简单,实则涉及诸多关键技术点:前端表单验证、加密传输、JWT令牌管理、跨域处理、后端密码加密存储、接口防刷等。我在实际开发中发现,很多新手容易在这些环节踩坑,比如忘记处理CSRF防护或JWT刷新机制不完善。接下来我会结合具体代码,拆解每个环节的实现要点。
2. 技术选型与架构设计
2.1 前端技术栈
采用Vue 3 + TypeScript的组合,相比传统选项有三大优势:
- Composition API更适合封装可复用的认证逻辑
- Axios拦截器能统一处理401未授权跳转
- Pinia状态管理方便跨组件共享用户信息
关键依赖版本:
bash复制"dependencies": {
"vue": "^3.3.0",
"axios": "^1.4.0",
"jsencrypt": "^3.3.2" # RSA加密库
}
2.2 后端技术栈
Spring Boot 3.x + Spring Security组合提供了开箱即用的安全功能:
- 内置BCrypt密码编码器
- 可配置的认证过滤器链
- 灵活的CORS策略支持
建议的Gradle配置:
groovy复制implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
3. 核心功能实现
3.1 前端登录表单实现
关键点在于:
- 使用vee-validate进行表单校验
- 密码字段采用RSA加密传输
- 处理Loading状态防止重复提交
典型登录组件代码:
vue复制<script setup>
const schema = yup.object({
username: yup.string().required().min(4),
password: yup.string().required().min(8)
});
const { handleSubmit } = useForm({ validationSchema: schema });
const onSubmit = handleSubmit(async (values) => {
const encrypted = encrypt(values.password); // RSA加密
const { data } = await axios.post('/auth/login', {
...values,
password: encrypted
});
localStorage.setItem('token', data.token);
});
</script>
3.2 后端认证接口
Spring Security配置要点:
java复制@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable()) // 根据实际需求配置
.authorizeHttpRequests(auth -> auth
.requestMatchers("/auth/**").permitAll()
.anyRequest().authenticated()
)
.addFilterBefore(jwtFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
密码加密存储示例:
java复制@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
public User register(User user) {
user.setPassword(passwordEncoder.encode(user.getPassword()));
return userRepository.save(user);
}
4. JWT令牌管理
4.1 令牌生成与验证
JWT工具类核心方法:
java复制public String generateToken(UserDetails user) {
return Jwts.builder()
.setSubject(user.getUsername())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 3600000)) // 1小时
.signWith(SignatureAlgorithm.HS512, secretKey)
.compact();
}
public Boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token);
return true;
} catch (Exception e) {
log.error("Invalid JWT: {}", e.getMessage());
return false;
}
}
4.2 前端令牌处理
Axios请求拦截器配置:
javascript复制axios.interceptors.request.use(config => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
axios.interceptors.response.use(
response => response,
error => {
if (error.response?.status === 401) {
router.push('/login');
}
return Promise.reject(error);
}
);
5. 安全增强措施
5.1 接口防护策略
- 登录接口限流:
java复制@RateLimiter(value = 5, key = "#request.getRemoteAddr()")
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest request) {
// 认证逻辑
}
- 密码强度校验正则:
java复制String pattern = "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=])(?=\\S+$).{8,}$";
if (!password.matches(pattern)) {
throw new WeakPasswordException();
}
5.2 敏感数据保护
- 响应数据脱敏处理:
java复制public UserDTO convert(User user) {
return UserDTO.builder()
.username(user.getUsername())
.email(desensitizeEmail(user.getEmail()))
.build();
}
private String desensitizeEmail(String email) {
return email.replaceAll("(^\\w{2})[^@]*(@.*$)", "$1****$2");
}
6. 部署与联调
6.1 跨域解决方案
后端CORS配置(生产环境应更严格):
java复制@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(List.of("http://localhost:8080"));
config.setAllowedMethods(List.of("GET","POST"));
config.setAllowedHeaders(List.of("*"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return source;
}
6.2 Nginx代理配置
前后端分离部署示例:
nginx复制server {
listen 80;
server_name example.com;
location /api {
proxy_pass http://backend:8080;
proxy_set_header Host $host;
}
location / {
root /var/www/frontend;
try_files $uri $uri/ /index.html;
}
}
7. 常见问题排查
7.1 典型错误案例
- 跨域请求失败:
- 检查后端CORS配置是否包含前端地址
- 确认OPTIONS预检请求返回200
- JWT令牌失效:
- 检查令牌过期时间设置
- 验证签名密钥前后端是否一致
- 密码验证不通过:
- 确认BCrypt加密版本一致
- 检查数据库存储的是加密后的密码
7.2 调试技巧
- 使用Postman测试流程:
code复制1. POST /auth/register 注册用户
2. POST /auth/login 获取JWT
3. 在Authorization头添加Bearer令牌
4. 测试GET /api/protected 验证认证
- 前端调试工具:
- Chrome开发者工具查看Network请求头
- 使用Vue Devtools检查Pinia状态
8. 项目优化方向
8.1 功能扩展建议
- 增加OAuth2.0第三方登录
- 实现双因素认证
- 添加图形验证码防暴力破解
8.2 性能优化方案
- Redis缓存用户信息:
java复制@Cacheable(value = "user", key = "#username")
public UserDetails loadUserByUsername(String username) {
// 数据库查询
}
- JWT黑名单处理:
java复制@CachePut(value = "blacklist", key = "#token")
public void invalidateToken(String token) {
// 将失效令牌存入Redis
}
在实际项目中,我建议先用Postman完整测试所有接口,再开发前端界面。遇到跨域问题时,可暂时关闭浏览器安全策略进行调试(chrome --disable-web-security),但正式环境必须配置正确的CORS策略。对于生产环境,一定要启用HTTPS并考虑添加WAF防护。