接下來,我們開始處理註冊功能,建立Entity。
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String email;
private String password;
public User() {
}
public User(Long id, String email, String password) {
this.id = id;
this.email = email;
this.password = password;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
Repository
public interface UserRepository extends JpaRepository<User, Long> {
User findByEmail(String email);
}
Service
新增用戶時,檢查email是否註冊過,如果有註冊就顯示例外,提醒使用者已經註冊了。
@Service
public class UserService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
}
public void createUser(User user) throws Exception {
User isEmailExists = userRepository.findByEmail(user.getEmail());
if(isEmailExists != null) {
throw new Exception("Error: Email is already registered.");
}
User createdUser = new User();
createdUser.setEmail(user.getEmail());
createdUser.setPassword(passwordEncoder.encode(user.getPassword()));
userRepository.save(createdUser);
}
}
上面這三段程式碼,相信大家應該都很熟悉了,不多做解釋。
修改AuthController.java,處理註冊請求,從request中取得email和密碼。
@PostMapping("/signup")
public ResponseEntity<AuthResponse> createUserHandler(@RequestBody User user) throws Exception {
userService.createUser(user);
String token = jwtProvider.generateToken();
AuthResponse authResponse = new AuthResponse();
authResponse.setJwt(token);
authResponse.setMessage("Signup Success");
return new ResponseEntity<>(authResponse, HttpStatus.CREATED);
}
啟動專案,測試註冊功能,記得設定email和密碼。
我們會取得token和Signup Success的訊息,也可以檢查資料庫中是不是有我們剛註冊的email。
{
"email": "user@new.com",
"password": "12345678"
}
我們再註冊一個帳號,取得另一個token。
現在我們有兩個token了,貼到jwt.io驗證,發覺我們無法辨別token來自於哪個用戶。
因此我們要在產生token時夾帶email地址。
修改AuthController.java,在註冊時,要傳送email給JWTProvider
String token = jwtProvider.generateToken(user.getEmail());
修改JWTProvider.java
增加夾帶emai的程式碼,並且標明這個欄位是email,這樣方便我們解讀。
public String generateToken(String email) {
return Jwts.builder()
.setIssuedAt(new Date())
.setExpiration(new Date(new Date().getTime() + 12 * 60 * 60 * 1000))
.claim("email", email)
.signWith(key)
.compact();
}
來到https://jwt.io/#debugger-io
把得到的token貼上,就能從payload欄位找到email,用JWT_CONSTANT取代your-256-bit-secret,就會顯示Signature verified。
就算有心人想要修改成別人的email,在沒有正確的JWT_CONSTANT的情況下,無法通過驗證。
註冊功能完成後,我們就能開始進行編寫登入的功能,登入成功就會回傳token和成功登入的訊息。
根據提供的email在資料庫中找尋用戶,如果找不到或密碼不對,就顯示錯誤。
這邊刻意不明說是email錯誤還是密碼錯誤,避免駭客使用登入,來找尋有人使用的email。
雖然註冊的部分,有個錯誤訊息是email已經註冊了,由於我們正在開發中,所以保留這個訊息。
等到專案完成了,可以修改這個錯誤訊息,改成顯示email不符合規定,就沒人知道是email重複了。
//AuthController.java
@PostMapping("/login")
public ResponseEntity<AuthResponse> loginHandler(@RequestBody User user) throws Exception {
User foundUser = userService.findUserByEmail(user.getEmail());
if(foundUser == null || !passwordEncoder.matches(user.getPassword(), foundUser.getPassword())) {
throw new Exception("Invalid email or password");
}
String token = jwtProvider.generateToken(foundUser.getEmail());
AuthResponse authResponse = new AuthResponse();
authResponse.setJwt(token);
authResponse.setMessage("Login Success");
return new ResponseEntity<>(authResponse, HttpStatus.OK);
}
在UserService.java,添加根據email尋找用戶的程式碼。
public User findUserByEmail(String email){
return userRepository.findByEmail(email);
}
測試登入功能,使用先前註冊的email和密碼進行登入,就能得到token和成功的訊息。