在SpringSecurity中,会使用默认的FilterSecurityInterceptor来进行权限校验。
(1)在FilterSecurityInterceptor中会从SecurityContextHolder获取其中的Authentication,然后获取其中的权限信息。当前用户是否拥有访问当前资源所需的权限。
(2)所以我们在项目中只需要把当前登录用户的权限信息也存入Authentication
(3)然后设置我们的资源所需要的权限即可
目录
1.1、在配置类SecurityConfig.java中开启相关配置。添加注解@EnableGlobalMethodSecurity(prePostEnabled = true)
2.1、登录时。在UserDetailsServiceImpl.java中修改
2.2、校验时。在JwtAuthenticationTokenFilter.java中修改
3.1、RBAC权限模型:(Role-Based Access Control)
3.2.1在UserDetailsServiceImpl.java中修改
4.1 可以定义自己的权限校验方法,在@PreAuthorize注解中使用我们的方法。
4.2 在SPEL表达式中使用 @ex相当于获取容器中bean的名字为ex的对象。然后再调用这个对象的hasAuthority方法
1、限制资源所需权限
SpringSecurity为我们提供了基于注解的权限控制方案,这也是我们项目中主要采用的方式。我们可以使用注解去指定访问对应的资源所需的权限
1.1、在配置类SecurityConfig.java中开启相关配置。添加注解@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {}
1.2、使用对应的注解。@PreAuthorize
自定义权限校验在 4.1
@RestController
public class HelloController {
@RequestMapping("/hello")
@PreAuthorize("hasAuthority('test')")
public String hello(){
return "hello";
}
}
2、封装权限信息
2.1、登录时。在UserDetailsServiceImpl.java中修改
在写UserDetailsServiceImpl的时候说过,在查询出用户后还要获取对应的权限信息,封装到UserDetails中返回。
(1)先直接把权限信息写死封装到UserDetails中进行测试。(修改UserDetailsServiceImpl.java)
(2)之前定义了UserDetails的实现类LoginUser,想要让其能封装权限信息就要对其进行修改
@Data
@NoArgsConstructor
public class LoginUser implements UserDetails {
private User user;
private List<String> permissions;
public LoginUser(User user, List<String> permissions) {
this.user = user;
this.permissions = permissions;
}
// SimpleGrantedAuthority这个类型是spring内部提供的一个类,
// 但是到时候需要把 LoginUser存入到redis当中,
//而redis为了安全考虑,默认情况下是不会对 SimpleGrantedAuthority进行序列化,到时候会报异常
// 所以需要加上注解@JSONField(serialize = false)
// 所以 成员变量authorities就不会被 序列化到 redis当中
@JSONField(serialize = false)
private List<SimpleGrantedAuthority> authorities;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
if (authorities != null){
return authorities;
}
// 把permissions中String类型的权限信息封装成SimpleGrantedAuthority对象
// authorities = new ArrayList<>();
// for (String permission : permissions) {
// SimpleGrantedAuthority authority = new SimpleGrantedAuthority(permission);
// authorities.add(authority);
// }
// 法二 函数式编程 stream流
authorities = permissions.stream()
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
return authorities;
}
}
【注】不使用Security提供的权限校验方法时;
自定义权限校验方法的时候,即直接取permissiond集合的数据进行校验
2.2、校验时。在JwtAuthenticationTokenFilter.java中修改
// 存入SecurityContextHolder
// 获取权限信息封装到Authentication中
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
3、从数据库查询权限信息
3.1、RBAC权限模型:(Role-Based Access Control)
即:基于角色的权限控制。这是目前最常被开发者使用也是相对易用、通用权限模型
3.2、代码实现
3.2.1在UserDetailsServiceImpl.java中修改
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserMapper userMapper;
@Autowired
private MenuMapper menuMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 认证流程图5.1:查询用户信息
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(User::getUserName, username);
User user = userMapper.selectOne(queryWrapper);
// 如果没有查询到用户就抛出异常
if (Objects.isNull(user)){
throw new RuntimeException("用户名或密码错误");
}
// 认证流程图5.1:查询对应权限信息
// List<String> list = new ArrayList<>(Arrays.asList("test", "admin"));
List<String> list = menuMapper.selectPermsByUserId(user.getId());
// 认证流程图5.2:把数据封装成UserDetails返回。
return new LoginUser(user, list); // 因为UserDetails是接口,所以返回的应为 它的实现类
}
}
4、自定义权限校验
4.1 可以定义自己的权限校验方法,在@PreAuthorize注解中使用我们的方法。
@Component("ex")
public class WheyExpressionRoot {
public boolean hasAuthority(String authority){
// 获取当前用户的权限 在过滤器JwtAuthenticationTokenFilter中,权限存到了 SecurityContextHolder
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
List<String> permissions = loginUser.getPermissions();
// 判断用户权限集合中是否存在authority
return permissions.contains(authority);
}
}
4.2 在SPEL表达式中使用 @ex相当于获取容器中bean的名字为ex的对象。然后再调用这个对象的hasAuthority方法
@RestController
public class HelloController {
@RequestMapping("/hello")
// 在这之前需在配置类SecurityConfig中开启注解 @EnableGlobalMethodSecurity(prePostEnabled = true)
// @PreAuthorize("hasAuthority('system:test:list')")
@PreAuthorize("@ex.hasAuthority('system:test:list')")
public String hello(){
return "hello!";
}
}
5、基于配置的权限控制
@RestController
public class HelloController {
@RequestMapping("/testCors")
public ResponseResult testCors(){
return new ResponseResult(200,"testCors");
}
}
在配置类SecurityConfig.java中配置
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
//关闭csrf
.csrf().disable()
//不通过Session获取SecurityContext
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
// 对于登录接口 允许匿名访问。即对接口进行放行
.antMatchers("/user/login").anonymous()
.antMatchers("/testCors").hasAuthority("system:test:list") // 基于配置的权限控制
}
}