springsecurity 分2部分,登錄驗證和權限管理,在登錄驗證成功之後會在 SecurityContextHolder 上線文中添加 Authentication 對象,包含用戶信息和角色信息,用於權限管理和系統驗證用戶信息保存。
源碼地址:https://github.com/huwenhu2007/springsecurityjwt/tree/master
登錄驗證
- 驗證相關類
AbstractAuthenticationProcessingFilter 信息驗證抽象類
UsernamePasswordAuthenticationFilter 登錄信息驗證攔截器,默認URL驗證 new AntPathRequestMatcher("/login", "POST"),會對 /login 請求進行攔截驗證
AuthenticationManager 驗證管理類(接口),驗證用戶
authenticate 驗證傳入用戶信息與
AbstractUserDetailsAuthenticationProvider 驗證抽象類
DaoAuthenticationProvider 實現類,從數據庫獲取信息
UserDetailsService 調用該接口的實現類,通過登錄帳號獲取賬戶信息
- 驗證的3種實現方式
1. 使用默認的認證方式
formLogin() 基於表單登錄
loginPage() 登錄頁
defaultSuccessUrl 登錄成功後的默認處理頁
failuerHandler登錄失敗之後的處理器
successHandler登錄成功之後的處理器
failuerUrl登錄失敗之後系統轉向的url,默認是this.loginPage + "?error"
配置:http.requestMatchers().antMatchers("/api/**")
.and().authorizeRequests().antMatchers("/user","/api/user").authenticated()
.anyRequest().authenticated()
.and().formLogin().loginPage("/login");
2. 自定義驗證攔截器
參考:https://www.cnblogs.com/lori/p/10400564.html
3. 自定義登錄驗證,將登錄驗證器中的驗證方法取出,添加到自己的驗證邏輯中,本人使用該種方式實現
//用戶驗證
final Authentication authentication = authenticate(username, password);
//存儲認證信息
SecurityContextHolder.getContext().setAuthentication(authentication);
private Authentication authenticate(String username, String password) {
try {
//該方法會去調用userDetailsService.loadUserByUsername()去驗證用戶名和密碼,如果正確,則存儲該用戶名密碼到“security 的 context中”
return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
} catch (DisabledException | BadCredentialsException e) {
throw new RuntimeException("用戶名或密碼無效");
}
}
權限管理
攔截器 AbstractSecurityInterceptor 實現權限管理,由 FilterInvocationSecurityMetadataSource 的 getAttributes 方法獲取所有資源與角色的信息,然後過濾出當前資源的角色,並返回給權限管理器 AccessDecisionManager 使用,權限管理器獲取 SecurityContextHolder 上下文中保存的用戶和角色信息,判斷用戶是否有操作該資源的權限。權限足夠則直接返回,權限不夠則提示對應異常。交易異常處理器處理。
遇到的坑及處理方法
- 使用自定義JWT-TOKEN過濾器JwtAuthenticationTokenFilter之後,該過濾器會忽略 web.ignoring() 和 permitAll 的配置,因爲要實現token保存在cookie的方式,導致登錄時不走帳號、密碼驗證而是走了token驗證。
解決方法:添加 AntPathMatcher antPathMatcher = new AntPathMatcher(); 配置解析器,對不需要進行token驗證的請求進行過濾
if(antPathMatcher.match("/auth/*", httpServletRequest.getRequestURI())){
// 過濾不需要通過自定義過濾器的鏈接
filterChain.doFilter(httpServletRequest, httpServletResponse);
return ;
}
- 添加使用了註解 @Component 的自定義權限過濾器 MyFilterSecurityInterceptor 之後,該過濾器會忽略 web.ignoring() 和 permitAll 的配置。
解決方法:去掉註解 @Component,使用new的方式創建對象添加
httpSecurity.addFilterBefore(new MyFilterSecurityInterceptor(myInvocationSecurityMetadataSourceService, myAccessDecisionManager), FilterSecurityInterceptor.class);
源碼地址:https://github.com/huwenhu2007/springsecurityjwt/tree/master