(七)Spring Security基於ajax登錄

spring security 最簡單的登錄莫過於form表單登錄,但是要做到極致的交互式體驗,還是需要用到ajax登錄

首先我們藉助於(五)Spring Security基於數據庫的權限授權的登錄頁面

一:修改form 表單

  1. 原表單:
    <form th:action="@{/login}" method="post">
        <div><label> User Name : <input type="text" name="username"/> </label></div>
        <div><label> Password: <input type="password" name="password"/> </label></div>
        <div><label> code: <input type="text" name="captcha"/> </label> <img src="/getCode" id="code" alt="驗證碼" onclick="refresh(this)"/></div>
        <label> <input type="hidden" name="uuid" id="uuid"/> </label>
        <div><input type="submit" value="Sign In"/></div>
    </form>
    
  2. 修改後
    <form >
        <div><label> User Name : <input type="text" name="username"/> </label></div>
        <div><label> Password: <input type="password" name="password"/> </label></div>
        <div><label> code: <input type="text" name="captcha"/> </label> <img src="/getCode" id="code" alt="驗證碼" onclick="refresh(this)"/></div>
        <label> <input type="hidden" name="uuid" id="uuid"/> </label>
        <div><a  href="javascript:login();" >登錄</a></div>
    </form>
    *********************************************************
     let  login=()=>{
        let username = $("input[name = 'username']");
        let password = $("input[name = 'password']");
        let captcha = $("input[name = 'captcha']");
        let uuid = $("input[name = 'uuid']");
        $.ajax({
            type : "POST",
            url : "/login",
            data : {
                "username" : username.val(),
                "password" : password.val(),
                "captcha" : captcha.val(),
                "uuid" : uuid.val()
            },
    
            success: function(result){
                if(result.code === 1){
                    window.location.href="/";
                }else {
                    alert('登錄失敗');
                }
            },
            error : function(data) {
                alert("錯誤的用戶或密碼");
            },
            beforeSend : function() {
                if (username.val() === "") {
                    alert("請輸入用戶名!");
                    username.focus();
                    return false;
                }
                if (password.val() === "") {
                    alert("請輸入密碼!");
                    password.focus();
                    return false;
                }
            }
        });
    };
    

二:創建一個登錄接口

	/**
	 * 這個接口用來代替spring security的登錄界面
	 */
    @GetMapping("/login_page")
    @ResponseBody
    public Object loginPage() {
        return new BaseResult(ReturnCode.FAILED.getCode(), "尚未登錄,請登錄!");
    }

在WebSecurityConfig 裏配置

@Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/login","/login_page","/home","/getCode").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login_page") /* 未登錄的自動跳轉到這個接口 */
                .loginProcessingUrl("/login") /* 指定登錄邏輯路徑 ,由過濾器攔截*/
                .successHandler(successHandler)
                .failureHandler(failureHandler)
                .and()
                .logout()
                .logoutSuccessUrl("/home");
        http.addFilterBefore(verifyCodeFilter, UsernamePasswordAuthenticationFilter.class);

三:自定義認證入口點(AuthenticationEntryPoint)

由於配置的loginPage("/login_page")跳轉的頁面是json數據,不是一個頁面,所以問哦們需要自定義一個AuthenticationEntryPoint,

  1. AuthenticationEntryPoint的實現類有多種
    在這裏插入圖片描述
    AuthenticationEntryPoint在ExceptionTranslationFilter使用,
    ExceptionTranslationFilter會截獲AuthenticationException
    或者AccessDeniedException異常,AuthenticationEntryPoint. Commence(…)就會被調用。
    如下:源代碼
    public class ExceptionTranslationFilter extends GenericFilterBean {
        private AuthenticationEntryPoint authenticationEntryPoint;
    ......
    
        public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
            HttpServletRequest request = (HttpServletRequest)req;
            HttpServletResponse response = (HttpServletResponse)res;
    
            try {
                chain.doFilter(request, response);
                this.logger.debug("Chain processed normally");
            } catch (IOException var9) {
                throw var9;
            } catch (Exception var10) {
                ......
    
                this.handleSpringSecurityException(request, response, chain, (RuntimeException)ase);
            }
    
        }
    
        private void handleSpringSecurityException(HttpServletRequest request, HttpServletResponse response, FilterChain chain, RuntimeException exception) throws IOException, ServletException {
            ......
            this.sendStartAuthentication(request, response, chain, (AuthenticationException)exception);
            ......
        }
    
        protected void sendStartAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, AuthenticationException reason) throws ServletException, IOException {
            SecurityContextHolder.getContext().setAuthentication((Authentication)null);
            this.requestCache.saveRequest(request, response);
            this.logger.debug("Calling Authentication entry point.");
            /* 調用 */
            this.authenticationEntryPoint.commence(request, response, reason);
        }
    ......
    
  2. 當用戶訪問受限的資源時跳轉登錄頁面
    @Component
    public class UnauthorizedEntryPoint implements AuthenticationEntryPoint {
        @Override
        public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
            if(isAjaxRequest(request)){
                response.sendError(HttpServletResponse.SC_UNAUTHORIZED,authException.getMessage());
            }else{
                response.sendRedirect("/login");
            }
        }
    
        private boolean isAjaxRequest(HttpServletRequest request) {
            return request.getHeader("X-Requested-With") != null&&request.getHeader("X-Requested-With").equals("XMLHttpRequest");
        }
    }
    

四:最終配置

  1. 關閉crsf
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                    .authorizeRequests()
                    .antMatchers("/login","/login_page","/home","/getCode").permitAll()
                    .anyRequest().authenticated()
                    .and()
                    .formLogin()
                    .loginPage("/login_page")
                    .loginProcessingUrl("/login")
                    .successHandler(successHandler)
                    .failureHandler(failureHandler)
                    .and()
                    .logout()
                    .logoutSuccessUrl("/home");
            //http.csrf().disable();      /* 關閉csrf,否則會攔截ajax請求 */  
            kuhttp.exceptionHandling().authenticationEntryPoint(unauthorizedEntryPoint); /* 用來解決匿名用戶訪問無權限資源時的異常 */
            http.addFilterBefore(verifyCodeFilter, UsernamePasswordAuthenticationFilter.class);
        }
    
  2. 開啓crsf
    crsf開啓,會攔截ajax請求所以需要在請求中加入crsf參數
    在這裏插入圖片描述
    注意:需要在頭部加入這些,否則無法創建session在這裏插入圖片描述
    示例
    在這裏插入圖片描述

五:項目地址

Spring Security基於ajax登錄

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