🚀 优质资源分享 🚀
学习路线指引(点击解锁) | 知识定位 | 人群定位 |
---|---|---|
🧡 Python实战微信订餐小程序 🧡 | 进阶级 | 本课程是python flask+微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一个全栈订餐系统。 |
💛Python量化交易实战💛 | 入门级 | 手把手带你打造一个易扩展、更安全、效率更高的量化交易系统 |
此文章属于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 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
来处理(不清楚的话可以前后打两个断点看调用栈)。
3. 在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 filterChains = filterChainProxy.getFilterChains(); for(SecurityFilterChain sfc:filterChains){ for(Filter filter:sfc.getFilters()){ System.out.println(filter.getClass().getName()); } } }
}