此文章属于ruoyi项目实战系列
ruoyi系统在前端主要通过权限字符包含与否来动态显示目录和按钮。为了防止通过http请求绕过权限限制,后端接口也需要进行相关权限设计。
@PreAuthorize使用
由于对@PreAuthorize
原理还不够深入了解,所以此处只粗浅讲解在ruoyi项目是如何应用的。
在请求调用接口前,被@preAuthorize
注解的接口需要首先通过验证。通过注解参数value()
返回值true
和false
来判断是否有权限。
|
|
Ruoyi并没有使用原生的Spel表达式,而是使用了自定义的PermissionService
类,通过其中自定义方法hasPermi(String Permission)
来进行权限判断。注解使用举例:@PreAuthorize("@ss.hasPermi('system:menu:list')")
|
|
接口权限校验流程
粗略用两个例子来讲解前端请求如何经过后端接口权限校验。
Login匿名请求
-
Login请求路径是
/login
,在过滤器链中被AnnoymousAuthenticationFilter
添加匿名authentication
到Spring上下文里。由于/login
请求在SecurityConfig.java
里设置成匿名请求,所以可以成功到达SysLoginController
。 -
调用
SysLoginService.login
方法,关键的一行命令:1 2
Authentication authentication = authenticationManager .authenticate(new UsernamePasswordAuthenticationToken(username, password));
authenticationManager.authenticate()
是钩子方法,在AbstractUserDetailsAuthenticationProvider
中实现,会根据传入的token类型来自动选择,此处UsernamePasswordAuthenticationToken
将由DaoAuthenticationProvider
来处理(不清楚的话可以前后打两个断点看调用栈)。 -
在
DaoAuthenticationProvider
中可以看到关键的一行:1 2
UserDetails loadedUser = this.getUserDetailsService() .loadUserByUsername(username);
`` 这会调用我们自定义实现的
UserDetailsServiceImpl#loadUserByUsername
方法(如流程图所示),获得user信息。至于为什么会使用自定义方法,因为在SecurityConfig.java
中进行了配置1 2 3 4 5
@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可以使用以下测试代码:
|
|