1. 程式人生 > >SpringSecurity許可權管理系統實戰—七、處理一些問題

SpringSecurity許可權管理系統實戰—七、處理一些問題

## 目錄 [SpringSecurity許可權管理系統實戰—一、專案簡介和開發環境準備](https://www.cnblogs.com/codermy/p/13516372.html) [SpringSecurity許可權管理系統實戰—二、日誌、介面文件等實現](https://www.cnblogs.com/codermy/p/13516369.html) [SpringSecurity許可權管理系統實戰—三、主要頁面及介面實現](https://www.cnblogs.com/codermy/p/13516379.html) [SpringSecurity許可權管理系統實戰—四、整合SpringSecurity(上)](https://www.cnblogs.com/codermy/p/13516385.html) [SpringSecurity許可權管理系統實戰—五、整合SpringSecurity(下)](https://www.cnblogs.com/codermy/p/13516388.html) [SpringSecurity許可權管理系統實戰—六、SpringSecurity整合jwt](https://www.cnblogs.com/codermy/p/13516394.html) [SpringSecurity許可權管理系統實戰—七、處理一些問題](https://www.cnblogs.com/codermy/p/13516397.html) [SpringSecurity許可權管理系統實戰—八、AOP 記錄使用者日誌、異常日誌](https://blog.csdn.net/HYDCS/article/details/107965522) ## 前言 在寫完上一篇文章之後,我又研究了很久。最終我發現似乎我們這個專案不太適合用jwt。layui不像vue那樣可以通過axios 全域性設定token(或許是我因為我菜,不知道怎麼設定,如果有小夥伴有好的辦法,歡迎留言告訴我)。 這裡稍微介紹下前端怎麼操作(vue為例),主要就是拿到token以後將其儲存在localstorage或者cookies中,再從localstorage或者cookies中拿到token設定全域性的請求頭,就可以了。 但是前一篇文章關於jwt的內容是沒有問題的,正常的使用也是那樣的步驟。 ## 具體內容 ### 去除JWT 那麼既然不打算再用jwt了,就要老老實實的回去用cookies和session。那麼我們需要把我們的專案還原成實戰五結束時候的樣子。 這裡不是很好解釋了,就是把與jwt相關的刪除就行了,需要修改的地方有MyAuthenticationSuccessHandler,JwtAuthenticationTokenFilter,SpringSecurityConfig三處,刪去有關jwt的內容即可。(不多費篇幅) ### 前端提示資訊 我這裡修改了一下登入頁面,讓其能夠在登入失敗時,給出提示資訊 ```html M-S-P Admin Spring Security 權 限 管 理 系 統 實 戰 ``` 後端也做了登入失敗的處理器 ```java /** * @author codermy * @createTime 2020/8/2 */ @Component public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler { @Override public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException { httpServletResponse.setCharacterEncoding("utf-8");//修改編碼格式 httpServletResponse.setContentType("application/json"); httpServletResponse.getWriter().write(JSON.toJSONString(Result.error().message(e.getMessage())));//返回資訊 } } ``` AuthenticationFailureHandler是一個抽象的異常類,他的常見子類為 ```java UsernameNotFoundException 使用者找不到 BadCredentialsException 壞的憑據 AccountStatusException 使用者狀態異常它包含如下子類 AccountExpiredException 賬戶過期 LockedException 賬戶鎖定 DisabledException 賬戶不可用 CredentialsExpiredException 證書過期 ``` 都是在使用者登入時可能會遇到的異常 修改後完整的SpringSecurityConfig ```java /** * @author codermy * @createTime 2020/7/15 */ @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class SpringSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsServiceImpl userDetailsService; @Autowired private VerifyCodeFilter verifyCodeFilter;//驗證碼攔截器 @Autowired MyAuthenticationSuccessHandler authenticationSuccessHandler;//登入成功邏輯 @Autowired private MyAuthenticationFailureHandler authenticationFailureHandler;//登入失敗邏輯 @Autowired private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;//jwt攔截器 @Autowired private RestAuthenticationEntryPoint restAuthenticationEntryPoint;//無許可權攔截器 @Autowired private RestfulAccessDeniedHandler accessDeniedHandler;// 無權訪問 JSON 格式的資料 @Override public void configure(WebSecurity web) throws Exception { web.ignoring() .antMatchers(HttpMethod.GET, "/swagger-resources/**", "/PearAdmin/**", "/**/*.html", "/**/*.css", "/**/*.js", "/swagger-ui.html", "/webjars/**", "/v2/**");//放行靜態資源 } /** * anyRequest | 匹配所有請求路徑 * access | SpringEl表示式結果為true時可以訪問 * anonymous | 匿名可以訪問 * denyAll | 使用者不能訪問 * fullyAuthenticated | 使用者完全認證可以訪問(非remember-me下自動登入) * hasAnyAuthority | 如果有引數,引數表示許可權,則其中任何一個許可權可以訪問 * hasAnyRole | 如果有引數,引數表示角色,則其中任何一個角色可以訪問 * hasAuthority | 如果有引數,引數表示許可權,則其許可權可以訪問 * hasIpAddress | 如果有引數,引數表示IP地址,如果使用者IP和引數匹配,則可以訪問 * hasRole | 如果有引數,引數表示角色,則其角色可以訪問 * permitAll | 使用者可以任意訪問 * rememberMe | 允許通過remember-me登入的使用者訪問 * authenticated | 使用者登入後可訪問 */ @Override protected void configure(HttpSecurity http) throws Exception { http.addFilterBefore(verifyCodeFilter, UsernamePasswordAuthenticationFilter.class); http.csrf().disable()//關閉csrf // .sessionManagement()// 基於token,所以不需要session // .sessionCreationPolicy(SessionCreationPolicy.STATELESS) // .and() .httpBasic().authenticationEntryPoint(restAuthenticationEntryPoint)//未登陸時返回 JSON 格式的資料給前端 .and() .authorizeRequests() .antMatchers("/captcha").permitAll()//任何人都能訪問這個請求 .anyRequest().authenticated() .and() .formLogin() .loginPage("/login.html")//登入頁面 不設限訪問 .loginProcessingUrl("/login")//攔截的請求 .successHandler(authenticationSuccessHandler) // 登入成功 .failureHandler(authenticationFailureHandler) // 登入失敗 .permitAll() .and() .rememberMe().rememberMeParameter("rememberme") // 防止iframe 造成跨域 .and() .headers() .frameOptions() .disable() .and(); // 禁用快取 http.headers().cacheControl(); // 新增JWT攔截器 // http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); http.exceptionHandling().accessDeniedHandler(accessDeniedHandler); // 無權訪問返回JSON 格式的資料 } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(12); } /** * 身份認證介面 */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); } } ``` ### 遇到的問題 #### 一、Springsecurity中的UsernameNotFoundException異常無法被正常捕獲 具體的解釋可以看[這篇文章](https://www.cnblogs.com/gcdd/p/12292335.html)(非常詳細,包括解決方案) 簡而言之,就是我丟擲了UsernameNotFoundException異常但是最後會被轉換為BadCredentialsException異常。我這裡不多做介紹了,上面那篇文章說的非常詳細。 ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200803160335401.gif#pic_center) 如何解決也請參照那篇文章。我所使用的是取巧的方法,就是直接丟擲BadCredentialsException異常而不是UsernameNotFoundException異常。因為畢竟最後給出的提示資訊是模糊的“使用者名稱或密碼錯誤”,而不是具體到哪個錯誤了。 #### 二、無法統一處理filter中丟擲的異常 這個問題主要是和驗證碼的攔截器有關,前端拿不到驗證碼錯誤的提示資訊。這裡我們可以不用攔截器來處理驗證碼,可以自定義一個login請求來避開這個問題。 這個問題也是原本的寫法問題吧,其實原本需要用拋這個異常,直接向頁面輸出提示資訊就好了。 我在找處理方法時找到有兩種方法供大家參考 - 1、[spring boot 專案在自定義Filter中丟擲異常捕獲不到的處理方法](https://blog.csdn.net/Chen_RuiMin/article/details/104418904?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.edu_weight&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.edu_weight) - 2、[全域性統一異常處理無法攔截FILTER中CATCH的異常](https://www.freesion.com/article/7691642390/) ## 後敘 這篇文章有點亂,博主的文筆真的不太行,所以在描述一些問題的時候可能會有點難以理解。如果小夥伴們在學習過程中有什麼問題,歡迎大家加我的qq(在我的碼雲主頁有)我們一起探討學習。 下一篇文章我們實現使用者的操作日誌和異常日誌功能 **在[gitee](https://gitee.com/witmy/my-springsecurity-plus)和[github](https://github.com/witmy/my-springsecurity-plus)中可獲取原始碼,與本系列文章同步