ruoyi介面許可權校驗
此文章屬於ruoyi項目實戰系列
ruoyi系統在前端主要通過許可權字元包含與否來動態顯示目錄和按鈕。為了防止通過http請求繞過許可權限制,後端介面也需要進行相關許可權設計。
@PreAuthorize使用
由於對@PreAuthorize
原理還不夠深入了解,所以此處只粗淺講解在ruoyi項目是如何應用的。
在請求調用介面前,被@preAuthorize
註解的介面需要首先通過驗證。通過註解參數value()
返回值true
和false
來判斷是否有許可權。
public @interface PreAuthorize {
String value();
}
Ruoyi並沒有使用原生的Spel表達式,而是使用了自定義的PermissionService
類,通過其中自定義方法hasPermi(String Permission)
來進行許可權判斷。註解使用舉例:@PreAuthorize("@ss.hasPermi('system:menu:list')")
public boolean hasPermi(String permission)
{
if (StringUtils.isEmpty(permission))//用註解就必須有permission值
{
return false;
}
LoginUser loginUser = SecurityUtils.getLoginUser();
if (StringUtils.isNull(loginUser) ||
CollectionUtils.isEmpty(loginUser.getPermissions()))
{
return false;
}
return hasPermissions(loginUser.getPermissions(), permission);
private boolean hasPermissions(Set<String> permissions, String permission)
{
return permissions.contains(ALL_PERMISSION) ||
permissions.contains(StringUtils.trim(permission)); //判斷是否持有"所有許可權」字元,或者持有該許可權
}
介面許可權校驗流程
粗略用兩個例子來講解前端請求如何經過後端介面許可權校驗。
Login匿名請求
-
Login請求路徑是
/login
,在過濾器鏈中被AnnoymousAuthenticationFilter
添加匿名authentication
到Spring上下文里。由於/login
請求在SecurityConfig.java
里設置成匿名請求,所以可以成功到達SysLoginController
。 -
調用
SysLoginService.login
方法,關鍵的一行命令:Authentication authentication = authenticationManager .authenticate(new UsernamePasswordAuthenticationToken(username, password));
authenticationManager.authenticate()
是鉤子方法,在AbstractUserDetailsAuthenticationProvider
中實現,會根據傳入的token類型來自動選擇,此處UsernamePasswordAuthenticationToken
將由DaoAuthenticationProvider
來處理(不清楚的話可以前後打兩個斷點看調用棧)。 -
在
DaoAuthenticationProvider
中可以看到關鍵的一行:UserDetails loadedUser = this.getUserDetailsService() .loadUserByUsername(username);
這會調用我們自定義實現的
UserDetailsServiceImpl#loadUserByUsername
方法(如流程圖所示),獲得user資訊。至於為什麼會使用自定義方法,因為在SecurityConfig.java
中進行了配置@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder()); }
-
生成token,然後返回。
已登錄請求
已登錄請求流程較簡單,在流程圖裡的some filters里會通過自定義的JwtAuthenticationFilter
,其中會通過token獲得user資訊,然後裝入Spring
的上下文,方便提取使用。
曾糾結踩坑的點
由於對SpringSecurity較陌生,雖然功能強大,但其複雜性也是大大提高,所以調試項目的同時翻看了很多入門部落格文章,其中都不約而同的提到了UsernamePasswordAuthenticationFilter
,可是我在實戰項目中反覆調試都沒有看到這個過濾器的調用。
原因:Security配置文件需要添加httpSecurity.formLogin()
啟用表單登錄才會使用該filter。查看項目使用的所有filter可以使用以下測試程式碼:
class RuoYiApplicationTest {
@Autowired
private FilterChainProxy filterChainProxy;
@Test
public void test() {
List<SecurityFilterChain> filterChains = filterChainProxy.getFilterChains();
for(SecurityFilterChain sfc:filterChains){
for(Filter filter:sfc.getFilters()){
System.out.println(filter.getClass().getName());
}
}
}
}