Java結合SpringBoot攔截器實現簡單的登錄認證模塊


之前在做項目時需要實現一個簡單的登錄認證的功能,就尋思着使用Spring Boot的攔截器來實現,在此記錄一下我的整個實現過程,源碼見文章底部。

1. 環境搭建

IntelliJ IDEA + Java8 + Spring Boot + Tomcat
我將之前項目中的登錄模塊抽離出來,單獨放在了一個新建的Spring Boot項目中;
整個項目的主要結構如下:
項目結構

參考資料:使用IDEA創建Spring Boot項目

2. 代碼詳解

2.1 前端代碼

之前項目里別的小伙伴已經寫好了一個簡單的登錄框樣式表(login.css)和一些image圖,我這里就順手拿來用了,希望哪天你見了眼熟別拍我…
login.vm代碼:
注意前端傳遞給后端Controller的password值並不是用戶實際輸入的密碼!
實際傳遞的是用戶名 + 密碼(統一小寫)組合的字符串的md5信息值;
這樣在前后台數據傳遞及后台數據保存時傳遞和保存的都不是用戶的真實密碼值,可以一定程度提升安全性及規避某些風險;

更多資料可參考:Web前端密碼加密是否有意義

  1.  
    <html>
  2.  
    <head>
  3.  
    <title>系統登錄</title>
  4.  
    <meta charset="utf-8"/>
  5.  
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
  6.  
    <meta name="author" content="Dreamer-1">
  7.  
    <meta name="renderer" content="webkit" />
  8.  
     
  9.  
    <link href="/css/login/login.css" rel="stylesheet">
  10.  
    <script type="text/javascript" src="/js/login/jquery/jquery.min.js?v=20170207"></script>
  11.  
    <script type="text/javascript" src="/js/login/md5/md5.js"></script>
  12.  
    </head>
  13.  
     
  14.  
    <body>
  15.  
    <form name="form1" method="post" action="/login" id="form1" onsubmit="return checkLogin();">
  16.  
    <div id="main">
  17.  
    <div class="wrapper">
  18.  
    <div class="login-hd"></div>
  19.  
    <div class="login-body">
  20.  
    <div class="logo">
  21.  
    <span class="icon-logo"></span>
  22.  
    </div>
  23.  
    <div class="box">
  24.  
    <div class="login-item">
  25.  
    <span class="icon-user"></span>
  26.  
    <input name="username" type="text" id="username" class="login-input" tabindex="1" maxlength="50" placeholder="請輸入用戶名" />
  27.  
     
  28.  
    </div>
  29.  
    <div class="login-item mt35">
  30.  
    <span class="icon-pwd"></span>
  31.  
    <input type="password" id="password" class="login-input" tabindex="2" maxlength="32" placeholder="請輸入密碼"/>
  32.  
    <input type="hidden" id="hidePwd" name="password">
  33.  
    </div>
  34.  
    <div class="login-forget" style="visibility:hidden">
  35.  
    <a href="#">忘記密碼</a>
  36.  
    </div>
  37.  
     
  38.  
    <input type="submit" name="Logon" value="登錄" id="Logon" tabindex="3" class="login-btn" />
  39.  
    <div class="login-bottom">
  40.  
    <div class="msg" style="display:none;" >
  41.  
    <span class="icon-err"></span>
  42.  
    <span id="message"></span>
  43.  
    </div>
  44.  
    </div>
  45.  
    </div>
  46.  
    </div>
  47.  
    </div>
  48.  
    </div>
  49.  
    </form>
  50.  
     
  51.  
    <script type="text/javascript">
  52.  
    // onsubmit值為true時,提交表單,否則顯示錯誤信息
  53.  
    // 生成用戶名+密碼組合的md5值,並設置傳給后端的密碼為該md5值
  54.  
    function checkLogin() {
  55.  
    var name = $("#username").val().toLowerCase();
  56.  
    var pwd = $("#password").val().toLowerCase();
  57.  
    if(name.trim()=="" || pwd.trim()=="") {
  58.  
    $("#message").text("請輸入用戶名和密碼");
  59.  
    $('.msg').show();
  60.  
    return false;
  61.  
    }else {
  62.  
    $('.msg').hide();
  63.  
    }
  64.  
     
  65.  
    var md5info = name + pwd;
  66.  
    $('#hidePwd').val(md5(md5info));
  67.  
    //$("#password").val();
  68.  
    return true;
  69.  
    }
  70.  
    </script>
  71.  
    </body>
  72.  
    </html>

welcome.vm代碼
登錄成功后顯示welcome.vm頁的內容,這個頁面很簡單:

  1.  
    <h1 align="center">登錄成功!!!</h1>
  2.  
     
  3.  
    <br>
  4.  
    <h3><a href="/loginout"><font color="red">退出登錄</font></a></h3>

2.2 后端代碼

后端代碼相較於前端要復雜一些,讓我們來一一拆解;

2.2.1 程序入口

ManApplication.java是整個程序的主入口,因為其上打了@SpringBootApplication的注解
注意:Spring Boot項目在tomcat上部署運行時,ManApplication需要繼承SpringBootServletInitializer
ManApplication.java代碼:

  1.  
    /**
  2.  
    * @SpringBootApplication 注解標明該類是本程序的入口
  3.  
    */
  4.  
    @SpringBootApplication
  5.  
    public class ManApplication extends SpringBootServletInitializer {
  6.  
     
  7.  
    @Override
  8.  
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
  9.  
    return application.sources(ManApplication.class);
  10.  
    }
  11.  
     
  12.  
    public static void main(String[] args) {
  13.  
    SpringApplication.run(ManApplication.class, args);
  14.  
    }
  15.  
    }
2.2.2 Controller

IndexViewController.java類里就是簡單的URL映射;

  1.  
    /**
  2.  
    * Created with logindemo.
  3.  
    * Author: dreamer-1
  4.  
    * Email: zhong--lei@outllok.com
  5.  
    * Date: 2018/5/13
  6.  
    * Time: 下午2:58
  7.  
    * Description:
  8.  
    */
  9.  
    @Controller
  10.  
    public class IndexViewController {
  11.  
    /**
  12.  
    * 登錄
  13.  
    * @return
  14.  
    */
  15.  
    @GetMapping("/")
  16.  
    public String index() {
  17.  
    return "login";
  18.  
    }
  19.  
     
  20.  
    /**
  21.  
    * 歡迎頁
  22.  
    * @return
  23.  
    */
  24.  
    @GetMapping("/welcome")
  25.  
    public String welcome() {
  26.  
    return "welcome";
  27.  
    }
  28.  
    }

LoginViewController.java類接收前端傳過來的username和password,進行簡單的校驗和重定向;
此處為了簡單就只設置了一個正確的賬號和密碼用於校驗,你后續使用時可以結合自己的實際需求來擴充整個校驗邏輯(比如通過專門的表來存儲用戶登錄信息等);
用戶名和密碼校驗通過后會在當前會話的session中放入一個登錄標識,以表示當前用戶已經登錄;在退出登錄或會話超時時銷毀該標識;

  1.  
    /**
  2.  
    * Created with logindemo.
  3.  
    * Author: dreamer-1
  4.  
    * Email: zhong--lei@outllok.com
  5.  
    * Date: 2018/5/13
  6.  
    * Time: 下午2:49
  7.  
    * Description:
  8.  
    */
  9.  
    @Controller
  10.  
    public class LoginViewController {
  11.  
     
  12.  
    // 預先設置好的正確的用戶名和密碼,用於登錄驗證
  13.  
    private String rightUserName = "admin";
  14.  
    private String rightPassword = "admin";
  15.  
     
  16.  
    /**
  17.  
    * 登錄校驗
  18.  
    *
  19.  
    * @param request
  20.  
    * @return
  21.  
    */
  22.  
    @RequestMapping("/login")
  23.  
    public String login(HttpServletRequest request) {
  24.  
    String username = request.getParameter("username");
  25.  
    String password = request.getParameter("password");
  26.  
    if (null == username || null == password) {
  27.  
    return "redirect:/";
  28.  
    }
  29.  
     
  30.  
    // 前端傳回的密碼實際為用戶輸入的:用戶名(小寫)+ 密碼(小寫)組合的字符串生成的md5值
  31.  
    // 此處先通過后台保存的正確的用戶名和密碼計算出正確的md5值,然后和前端傳回來的作比較
  32.  
    String md5info = rightUserName.toLowerCase() + rightPassword.toLowerCase();
  33.  
    String realPassword = DigestUtils.md5DigestAsHex(md5info.getBytes());
  34.  
    if (!password.equals(realPassword)) {
  35.  
    return "redirect:/";
  36.  
    }
  37.  
     
  38.  
    // 校驗通過時,在session里放入一個標識
  39.  
    // 后續通過session里是否存在該標識來判斷用戶是否登錄
  40.  
    request.getSession().setAttribute("loginName", "admin");
  41.  
    return "redirect:/welcome";
  42.  
    }
  43.  
     
  44.  
    /**
  45.  
    * 注銷登錄
  46.  
    *
  47.  
    * @param request
  48.  
    * @return
  49.  
    */
  50.  
    @RequestMapping("/loginout")
  51.  
    public String loginOut(HttpServletRequest request) {
  52.  
    request.getSession().invalidate();
  53.  
    return "redirect:/";
  54.  
    }
  55.  
     
  56.  
    }
2.2.3 Interceptor

LoginInterceptor.java是整個登錄認證模塊中的核心類之一,它實現了HandlerInterceptor類,由它來攔截並過濾到來的每一個請求;它的三個方法能分別作用於每個請求的不同生命周期,你可以根據自己的需要來加入相應的處理邏輯;

  1.  
    /**
  2.  
    * Created with logindemo.
  3.  
    * Author: dreamer-1
  4.  
    * Email: zhong--lei@outllok.com
  5.  
    * Date: 2018/5/13
  6.  
    * Time: 下午2:58
  7.  
    * Description:
  8.  
    */
  9.  
    public class LoginInterceptor implements HandlerInterceptor {
  10.  
     
  11.  
    /**
  12.  
    * 在請求被處理之前調用
  13.  
    * @param request
  14.  
    * @param response
  15.  
    * @param handler
  16.  
    * @return
  17.  
    * @throws Exception
  18.  
    */
  19.  
    @Override
  20.  
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  21.  
    // 檢查每個到來的請求對應的session域中是否有登錄標識
  22.  
    Object loginName = request.getSession().getAttribute("loginName");
  23.  
    if (null == loginName || !(loginName instanceof String)) {
  24.  
    // 未登錄,重定向到登錄頁
  25.  
    response.sendRedirect("/");
  26.  
    return false;
  27.  
    }
  28.  
    String userName = (String) loginName;
  29.  
    System.out.println("當前用戶已登錄,登錄的用戶名為: " + userName);
  30.  
    return true;
  31.  
    }
  32.  
     
  33.  
    /**
  34.  
    * 在請求被處理后,視圖渲染之前調用
  35.  
    * @param request
  36.  
    * @param response
  37.  
    * @param handler
  38.  
    * @param modelAndView
  39.  
    * @throws Exception
  40.  
    */
  41.  
    @Override
  42.  
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
  43.  
     
  44.  
    }
  45.  
     
  46.  
    /**
  47.  
    * 在整個請求結束后調用
  48.  
    * @param request
  49.  
    * @param response
  50.  
    * @param handler
  51.  
    * @param ex
  52.  
    * @throws Exception
  53.  
    */
  54.  
    @Override
  55.  
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
  56.  
     
  57.  
    }
  58.  
    }
2.2.4 Configuration

LoginConfiguration.java是另一個核心類之一,它繼承自WebMvcConfigurerAdapter類,負責注冊並生效我們自己定義的攔截器配置;
在這里要注意定義好攔截路徑和排除攔截的路徑;

WebMvcConfigurerAdapter其實還可以做很多其他的事,包括添加自定義的視圖控制器等等,詳見這里

  1.  
    /**
  2.  
    * Created with logindemo.
  3.  
    * Author: dreamer-1
  4.  
    * Email: zhong--lei@outllok.com
  5.  
    * Date: 2018/5/13
  6.  
    * Time: 下午2:58
  7.  
    * Description:
  8.  
    */
  9.  
    @Configuration
  10.  
    public class LoginConfiguration implements WebMvcConfigurer {
  11.  
    @Override
  12.  
    public void addInterceptors(InterceptorRegistry registry) {
  13.  
    // 注冊攔截器
  14.  
    LoginInterceptor loginInterceptor = new LoginInterceptor();
  15.  
    InterceptorRegistration loginRegistry = registry.addInterceptor(loginInterceptor);
  16.  
    // 攔截路徑
  17.  
    loginRegistry.addPathPatterns("/**");
  18.  
    // 排除路徑
  19.  
    loginRegistry.excludePathPatterns("/");
  20.  
    loginRegistry.excludePathPatterns("/login");
  21.  
    loginRegistry.excludePathPatterns("/loginout");
  22.  
    // 排除資源請求
  23.  
    loginRegistry.excludePathPatterns("/css/login/*.css");
  24.  
    loginRegistry.excludePathPatterns("/js/login/**/*.js");
  25.  
    loginRegistry.excludePathPatterns("/image/login/*.png");
  26.  
    }
  27.  
    }

3. 踩坑與填坑

3.1 多次重定向與Circle view path錯誤

剛開始程序部署至tomcat里運行時,理所當然的出現了意想不到的情況,詳情如下:
啟動時localhost:8080顯示:
重定向次數過多

后台一直報錯:
template might not exist

通過斷點調試,發現啟動后不停地進入IndexViewController中的“/”這個URL映射里;
手動指定訪問路徑為 localhost:8080/welcome 時后台報錯:
cycle-view-path

解決辦法:

  1. 在pom文件中導入thymeleaf依賴:
  1.  
    <dependency>
  2.  
    <groupId>org.springframework.boot</groupId>
  3.  
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
  4.  
    </dependency>
  1. 在application.properties里添加如下配置:
  1.  
    spring.thymeleaf.prefix=classpath:/templates/
  2.  
    spring.thymeleaf.suffix=.vm

我猜想可能是我的view文件都以.vm結尾,thymeleaf默認找的是.html結尾的視圖,所以一直找不到;
有知道的大神可以在下面留言詳細講解一下 ^_^

4. 大功告成

運行截圖:
未登錄情況下訪問 localhost:8080/welcome 等非登錄頁面時會自動跳轉到登錄頁面:
運行截圖

http://www.vxjezfv.cn/
http://news.vxjezfv.cn/
http://www.xibiyo.com.cn/
http://news.xibiyo.com.cn/
http://www.9208361.org.cn/
http://news.9208361.org.cn/
http://www.9111316.cn/
http://news.9111316.cn/
http://www.bluelf.com.cn/
http://news.bluelf.com.cn/
http://www.qqq136com.cn/
http://news.qqq136com.cn/
http://www.2819w.cn/
http://news.2819w.cn/
http://www.9019758.org.cn/
http://news.9019758.org.cn/
http://www.wydaogou.cn/
http://news.wydaogou.cn/
http://www.ralhys.cn/
http://news.ralhys.cn/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM