1. 程式人生 > 實用技巧 >SpringBoot學習-自定義UsernamePasswordAuthenticationFilter-(Security自定義登入驗證規則)

SpringBoot學習-自定義UsernamePasswordAuthenticationFilter-(Security自定義登入驗證規則)

  眾所周知SpringBoot-Security可以繼承WebSecurityConfigurerAdapter來進行登入驗證,若我們想自定義登入驗證的規則,則可以編寫一個登入過濾的類通過繼承UsernamePasswordAuthenticationFilter,關於這方面的內容,我是通過看鬆哥的微人事專案進行的簡單學習,這裡貼上鬆哥的部落格地址:http://springboot.javaboy.org/2019/1120/springboot-vue,感謝鬆哥的貢獻。

  我來簡單的寫一下個人對這個的理解,若有錯誤之處,希望大家能在評論區指出。

 1 /**
 2  * 該類主要是自定義登入驗證的規則,包括檢查驗證碼
3 */ 4 public class LoginFilter extends UsernamePasswordAuthenticationFilter { 5 @Autowired 6 private SessionRegistry sessionRegistry; //用於向session裡註冊使用者 7 8 @Override 9 public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws
AuthenticationException { 10 //登入表單只支援post,進行驗證 11 if(!request.getMethod().equals("POST")){ 12 throw new AuthenticationServiceException( 13 "Authentication method not supported: " + request.getMethod()); 14 } 15 //如果請求型別為json的話,自定義處理 16 String localCode = (String) request.getSession().getAttribute("verify_code");
17 String verifyCode = null; 18 if(request.getContentType().contains(MediaType.APPLICATION_JSON_VALUE) || request.getContentType().contains(MediaType.APPLICATION_JSON_UTF8_VALUE)){ 19 Map<String,String> loginData = new HashMap<>();//用於存放登入使用者資訊的鍵值對 20 try { 21 //把請求裡資訊讀進map 22 loginData = new ObjectMapper().readValue(request.getInputStream(), Map.class); 23 } catch (IOException e) { 24 e.printStackTrace(); 25 }finally { 26 verifyCode = loginData.get("code"); 27 checkCode(localCode,verifyCode); 28 //以後要在這裡進行檢查驗證碼 29 } 30 //通過父類提供的方法獲取使用者名稱密碼 31 String username = loginData.get(getUsernameParameter()); 32 String password = loginData.get(getPasswordParameter()); 33 //這一步照抄父類的處理,可能是處理為空時候的使用者密碼? 34 if(username==null){ 35 username=""; 36 } 37 if(password==null){ 38 password=""; 39 } 40 username=username.trim(); //去除使用者名稱前後的空格 41 //開始驗證 42 UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password); 43 setDetails(request,authRequest); 44 //向session裡註冊此使用者 45 Hr principal = new Hr(); 46 principal.setName(username); 47 sessionRegistry.registerNewSession(request.getSession(true).getId(),principal); 48 return this.getAuthenticationManager().authenticate(authRequest); //獲取身份驗證管理器,提交令牌驗證 49 } else { 50 //檢查一下驗證碼,然後直接呼叫父類的 51 checkCode(localCode,verifyCode); 52 return super.attemptAuthentication(request,response); 53 } 54 } 55 56 private void checkCode(String localCode,String verifyCode){ 57 if(verifyCode==null || verifyCode.equals("") || localCode==null || localCode.equals("") || !localCode.toLowerCase().equals(verifyCode.toLowerCase())){ 58 throw new AuthenticationServiceException("驗證碼不正確!"); 59 } 60 } 61 }

  在這篇程式碼中,主要是希望登入資料以JSON的方式傳送過來,所以在開頭進行了request型別是否為JSON的驗證,又通過JACKSON提供的ObjectMapper().readValue將request請求的資料讀成Map形式儲存。中間的處理過程完全參照父類UsernamePasswordAuthenticationFilter裡的處理,最後向session註冊一個使用者。

  綜上舉一反三,若想進行其他的一些資料處理或者判斷,都可以以繼承這個類的方式自定義,比如裡面的驗證碼的驗證。父類還提供了一些其他非常有用的方法,比如getUsernameParameter,可以直接獲取到傳過來的username,當然你也可以自定義。

  最後在SecurityConfig裡new 一個LoginFilter的例項,在重寫的configure(HttpSecurity http)方法裡,以AOP形式織入。

http.addFilterAt(loginFilter(), UsernamePasswordAuthenticationFilter.class);