这一篇以“用Postman发送请求”的方式,来完成“一个新用户的注册”。将在下面几篇文章,完成前端框架的配置,以及前端用户注册页面的建立。从而完成整个“前端页面注册 - 后端校验 - 数据库存入数据”的流程。
代码改动
1. 首先添加用户注册的DTO(Data Transfer Object)
package com.quickstore.dto;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
public class RegisterRequest {
@NotBlank(message = "用户名不能为空")
@Size(min = 3, max = 20, message = "用户名长度必须在3-20之间")
private String username;
@NotBlank(message = "密码不能为空")
@Size(min = 6, max = 40, message = "密码长度必须在6-40之间")
private String password;
@NotBlank(message = "全名不能为空")
private String fullName;
@Pattern(regexp = "^(admin|staff|warehouse)$", message = "角色必须是 admin、staff 或 warehouse 之一")
private String role = "staff"; // 默认角色为staff
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getFullName() {
return fullName;
}
public void setFullName(String fullName) {
this.fullName = fullName;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
}
2. 在AuthController中添加注册接口
package com.quickstore.controller;
import com.quickstore.dto.RegisterRequest;
import com.quickstore.model.User;
import com.quickstore.security.JwtTokenProvider;
import com.quickstore.service.UserService;
import jakarta.validation.Valid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Collections;
@RestController
@RequestMapping("/auth")
public class AuthController {
private static final Logger logger = LoggerFactory.getLogger(AuthController.class);
private final UserService userService;
private final PasswordEncoder passwordEncoder;
private final JwtTokenProvider tokenProvider;
public AuthController(UserService userService, PasswordEncoder passwordEncoder, JwtTokenProvider tokenProvider) {
this.userService = userService;
this.passwordEncoder = passwordEncoder;
this.tokenProvider = tokenProvider;
}
@PostMapping("/register")
public ResponseEntity<?> register(@Valid @RequestBody RegisterRequest registerRequest) {
logger.info("Attempting to register new user: {}", registerRequest.getUsername());
// 检查用户名是否已存在
if (userService.findByUsername(registerRequest.getUsername()) != null) {
logger.warn("Registration failed: Username {} already exists", registerRequest.getUsername());
return ResponseEntity.badRequest().body("Username already exists");
}
// 创建新用户
User user = new User();
user.setUsername(registerRequest.getUsername());
user.setPasswordHash(passwordEncoder.encode(registerRequest.getPassword()));
user.setFullName(registerRequest.getFullName());
user.setRole(registerRequest.getRole().toLowerCase()); // 确保角色是小写的
// 保存用户
userService.save(user);
logger.info("User registered successfully: {}", user.getUsername());
return ResponseEntity.ok("User registered successfully");
}
@PostMapping("/login")
public ResponseEntity<LoginResponse> login(@RequestBody LoginRequest loginRequest) {
logger.info("Attempting login for user: {}", loginRequest.getUsername());
User user = userService.findByUsername(loginRequest.getUsername());
if (user != null && passwordEncoder.matches(loginRequest.getPassword(), user.getPasswordHash())) {
logger.info("Login successful for user: {}", user.getUsername());
UserDetails userDetails = new org.springframework.security.core.userdetails.User(
user.getUsername(),
user.getPasswordHash(),
Collections.singletonList(new org.springframework.security.core.authority.SimpleGrantedAuthority("ROLE_" + user.getRole().toUpperCase()))
);
String token = tokenProvider.generateToken(userDetails);
return ResponseEntity.ok(new LoginResponse(token, user.getUsername(), user.getRole()));
}
logger.warn("Login failed for user: {}", loginRequest.getUsername());
return ResponseEntity.badRequest().body(null);
}
}
class LoginRequest {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
class LoginResponse {
private String token;
private String username;
private String role;
public LoginResponse(String token, String username, String role) {
this.token = token;
this.username = username;
this.role = role;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
}
3. 在UserService中添加save方法
package com.quickstore.service;
import com.quickstore.model.User;
import org.springframework.security.core.userdetails.UserDetailsService;
public interface UserService extends UserDetailsService {
User findByUsername(String username);
User save(User user);
}
4. 在UserServiceImpl中实现save方法
package com.quickstore.service.impl;
import com.quickstore.model.User;
import com.quickstore.repository.UserRepository;
import com.quickstore.service.UserService;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.Collections;
@Service
public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
public UserServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("User not found with username: " + username);
}
return new org.springframework.security.core.userdetails.User(
user.getUsername(),
user.getPasswordHash(),
Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + user.getRole().toUpperCase()))
);
}
@Override
public User findByUsername(String username) {
return userRepository.findByUsername(username);
}
@Override
public User save(User user) {
if (user.getRole() != null) {
user.setRole(user.getRole().toLowerCase());
}
return userRepository.save(user);
}
}
验证用户注册功能
- 发送
POST请求到:http://localhost:8080/api/auth/register - Header设置为:
Content-Type: application/json
- 请求体(body)设置为:
{
"username": "testuser",
"password": "password123",
"fullName": "Test User",
"role": "staff"
}
注意:根据建表时候的规定,role只能从三个限定角色中选择一个。后续在前端页面也会设置为下拉框。
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
username TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
full_name TEXT,
role TEXT CHECK (role IN ('admin', 'staff', 'warehouse')),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
如果注册成功,会返回200状态。
成功!
注册成功之后,可以用这个账号进行登录。
成功!
接下来几篇,将搭建前端框架,并用React完成用户注册页面。