Spring Security使用詳解6(自定義登錄頁、接口、結果)

在之前的所有樣例中,登錄表單一直都是使用 Spring Security 提供的默認登錄頁,登錄成功後也是默認的頁面跳轉。有時我們想要使用自定義的登錄頁,或者在前後端分離的開發方式中,前後端的數據交互通過 JSON 進行,這時登錄成功後就不是頁面跳轉了,而是一段 JSON 提示。下面通過樣例演示如何進行登錄表單的個性化配置。

 

六、自定義登錄頁面、登錄接口、登錄成功或失敗的處理邏輯

1、樣例代碼

(1)首先修改 Spring Security 配置,增加相關的自定義代碼:

  • 將登錄頁改成使用自定義頁面,並配置登錄請求處理接口,以及用戶密碼提交時使用的參數名。
  • 自定義了登錄成功、登錄失敗的處理邏輯,根據情況返回響應的 JSON 數據。
@Configuration
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {
    // 指定密碼的加密方式
    @SuppressWarnings("deprecation")
    @Bean
    PasswordEncoder passwordEncoder(){
        // 不對密碼進行加密
        return NoOpPasswordEncoder.getInstance();
    }
 
    // 配置用戶及其對應的角色
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("root").password("123").roles("DBA")
                .and()
                .withUser("admin").password("123").roles("ADMIN")
                .and()
                .withUser("hangge").password("123").roles("USER");
    }
 
    // 配置 URL 訪問權限
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests() // 開啓 HttpSecurity 配置
            .antMatchers("/db/**").hasRole("DBA") // db/** 模式URL需DBA角色
            .antMatchers("/admin/**").hasRole("ADMIN") // admin/** 模式URL需ADMIN角色
            .antMatchers("/user/**").hasRole("USER") // user/** 模式URL需USER角色
            .anyRequest().authenticated() // 用戶訪問其它URL都必須認證後訪問(登錄後訪問)
            .and().formLogin()  // 開啓登錄表單功能
            .loginPage("/login_page") // 使用自定義的登錄頁面,不再使用SpringSecurity提供的默認登錄頁
            .loginProcessingUrl("/login") // 配置登錄請求處理接口,自定義登錄頁面、移動端登錄都使用該接口
            .usernameParameter("name") // 修改認證所需的用戶名的參數名(默認爲username)
            .passwordParameter("passwd") // 修改認證所需的密碼的參數名(默認爲password)
            // 定義登錄成功的處理邏輯(可以跳轉到某一個頁面,也可以返會一段 JSON)
            .successHandler(new AuthenticationSuccessHandler() {
                @Override
                public void onAuthenticationSuccess(HttpServletRequest req,
                                                    HttpServletResponse resp,
                                                    Authentication auth)
                        throws IOException, ServletException {
                    // 我們可以跳轉到指定頁面
                    // resp.sendRedirect("/index");
  
                    // 也可以返回一段JSON提示
                    // 獲取當前登錄用戶的信息,在登錄成功後,將當前登錄用戶的信息一起返回給客戶端
                    Object principal = auth.getPrincipal();
                    resp.setContentType("application/json;charset=utf-8");
                    PrintWriter out = resp.getWriter();
                    resp.setStatus(200);
                    Map<String, Object> map = new HashMap<>();
                    map.put("status", 200);
                    map.put("msg", principal);
                    ObjectMapper om = new ObjectMapper();
                    out.write(om.writeValueAsString(map));
                    out.flush();
                    out.close();
                }
            })
            // 定義登錄失敗的處理邏輯(可以跳轉到某一個頁面,也可以返會一段 JSON)
            .failureHandler(new AuthenticationFailureHandler() {
                @Override
                public void onAuthenticationFailure(HttpServletRequest req,
                                                    HttpServletResponse resp,
                                                    AuthenticationException e)
                        throws IOException, ServletException {
                    resp.setContentType("application/json;charset=utf-8");
                    PrintWriter out = resp.getWriter();
                    resp.setStatus(401);
                    Map<String, Object> map = new HashMap<>();
                    // 通過異常參數可以獲取登錄失敗的原因,進而給用戶一個明確的提示。
                    map.put("status", 401);
                    if (e instanceof LockedException) {
                        map.put("msg", "賬戶被鎖定,登錄失敗!");
                    }else if(e instanceof BadCredentialsException){
                        map.put("msg","賬戶名或密碼輸入錯誤,登錄失敗!");
                    }else if(e instanceof DisabledException){
                        map.put("msg","賬戶被禁用,登錄失敗!");
                    }else if(e instanceof AccountExpiredException){
                        map.put("msg","賬戶已過期,登錄失敗!");
                    }else if(e instanceof CredentialsExpiredException){
                        map.put("msg","密碼已過期,登錄失敗!");
                    }else{
                        map.put("msg","登錄失敗!");
                    }
                    ObjectMapper mapper = new ObjectMapper();
                    out.write(mapper.writeValueAsString(map));
                    out.flush();
                    out.close();
                }
            })
            .permitAll() // 允許訪問登錄表單、登錄接口
            .and().csrf().disable(); // 關閉csrf
    }
}

(2)在 resource/templates 目錄下創建一個登錄頁面 login_page.html,內容如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/login" method="post">
        <div>
            <label>用戶名</label>
            <input type="text" name="name"/>
        </div>
        <div>
            <label>密碼</label>
            <input type="password" name="passwd"/>
        </div>
        <div>
            <input type="submit" value="登陸">
        </div>
    </form>
</body>
</html>

 (3)最後我們自定義一個 MVC 配置,並重寫 addViewControllers 方法進行映射關係配置即可。

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/login_page").setViewName("login_page");
    }
}

 

2、運行測試 

(1) 隨便訪問一個接口,瀏覽器會自動跳轉到我們自定義的登錄頁面:

(2)如果填寫正確的用戶名密碼提交,則返回如下信息:

(3)如果填寫錯誤的用戶名密碼條,則返回如下信息:

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