Spring Boot - 個人博客 - 登錄


在這裏插入圖片描述


1. 需求分析

管理後臺的登錄的唯一目的就是去驗證用戶的合法性,即根據輸入的用戶名和密碼能否在數據庫中找到相應的用戶。因此,登錄頁需要做的最重要的工作就是驗證。驗證可以分爲前端和後端兩個方面進行:

  • 前端:不允許不輸入用戶名或密碼來直接登錄,如果用戶在這兩項中有一項沒有輸入相應的信息,前端頁面應該要給出相應的提示;如果根據輸入的用戶名和密碼無法在數據庫中找到合法的用戶,那麼也應該在前端頁面給出相應的提示,告訴用戶用戶名或密碼錯誤
  • 後端:如果用戶正常的輸入了用戶名和密碼,那麼接下來就需要根據用戶名和密碼到數據庫中進行驗證,那麼就要求在數據持久層應該有相應的方法。如果根據用戶名和密碼能找到合法的用戶,那麼將跳轉到歡迎頁,否則在前端給出錯誤提示

在分析了前端和後端對於用戶登錄的需求後,我們就可以分別對其進行處理,從而實現用戶登錄驗證管理功能。

2. 前端驗證

登錄頁較爲簡單,本質上就是一個form表單

 <div class="m-container-small m-padded-tb-massive" style="max-width: 30em !important;">
   <div class="ur container">
     <div class="ui middle aligned center aligned grid">
       <div class="column">
         <h2 class="ui teal image header">
           <div class="content">
             管理後臺登錄
           </div>
         </h2>
         <form class="ui large form" method="post" th:action="@{/admin/login}">
           <div class="ui  segment">
             <!--用戶名輸入框-->
             <div class="field">
               <div class="ui left icon input">
                 <i class="user icon"></i>
                 <input type="text" name="username" placeholder="用戶名">
               </div>
             </div>
             <!--用戶名輸入框-->
             <div class="field">
               <div class="ui left icon input">
                 <i class="lock icon"></i>
                 <input type="password" name="password" placeholder="密碼">
               </div>
             </div>
             <button class="ui fluid large teal submit button">登   錄</button>
           </div>
           <div class="ui error mini message"></div>
           <div class="ui mini negative message" th:unless="${#strings.isEmpty(message)}" th:text="${message}">用戶名或密碼錯誤</div>
         </form>
       </div>
     </div>
   </div>
 </div>

對於前端來說,主要工作是進行進行表單的驗證,不允許用戶輸入空的信息,針對的部分就是<div class="ui segment">所定義的form表單。因此,可以通過JS對錶單進行驗證,如下所示:

<script>
  $('.ui.form').form({
    fields : {
      username : {
        identifier: 'username',
        rules: [{
          type : 'empty',
          prompt: '請輸入用戶名'
        }]
      },
      password : {
        identifier: 'password',
        rules: [{
          type : 'empty',
          prompt: '請輸入密碼'
        }]
      }
    }
  });
</script>

首先通過類'.ui.form'找到form表單,然後對於其中的兩個field區域,即用戶名輸入和密碼輸入定義規則:

  • 如果用戶名爲空,則前端提示請輸入用戶名
  • 如果密碼爲空,則前端提示請輸入密碼

整體上最後是這樣的一個效果,當我不輸入任何信息時,頁面就會彈出信息,另外對應的框會變紅,提示這是一個不合法的登錄請求。
在這裏插入圖片描述
如果用戶名或密碼錯誤,同樣會給出提示信息。
在這裏插入圖片描述

3. 後端驗證

首先在表現層應該有一個Controller來處理登錄請求,然後使用localhost:8080/admin就可以返回登錄頁,如下所示:

@Controller
@RequestMapping("/admin")
public class LoginController {

    @Autowired
    private UserService userService;

    @GetMapping
    public String loginPage(){
        return "admin/login";
    }
}

程序會在resource目錄及其子目錄下找名爲login.html的頁面進行加載。

而在form表單中使用thymleaf定義的動作是th:action="@{/admin/login},因此需要一個方法處理/admin/login爲鏈接的請求,進行用戶驗證,如下所示

@Controller
@RequestMapping("/admin")
public class LoginController {

    @Autowired
    private UserService userService;

    @PostMapping("/login")
    public String login(@RequestParam String username,
                        @RequestParam String password,
                        HttpSession session, RedirectAttributes attributes){
		...
    }
}

首先使用@PostMapping表示該方法處理的一個post請求,然後使用@RequestParam從request域中拿到前端輸入的用戶名(username)和密碼(password)。同時使用HttpSession在session中記錄登錄的用戶,使用RedirectAttributes實現重定向,當用戶名或密碼輸入錯誤時,仍然重定向回登錄頁。

那麼拿到用戶名和密碼後,在業務層需要使用它們到數據庫中找有沒有相應的用戶。因此,業務層需要定義checkUser()來進行驗證。

public interface UserService {
    
    User checkUser(String username, String password);
}

接口的實現類爲:

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserRepository userRepository;

    @Override
    public User checkUser(String username, String password) {
        // 控制檯顯示信息
        System.out.println(username + " " + password);
        // 驗證
        User user = userRepository.findByUsernameAndPassword(username, password);

        return user;
    }
}

那麼在持久層就需要相應的操作方法,根據Jpa的命名規則findByxxxAndxxx定義相應的方法findByUsernameAndPassword(),只需要定義相應的方法即可,並不需要自己寫處理邏輯,Jpa會自動的幫我們處理:

public interface UserRepository extends JpaRepository<User,Long> {

    User findByUsernameAndPassword(String username, String password);
}

不熟悉Jpa可以查閱我之前的一篇博文:一文詳盡 Spring Data JPA 的日常使用

再次回到業務層,當調用UserRepository中的方法查詢用戶後會得到一個User對象。如果可以找到,那麼user不爲空,否則user就等於null,表示該用戶不存在。因此,最終表現層通過調用業務層的方法就能拿到一個user,其中處理post請求的方法處理邏輯如下所示:

@PostMapping("/login")
    public String login(@RequestParam String username,
                        @RequestParam String password,
                        HttpSession session, RedirectAttributes attributes){
		// 拿到User對象
        User user = userService.checkUser(username, password);
        // 如果當前user合法
        if (user != null){
            // 那麼在session中保存user,同時爲了安全將password置空
            user.setPassword(null);
            session.setAttribute("user", user);
            // 跳轉到歡迎頁
            return "admin/index";
        } else {
            // 如果當前的user爲空,說明用戶名或密碼錯誤
            // 使用attributes向前端傳遞提示message
            attributes.addFlashAttribute("message", "用戶名或密碼錯誤");
            // 重定向回登錄頁
            return "redirect:/admin";
        }
    }

這樣後端的驗證工作就處理結束了,主要就是驗證用戶名和密碼在數據庫中是否存在。另外,通常爲了密碼的安全,嘗試用MD5進行加密,這樣數據庫中保存的密碼其實是加密後的結果。

public class MD5Utils {
    public static String code(String str){
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(str.getBytes());
            byte[]byteDigest = md.digest();
            int i;
            StringBuffer buf = new StringBuffer("");
            for (int offset = 0; offset < byteDigest.length; offset++) {
                i = byteDigest[offset];
                if (i < 0)
                    i += 256;
                if (i < 16)
                    buf.append("0");
                buf.append(Integer.toHexString(i));
            }
            //32位加密
            return buf.toString();
            // 16位的加密
            //return buf.toString().substring(8, 24);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return null;
        }

    }
}

使用MD5加密後,checkUser()就變成了如下的形式:

@Override
public User checkUser(String username, String password) {
    System.out.println(username + " " + password);
    User user = userRepository.findByUsernameAndPassword(username, MD5Utils.code(password));
    
    return user;
}

即傳遞到數據庫中的也是加密後的結果。如果一切順利通過,那麼最終會正常跳轉到後臺的歡迎頁。
在這裏插入圖片描述

另外,還需要一個註銷功能,當用戶點擊右上角的註銷時會退出登錄,重新回到登錄頁。因此,導航欄的註銷按鈕部分就需要使用th:href添加一個跳轉

<i class="dropdown icon"></i>
<div class="menu">
    <a href="#" th:href="@{/admin/logout}" class="item">註銷</a>
</div>

然後表現層需要有方法來處理,即將之前保存在session域中的user清除掉,最後重定向會登錄頁。

@GetMapping("/logout")
public String logout(HttpSession session){
    session.removeAttribute("user");
    return "redirect:/admin";
}

4. 登錄歡迎頁

登錄歡迎頁只是簡單的展示一個頁面,並不會處理其他的請求,頁面設計如下:

<div class="m-container-small m-padded-tb-big">
    <div class="ui container">
        <div class="ui success large  message">
            <h3 th:text="'Hi, ' + ${session.user.nickname}+' ,歡迎回來!'">Hi, Forlogen,歡迎登錄!</h3>
        </div>
        <img src="../static/images/manba.jpg" th:src="@{/images/manba.jpg}" alt=""
             class="ui rounded bordered fluid image">
    </div>
</div

這裏需要從session域中拿到user對象的nickname字段,然後使用th:text進行動態的替換;而顯示的圖片使用th:src來指定圖片存放的路徑。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章