SpringとJPAをベースにしたWebアプリケーションの開発#37パスワードを忘れました.(+Spring MVCのフォーム処理モードの復習)

43981 ワード

SpringとJPAをベースにしたWebアプリケーションの開発#37パスワードを忘れました.
これらはインフラストラクチャ、Spring、JPAベースのWebアプリケーション開発のコースに基づいて作成されています.

要約は、学習コース、要約ソースのタグ付け、ブログまたはドキュメント形式で公開できるようにする原則の下で公開されます.出典は前述の通り、インフラストラクチャ、Spring、JPAベースのWebアプリケーション開発.
私が勉強しているソースコードはhttps://github.com/n00nietzsche/jakestudy_webappにアップロードされます.私は伝言ごとに講義のどの部分を記録します.

パスワードを忘れました。

  • のパスワードを忘れた場合は、Eメールで「ログイン可能リンク」を送信します.メールで送信したリンクをクリックしてログインします.
  • GET /email-login
  • Eメールを入力できるフォームが表示され、リンク転送ボタンが表示されます.
  • POST /email-login
  • に入力されたEメールに対応するアカウントを検索します.もしそうであれば、Eメールでログイン可能なリンクを送信します.
  • Eメールを送信すると、プロンプトが表示されます.
  • GET /login-by-email
  • TokenとEメールを確認し、アカウントにログインします.
  • 1.Form Backingオブジェクトの作成

    @Data
    public class EmailForm {
        @Email
        @NotBlank
        private String email;
    }
    これはjavax.validationを用いて基本検証を行う場所であり、将来ModelMapperまたはsetterによってドメインオブジェクトに統合される情報を保存する場所でもある.

    2.Validatorの作成(オプション)

    @Component
    @RequiredArgsConstructor
    public class EmailFormValidator implements Validator {
    
        private final AccountRepository accountRepository;
    
        @Override
        public boolean supports(Class<?> clazz) {
            return clazz.isAssignableFrom(EmailForm.class);
        }
    
        @Override
        public void validate(Object target, Errors errors) {
            EmailForm emailForm = (EmailForm) target;
    
            if(!accountRepository.existsByEmail(emailForm.getEmail())) {
                errors.rejectValue("email", "not.exist.email", new Object[]{emailForm.getEmail()}, "존재하지 않는 이메일 입니다.");
            }
        }
    }
    repositoryなどのゲストで検証する際に主に使用するValidatorが必要です.認証に失敗した場合、Errorsオブジェクトがコントローラに送信され、認証に失敗した場合に論理を記述します.

    3.コントローラの作成


    3.1. initBinder()の作成

        @InitBinder("emailForm")
        public void initBinderEmailForm(WebDataBinder webDataBinder) {
            webDataBinder.addValidators(emailFormValidator);
        }
    Validatorが完成したら登録してください.

    3.2. GPRGとビジネスロジックの作成

        @GetMapping("/login-by-email")
        public String loginByEmailForm(Model model) {
            model.addAttribute(new EmailForm());
            return "/login-by-email";
        }
    
        @PostMapping("/login-by-email")
        public String loginByEmail(@Valid @ModelAttribute EmailForm emailForm, Errors errors, RedirectAttributes redirectAttributes, Model model) {
    
            if(errors.hasErrors()) {
    //            model.addAttribute("error", "에러 발생");
                return "/login-by-email";
            }
    
            if(accountService.sendLoginConfirmEmail(emailForm.getEmail())) {
                redirectAttributes.addFlashAttribute("message", "이메일이 전송되었습니다. 이메일을 확인해주세요.");
            }else {
                redirectAttributes.addFlashAttribute("error", "이미 이메일을 재전송했습니다. 1시간 이내에 이메일을 재전송 할 수 없습니다.");
            }
    
            return "redirect:/login-by-email";
        }
    
        @GetMapping("/check-login-token")
        public String checkLoginToken(String token, String email, RedirectAttributes redirectAttributes, Model model) {
            Account account = accountRepository.findByEmail(email);
    
            if (account == null || !account.isValidLoginCheckToken(token)) {
                redirectAttributes.addFlashAttribute("error", "토큰 혹은 이메일이 잘못되었습니다.");
                return "redirect:/login-by-email";
            }
    
            accountService.login(account);
            return "redirect:/";
        }
    失敗した場合はredirect:/を積極的に利用する./login-by-emailと同じパスを使用している場合、フォームに入れることができるModelAttributeの情報がない場合は、th:objectのようなthymeleaf機能を使用するとエラーが発生するので注意してください.redirect:/を利用すると、このような現象を容易に防止することができる.
    また、POSTメソッドではビューがredirect:/に戻ることはありません.returnに直接戻ると、リフレッシュ時に同じコンテンツがフォームに再渡され、同じコンテンツが再び渡されることに注意してください.

    4.サービス・エンド・メソッドの実装

        public boolean sendLoginConfirmEmail(String email) {
            Account account = accountRepository.findByEmail(email);
    
            if(account.canSendLoginCheckTokenAgain()) {
                account.generateLoginCheckToken();
                SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
                simpleMailMessage.setTo(account.getEmail());
                simpleMailMessage.setSubject("제이크 스터디 이메일로 로그인");
                simpleMailMessage.setText("/check-login-token?token=" + account.getLoginCheckToken() + "&email=" + account.getEmail());
                javaMailSender.send(simpleMailMessage);
                return true;
            } else {
                return false;
            }
    これは、ログイントークン確認メールを送信する部分です.確認を使用してログイン電子メールを再送信できる.canSendLoginCheckTokenAgain()が正常に送信された後、trueに送信されない場合はfalseに戻ります.

    5.合成HTML

    <!DOCTYPE html>
    <html lang="en"
          xmlns:th="http://www.thymeleaf.org"
          xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
    <head>
      <title>로그인</title>
      <th:block th:replace="fragments :: headLibraryInjection"></th:block>
    </head>
    
    <body class="bg-light">
    <th:block th:replace="fragments :: main-nav"></th:block>
    <div class="container">
      <div class="py-5 text-center">
        <p class="lead">스터디올래</p>
        <h2>로그인</h2>
      </div>
      <div class="row justify-content-center">
        <div th:if="${param.error}" class="alert alert-danger" role="alert">
          <p>이메일이 정확하지 않습니다.</p>
        </div>
        <div th:if="${message}" class="alert alert-info alert-dismissible fade show mt-3" role="alert">
          <span th:text="${message}">알림 메시지</span>
          <button type="button" class="close" data-dismiss="alert" aria-label="Close">
            <span aria-hidden="true">x</span>
          </button>
        </div>
        <div th:if="${error}" class="alert alert-danger alert-dismissible fade show mt-3" role="alert">
          <span th:text="${error}">에러 메시지</span>
          <button type="button" class="close" data-dismiss="alert" aria-label="Close">
            <span aria-hidden="true">x</span>
          </button>
        </div>
        <!-- `th:object` 를 쓴 경우, 사용할 객체를 올바르게 모델에서 내려받지 못한 경우 500에러가 나니까 조심해야 한다. -->
        <form class="needs-validation col-sm-6" th:object="${emailForm}" action="#" th:action="@{/login-by-email}" method="post" novalidate>
          <div class="form-group">
            <label for="username">이메일</label>
            <input id="username" type="text" th:field="*{email}" class="form-control"
                   placeholder="[email protected]" aria-describedby="emailHelp" required />
            <small id="emailHelp" class="form-text text-muted">
              가입할 때 사용한 이메일을 입력하세요.
            </small>
            <small class="invalid-feedback">이메일을 입력하세요.</small>
            <!-- `th:if`는 `Validator` 에서 `.rejectValue()` 에 넣은 `field` 값을 인식하여 에러가 있는지 확인한다. -->
            <small class="form-text text-danger" th:if="${#fields.hasErrors('email')}" th:errors="*{email}">
              이메일이 잘못되었습니다.
            </small>
          </div>
    
          <div class="form-group">
            <button class="btn btn-success btn-block" type="submit"
                    aria-describedby="submitHelp">로그인</button>
            <small class="form-text text-muted">스터디올래에 처음 오셨다면,
              <a href="#" th:href="@{/sign-up}">계정을 먼저 만드세요.</a>
            </small>
          </div>
    
        </form>
      </div>
    
      <th:block th:replace="fragments :: footer"></th:block>
    </div>
    
    <script th:replace="fragments :: form-validation"></script>
    </body>
    </html>