SpringSecurity之認證

SpringSecurity之認證

1. 鹽值加密

1. 原理概述

SpringSecurity使用的是隨機鹽值加密javascript

隨機鹽是在對密碼摘要以前隨機生成一個鹽,而且會把這個鹽的明文和摘要拼接一塊兒保存css

舉個例子:密碼是pwd,隨機鹽是abc,pwd+abc摘要後的信息是xyz,最後保存的密碼就是abcxyzhtml

隨機鹽 同一個密碼,每次摘要後的結果都不一樣,可是能夠根據摘要裏保存的鹽來校驗摘要和明文密碼是否匹配前端

在hashpw函數中, 咱們能夠看到如下這句java

real_salt = salt.substring(off + 3, off + 25);

說明咱們真正用於鹽值加密的是real_salt, 從而保證了咱們生成隨機鹽值也能再校驗時經過相同的規則獲得須要的結果jquery

2. 使用說明

1. 加密

  • 首先咱們要在SpringSecurity的配置文件中配置密碼的加密方式
/密碼使用鹽值加密 BCryptPasswordEncoder
//BCrypt.hashpw() ==> 加密
//BCrypt.checkpw() ==> 密碼比較
//咱們在數據庫中存儲的都是加密後的密碼, 只有在網頁上輸入時是明文的
@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}
  • 而後在咱們的用戶管理實現類中實現向數據庫添加新用戶(註冊功能) 時對密碼加密
@Override
public Integer addUser(UserDTO user) {
    //先查看要添加的用戶是否在數據庫中
    String username = user.getUsername();
    UserDTO userByUsername = getUserByUsername(username);
    //若是待插入的用戶存在在數據庫中, 插入0條
    if (null != userByUsername) {
        return 0;
    } else {
        //不存在, 則插入用戶
        //先對密碼進行鹽值加密, SpringSecurity中使用的是隨機鹽值加密
        String hashpw = passwordEncoder.encode(user.getPassword());
        user.setPassword(hashpw);
        return userMapper.addUser(user);
    }
}
  • 在咱們提交用戶名和密碼的表單以後, 在數據庫中差看咱們存儲的用戶名和密碼

image-20201118135809970

能夠看到, 密碼與咱們明文輸入的 123456 徹底不一樣web

  • 這裏要注意一點, 設計數據庫時密碼不要少於60位!

2. 認證

講在前面的話:ajax

認證的配置類的 setFilterProcessesUrl("/login") (這裏是自定義過濾器的配置, form方式與其一致)中, url只是咱們提交表單或者ajax請求的地址, 不須要在Controller中註冊, 註冊了PostMapping也不會走, 可是會走Get方式, 此時SpringSecurity不會幫咱們認證(認爲是不安全的提交方式)spring

1. 頁面成功跳轉的坑

頁面成功跳轉有兩個方法數據庫

  • defaultSuccessUrl
  • successForwardUrl

前者是重定向, 後者是轉發, 因爲轉發地址欄不會變化, 而咱們SpringSecurity要求提交表單的方法必須爲post(此處也是大坑!切記!), 所以請求類型後者依然爲post

此時, 若是咱們在addViewControllers中配置了首頁的路徑映射, 同時咱們成功後要跳轉到首頁, 使用後一種方法就會報405錯誤, 提示咱們請求類型錯誤

有兩種解決方法

  • 使用第一種方法, 能夠接受一個get請求的url
  • 配置一個Controller進行Post方式的頁面跳轉

2. 使用驗證碼校驗的坑

驗證碼校驗我在以前的文章中提到過, 這裏就再也不贅述

主要說說驗證碼隨認證一塊兒提交的坑

設置提交的url和咱們login的form url一致, 注意此時必定要用GET請求提交表單!

若是咱們使用相同的url在controller層試圖進行校驗並重定向跳轉, 能夠發現根本就不會走咱們的controller!

同時, 咱們試圖用攔截器攔截響應的url, 並在表單提交以前攔截來下進行校驗, 也失敗了

說明SpringSecurity本身的校驗的優先級至關的高

此時, 咱們只能實現一個認證成功的處理器來處理咱們的驗證碼

  • 實現AuthenticationSuccessHandler接口並用SpringBoot託管
package com.wang.spring_security_framework.config.SpringSecurityConfig;

import com.wang.spring_security_framework.service.CaptchaService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

//登陸成功處理, 用於比對驗證碼
@Component
public class LoginSuccessHandler implements AuthenticationSuccessHandler {
    @Autowired
    CaptchaService captchaService;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        //校驗驗證碼
        Boolean verifyResult = captchaService.versifyCaptcha(request.getParameter("token"),
                request.getParameter("inputCode"));
        if (verifyResult) {
            response.sendRedirect("/index");
        } else {
            response.sendRedirect("/toLoginPage");
        }
    }
}
  • 在SpringSecurity的配置類中使用咱們本身定義的處理類
@Override
protected void configure(HttpSecurity http) throws Exception {
    //指定自定義的登陸頁面, 表單提交的url, 以及成功後的處理器
    http.formLogin()
            .usernameParameter("username")
            .passwordParameter("password")
            .loginPage("/toLoginPage")
            .loginProcessingUrl("/login")
            .successHandler(loginSuccessHandler)
            .and()
            .csrf()
            .disable();
}

此處有個大坑, 若是設置了成功的處理類, 咱們就千萬不要在配置類中寫成功跳轉的方法了, 這樣會覆蓋掉咱們的成功處理器!

3. 前端用ajax請求並附加驗證碼校驗

此處爲天坑! 足足費了我快一天半才爬出來! 簡直處處都是坑, 還有一個問題沒解決...

總之不推薦這麼幹, 主要指用AJAX請求再用後臺跳轉

  • 首先, 咱們要明確一點, AJAX會刷新局部頁面, 這就形成了重定向請求沒問題, 可是頁面不跳轉, 看請求頭咱們會發現url仍是當前頁面
  • 其次, SpringSecurity的認證是用request.getparameter()讀出的, 所以沒法解析AJAX請求傳來的JSON, 咱們要本身寫過濾器解析
  • 最後, SpringSecurity在認證過濾器結束後會關閉request的Stream, 致使咱們沒法取出前端發來的數據, 須要咱們再添加一個request, 再在成功的處理器中得到request中的對象

好了, 讓咱們來看看這個坑吧!

  • 前端代碼

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
            "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    
    <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>登陸界面</title>
        <link th:href="@{css/default.css}" rel="stylesheet" type="text/css"/>
        <!--必要樣式-->
        <link th:href="@{css/styles.css}" rel="stylesheet" type="text/css"/>
        <link th:href="@{css/demo.css}" rel="stylesheet" type="text/css"/>
        <link th:href="@{css/loaders.css}" rel="stylesheet" type="text/css"/>
    </head>
    <body>
    <div class='login'>
        <div class='login_title'>
            <span>登陸</span>
        </div>
        <div class='login_fields'>
            <!--        <form action="/login" method="post">-->
            <div class='login_fields__user'>
                <div class='icon'>
                    <img alt="" src='img/user_icon_copy.png'>
                </div>
                <input name="username" placeholder='用戶名' maxlength="16" type='text' autocomplete="off"/>
                <div class='validation'>
                    <img alt="" src='img/tick.png'>
                </div>
            </div>
            <div class='login_fields__password'>
                <div class='icon'>
                    <img alt="" src='img/lock_icon_copy.png'>
                </div>
                <input name="password" placeholder='密碼' maxlength="16" type='text' autocomplete="off">
                <div class='validation'>
                    <img alt="" src='img/tick.png'>
                </div>
            </div>
            <div class='login_fields__password'>
                <div class='icon'>
                    <img alt="" src='img/key.png'>
                </div>
                <input name="inputCode" placeholder='驗證碼' maxlength="4" type='text' autocomplete="off">
                <div class='validation' style="opacity: 1; top: -3px;">
                    <!-- 當用戶連接時,void(0)計算爲0,用戶點擊不會發生任何效果 -->
                    <a href="javascript:void(0);" title="點擊更換驗證碼">
                        <!--this參數, 返回當前的DOM元素-->
                        <img src="" alt="更換驗證碼" id="imgVerify" onclick="getVerify(this)">
                    </a>
                </div>
            </div>
            <div class='login_fields__submit'>
                <input type='button' value='登陸'>
            </div>
            <div>
                <!--經過隱藏域傳遞值, 在下面的驗證碼點擊事件中, 將值綁定過來, 這樣就能夠得到最新的驗證碼對應的值了!-->
                <input name="token" value="" type="hidden" id="token">
            </div>
            <!--        </form>-->
        </div>
    </div>
    
    <link th:href="@{layui/css/layui.css}" rel="stylesheet" type="text/css"/>
    
    <script type="text/javascript" th:src="@{js/jquery.min.js}"></script>
    <script type="text/javascript" th:src="@{js/jquery-ui.min.js}"></script>
    <script type="text/javascript" th:src="@{layui/layui.js}"></script>
    <script type="text/javascript" th:src="@{js/Particleground.js}"></script>
    <script type="text/javascript" th:src="@{js/Treatment.js}"></script>
    <script type="text/javascript" th:src="@{js/jquery.mockjax.js}"></script>
    <script type="text/javascript">
        $(document).keypress(function (e) {
            // 回車鍵事件 ascii 13
            if (e.which === 13) {
                $('input[type="button"]').click();
            }
        });
    
        //粒子背景特效
        $('body').particleground({
            dotColor: '#39db24',
            lineColor: '#133b88'
        });
        $('input[name="password"]').focus(function () {
            $(this).attr('type', 'password');
        });
        $('input[type="text"]').focus(function () {
            $(this).prev().animate({'opacity': '1'}, 200);
        });
        $('input[type="text"],input[type="password"]').blur(function () {
            $(this).prev().animate({'opacity': '.5'}, 200);
        });
        $('input[name="username"],input[name="password"]').keyup(function () {
            var Len = $(this).val().length;
            if (!$(this).val() === '' && Len >= 5) {
                $(this).next().animate({
                    'opacity': '1',
                    'right': '30'
                }, 200);
            } else {
                $(this).next().animate({
                    'opacity': '0',
                    'right': '20'
                }, 200);
            }
        });
    
        layui.use('layer', function () {
            //非空驗證
            $('input[type="button"]').click(function () {
                let login = $('input[name="username"]').val();
                let pwd = $('input[name="password"]').val();
                let code = $('input[name="inputCode"]').val();
                let token = $('input[name="token"]').val();
                let JsonData = {"username": login, "password": pwd, "inputCode": code, "token": token};
                if (login === '') {
                    ErroAlert('請輸入您的帳號');
                } else if (pwd === '') {
                    ErroAlert('請輸入密碼');
                } else if (code === '' || code.length !== 4) {
                    ErroAlert('輸入驗證碼');
                } else {
                    let url = "/login";
                    $.ajaxSetup({
                        url: url,
                        type: "post",
                        dataType: "json",
                        contentType: "application/json;charset=utf-8",
                        complete: function (XMLHttpRequest, textStatus) {
                            console.log(XMLHttpRequest.status);
                            //經過XMLHttpRequest獲取響應頭
                            let redirect = XMLHttpRequest.getResponseHeader("REDIRECT");
                            console.log(redirect);
                            if (redirect === "REDIRECT") {
                                let win = window;
                                while (win != win.top) {
                                    win = win.top;
                                }
                                win.location.href = XMLHttpRequest.getResponseHeader("CONTEXTPATH");
                            }
                        }
                    });
                    $.ajax({
                        data: JSON.stringify(JsonData),
                        success: function () {
                            console.log("進入回調函數了!");
                        },
                        error: function (xhr, textStatus, errorThrown) {
                            alert("進入error---");
                            alert("狀態碼:"+xhr.status);
                            alert("狀態:"+xhr.readyState); //當前狀態,0-未初始化,1-正在載入,2-已經載入,3-數據進行交互,4-完成。
                            alert("錯誤信息:"+xhr.statusText );
                            alert("返回響應信息:"+xhr.responseText );//這裏是詳細的信息
                            alert("請求狀態:"+textStatus);
                            alert(errorThrown);
                            alert("請求失敗");
                        }
                    });
                }
            });
        });
        //得到img對象
        let imgVerify = $("#imgVerify").get(0);
        //$(function())等同於$(document).ready(function()) ==> 頁面加載完畢以後, 才執行函數
        $(function () {
            getVerify(imgVerify);
        });
    
        //onclick時間綁定的getVerify函數
        function getVerify(obj) {
            $.ajax({
                type: "POST",
                url: "/captcha",
                success: function (result) {
                    obj.src = "data:image/jpeg;base64," + result.img;
                    $("#token").val(result.token);
                }
            });
        }
    </script>
    
    </body>
    </html>
    • 這裏主要是$.ajaxSetup()方法, 能夠定義全局的(同一個函數中的)ajax的一些參數, 尤爲是裏面的complete方法, 是在所有執行完以後調用的, 爲了能強行跳轉AJAX, 咱們要天劍請求頭, 咱們在後面的後端代碼中能夠看到
    • 咱們還須要寫$.ajax()傳遞數據, 注意, json數據就算咱們用json的格式寫了, 仍是要用JSON.stringify()方法轉一下, 不然傳到後端的不是JSON!
    • 此處有一個沒有解決的問題, 不知道爲何不會走成功的回調函數, 只會走失敗的回調函數
  • 自定義認證過濾器

    package com.wang.spring_security_framework.config.SpringSecurityConfig;
    
    import com.alibaba.fastjson.JSON;
    import org.springframework.http.MediaType;
    import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.AuthenticationException;
    import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.Map;
    
    //默認的提取用戶名和密碼是經過 request.getParameter() 方法來提取的, 因此經過form咱們能夠提取到
    //可是若是咱們用ajax傳遞的話, 就提取不到了, 須要本身寫過濾器!
    //這裏不能寫 @Component, 由於咱們要在SpringSecurity配置類中註冊 myCustomAuthenticationFilter 並配置
    //不然會爆出重名的Bean!
    public class MyCustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
        @Override
        public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
            //若是request請求是一個json同時編碼方式爲UTF-8
            if (request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE)
                    || request.getContentType().equals(MediaType.APPLICATION_JSON_UTF8_VALUE)) {
                UsernamePasswordAuthenticationToken authRequest = null;
                
                Map<String, String> authenticationBean = null;
                try (InputStream inputStream = request.getInputStream()) {
                    //將JSON轉爲map
                    authenticationBean = JSON.parseObject(inputStream, Map.class);
                    //將用戶名和密碼放入 authRequest
                    authRequest = new UsernamePasswordAuthenticationToken(
                            authenticationBean.get("username"), authenticationBean.get("password"));
                    System.out.println(authenticationBean);
                } catch (IOException e) {
                    e.printStackTrace();
                    //出現IO異常, 放空的用戶信息
                    authRequest = new UsernamePasswordAuthenticationToken("", "");
                } finally {
                    //將請求 request 和解析後的用戶信息 authRequest 放入userDetails中
                    setDetails(request, authRequest);
                    //將咱們前端傳遞的JSON對象繼續放在request裏傳遞, 這樣咱們就能夠在認證成功的處理器中拿到它了!
                    request.setAttribute("authInfo", authenticationBean);
    
                    return this.getAuthenticationManager().authenticate(authRequest);
                }
            } else {
                return super.attemptAuthentication(request, response);
            }
        }
    }
    • 這裏仍是要強調一點, @Component會自動註冊內部的所有的方法, 若是咱們在別的地方@Bean了方法, 會報一些奇怪的錯誤, 本質上是衝突了!
    • 此處咱們是用FastJSON將JSON轉爲了Map
  • 認證成功處理器

    package com.wang.spring_security_framework.config.SpringSecurityConfig;
    
    import com.alibaba.fastjson.JSON;
    import com.wang.spring_security_framework.service.CaptchaService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.context.SecurityContext;
    import org.springframework.security.core.context.SecurityContextHolder;
    import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
    import org.springframework.stereotype.Component;
    
    import javax.servlet.ServletException;
    import javax.servlet.ServletInputStream;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.util.HashMap;
    import java.util.Map;
    
    //登陸成功處理
    //咱們不能在這裏得到request了, 由於咱們已經在前面自定義了認證過濾器, 作完後SpringSecurity會關閉inputStream流
    @Component
    public class LoginSuccessHandler implements AuthenticationSuccessHandler {
        @Autowired
        CaptchaService captchaService;
    
        @Override
        public void onAuthenticationSuccess(HttpServletRequest request,
                                            HttpServletResponse response,
                                            Authentication authentication) throws IOException, ServletException {
            //咱們從自定義的認證過濾器中拿到的authInfo, 接下來作驗證碼校驗和跳轉
            Map<String, String> authInfo = (Map<String, String>) request.getAttribute("authInfo");
            System.out.println(authInfo);
            System.out.println("success!");
            String token = authInfo.get("token");
            String inputCode = authInfo.get("inputCode");
    
            //校驗驗證碼
            Boolean verifyResult = captchaService.versifyCaptcha(token, inputCode);
            System.out.println(verifyResult);
            if (verifyResult) {
                HashMap<String, String> map = new HashMap<>();
                map.put("url", "/index");
                System.out.println(map);
                String VerifySuccessUrl = "/index";
                response.setHeader("Content-Type", "application/json;charset=utf-8");
    //            response.setContentType("application/json;charset=utf-8");
                response.addHeader("REDIRECT", "REDIRECT");
                response.addHeader("CONTEXTPATH", VerifySuccessUrl);
            } else {
                String VerifyFailedUrl = "/toRegisterPage";
                response.setHeader("Content-Type", "application/json;charset=utf-8");
    //            response.setContentType("application/json;charset=utf-8");
                response.addHeader("REDIRECT", "REDIRECT");
                response.addHeader("CONTEXTPATH", VerifyFailedUrl);
    //            response.sendRedirect("/toRegisterPage");
            }
        }
    }
    • 這裏須要注意一點, 咱們須要從前面的Request拿到對象
    • addHeader裏面咱們爲了重定向, 添加了響應頭, 能夠和前端的ajaxSetup對應着看
  • SpringSecurity配置類

    package com.wang.spring_security_framework.config;
    
    import com.wang.spring_security_framework.config.SpringSecurityConfig.LoginSuccessHandler;
    import com.wang.spring_security_framework.config.SpringSecurityConfig.MyCustomAuthenticationFilter;
    import com.wang.spring_security_framework.service.UserService;
    import com.wang.spring_security_framework.service.serviceImpl.UserDetailServiceImpl;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
    
    //SpringSecurity設置
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
        @Autowired
        UserService userService;
        @Autowired
        UserDetailServiceImpl userDetailServiceImpl;
        @Autowired
        LoginSuccessHandler loginSuccessHandler;
    
        //受權
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            //指定自定義的登陸頁面, 表單提交的url, 以及成功後的處理器
            http.formLogin()
                    .loginPage("/toLoginPage")
                    .failureForwardUrl("/index")
                    .and()
                    .csrf()
                    .disable();
    //        .failureForwardUrl();
            //註銷
    
            //設置過濾器鏈, 添加自定義過濾器
            http.addFilterAt(
                    myCustomAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class
            );
            //容許iframe
    //        http.headers().frameOptions().sameOrigin();
        }
    
        //註冊自定義過濾器
        @Bean
        MyCustomAuthenticationFilter myCustomAuthenticationFilter() throws Exception {
            MyCustomAuthenticationFilter filter = new MyCustomAuthenticationFilter();
            //設置過濾器認證管理
            filter.setAuthenticationManager(super.authenticationManagerBean());
            //設置filter的url
            filter.setFilterProcessesUrl("/login");
            //設置登陸成功處理器
            filter.setAuthenticationSuccessHandler(loginSuccessHandler);
            //TODO 設置登陸失敗處理器
    
            return filter;
        }
    
        //密碼使用鹽值加密 BCryptPasswordEncoder
        //BCrypt.hashpw() ==> 加密
        //BCrypt.checkpw() ==> 密碼比較
        //咱們在數據庫中存儲的都是加密後的密碼, 只有在網頁上輸入時是明文的
        @Bean
        public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    
    }
    • 這裏主要乾了兩件事
      • 註冊了咱們自定義的過濾器
      • 在過濾器鏈中註冊咱們的過濾器

4. 後端只提供JSON讓前端進行跳轉

這裏主要修改了兩處, 咱們的成功處理器返回的是一個封裝好的JSON, 同時咱們在ajax的回調函數中寫了頁面跳轉的邏輯

  • 成功處理器

    package com.wang.spring_security_framework.config.SpringSecurityConfig;
    
    import com.alibaba.fastjson.JSON;
    import com.wang.spring_security_framework.service.CaptchaService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.context.SecurityContext;
    import org.springframework.security.core.context.SecurityContextHolder;
    import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
    import org.springframework.stereotype.Component;
    
    import javax.servlet.ServletException;
    import javax.servlet.ServletInputStream;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.util.HashMap;
    import java.util.Map;
    
    //登陸成功處理
    //咱們不能在這裏得到request了, 由於咱們已經在前面自定義了認證過濾器, 作完後SpringSecurity會關閉inputStream流
    @Component
    public class LoginSuccessHandler implements AuthenticationSuccessHandler {
        @Autowired
        CaptchaService captchaService;
    
        @Override
        public void onAuthenticationSuccess(HttpServletRequest request,
                                            HttpServletResponse response,
                                            Authentication authentication) throws IOException, ServletException {
            //咱們從自定義的認證過濾器中拿到的authInfo, 接下來作驗證碼校驗和跳轉
            Map<String, String> authInfo = (Map<String, String>) request.getAttribute("authInfo");
            System.out.println(authInfo);
            System.out.println("success!");
            String token = authInfo.get("token");
            String inputCode = authInfo.get("inputCode");
    
            //校驗驗證碼
            Boolean verifyResult = captchaService.versifyCaptcha(token, inputCode);
            System.out.println(verifyResult);
    
            Map<String, String> result = new HashMap<>();
            if (verifyResult) {
                HashMap<String, String> map = new HashMap<>();
                map.put("url", "/index");
                System.out.println(map);
                String VerifySuccessUrl = "/index";
                response.setHeader("Content-Type", "application/json;charset=utf-8");
                result.put("code", "200");
                result.put("msg", "認證成功!");
                result.put("url", VerifySuccessUrl);
                PrintWriter writer = response.getWriter();
                writer.write(JSON.toJSONString(result));
            } else {
                String VerifyFailedUrl = "/toLoginPage";
                response.setHeader("Content-Type", "application/json;charset=utf-8");
                result.put("code", "201");
                result.put("msg", "驗證碼輸入錯誤!");
                result.put("url", VerifyFailedUrl);
                PrintWriter writer = response.getWriter();
                writer.write(JSON.toJSONString(result));
            }
        }
    }
    • 這裏只須要注意一點, 及時ContentType必定要加上, 防止出現奇怪的響應頭的問題
  • 前端修改, 這裏刪除了complete方法, 添加了回調函數, 所以咱們只放出ajax

    $.ajax({
        data: JSON.stringify(JsonData),
        success: function (data) {
            alert("進入success---");
            let code = data.code;
            let url = data.url;
            let msg = data.msg;
            if (code == 200) {
                alert(msg);
                window.location.href = url;
            } else if (code == 201) {
                alert(msg);
                window.location.href = url;
            } else {
                alert("未知錯誤!")
            }
        },
        error: function (xhr, textStatus, errorThrown) {
            alert("進入error---");
            alert("狀態碼:" + xhr.status);
            alert("狀態:" + xhr.readyState); //當前狀態,0-未初始化,1-正在載入,2-已經載入,3-數據進行交互,4-完成。
            alert("錯誤信息:" + xhr.statusText);
            alert("返回響應信息:" + xhr.responseText);//這裏是詳細的信息
            alert("請求狀態:" + textStatus);
            alert(errorThrown);
            alert("請求失敗");
        }
    });

5. 失敗處理器

認證失敗的處理器, 主要是三個部分, 失敗處理器, 配置類中自定義過濾器添加失敗處理器, 以及前端添加回調函數的失敗處理器的跳轉邏輯

其中配置類和前端都很是簡單, 咱們這裏只貼出失敗處理器供你們參考

package com.wang.spring_security_framework.config.SpringSecurityConfig;

import com.alibaba.fastjson.JSON;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;

//認證失敗的處理器
@Component
public class LoginFailHandler implements AuthenticationFailureHandler {
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        HashMap<String, String> result = new HashMap<>();
        String AuthenticationFailUrl = "/toRegisterPage";
        response.setHeader("Content-Type", "application/json;charset=utf-8");
        result.put("code", "202");
        result.put("msg", "認證失敗!密碼或用戶名錯誤!即將跳轉到註冊頁面!");
        result.put("url", AuthenticationFailUrl);
        PrintWriter writer = response.getWriter();
        writer.write(JSON.toJSONString(result));
    }
}

3. 寫在最後的話

  • 本文其實不算是教程, 只是我的在練習SpringSecurity進行認證的踩坑以及總結
  • 固然, 附加驗證碼校驗應該寫在token的自定義類中, 這裏我偷懶了...有機會再補上吧
  • 請忽視我醜陋的AJAX回調信息, 這裏的標準作法是定義返回的信息類
相關文章
相關標籤/搜索