SpringSecurity
授权
权限系统可以使得不同的用户可以使用不同的功能
所以我们需要在后端进行相应的权限判断,判断当前用户是否具有相应的权限,必须基于所需权限才能进行相应的操作
- 授权原理
在SpringSecurity中,会使用默认的FilterSecurityInterceptor来进行权限校验,在FilterSecurityInterceptor中会从securityContextHolder获取其中的Authentication,然后获取其中的权限信息,当前用户是否拥有访问当前资源所需的权限
框架提供了基于注解的权限控制的方案:
开启权限注解的相关配置:
java
@EnableGlobalMathodSecurity(prePostEnabled= true)
注解放在所继承WebSecurityConfigurerAdapter的配置类上
然后可以使用对应的权限注解
java
@RestController
public class HelloController{
@RequestMapping("/hello")
@PreAuthorize("hasAuthority('test')")
public String hello(){
return "hello";
}
}
注解中的hasAuthority是一个方法,在进行判断的时候根据注解中传入的参数进行方法来进行判断
我们需要对UserDetails中完善授权信息部分:
在实现UserDetails的实现类中由方法getAuthorities()来返回控制权限的信息
java
public class userdatails implements UserDetails{
private List<String> primissions;
@JSONField(serialize=false)
private List<SimpleGrantedAuthority> authorities;
public Colletion<? extends GrantedAuthority> getAuthorities(){
if(authorities!=null)
{
return authorities;
}
authorities=permissions.stream()
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
return authorities;
}
}
在这个之中我们从数据库访问回来的权限信息放在List集合之中,但是在重写的方法getAuthorities()
中它的返回信息是一个继承自GrantedAuthority
的对象集合,我们需要将权限信息封装成每一个它的子类对象SimpleGrantedAuthority
但是我们需要将控制信息放在redis中,不能够将spring提供的该对象进行序列化保存,所以我们需要通过注解 @JSONField对这个对象集合进行忽略,我们只需要将primissions
进行序列化保存即可
在实现UserDetailsService的实现类中,我们封装一个权限集合并同User信息一起返回
java
public class UserDetailsserviceimpl implements UserDetailsService{
//封装一个权限的List集合
List<String> list=new ArrayList<>(Arrays.asList("test"));
return new User(user,list);
}
在jwt过滤器中再将之前封装到UserDetails的权限信息封装到Authentication中
具体的过滤器请看博主第一篇,博主在这里对之前的内容进行添加
java
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken=new UsernamePasswordAuthenticationToken(user,null,user.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
从数据库中获取权限信息
RBAC权限模型
RBAC权限模型(Role-Based Access Control)基于角色的权限控制
上述我们在获取数据库中权限信息中我们直接给定权限信息:
java
List<String> list=new ArrayList<>(Arrays.asList("test"));
但在真实中是通过数据库来的
我们可以将所有的操作权限放在一个权限表中:
id | 权限信息 |
---|---|
1 | 删除图书 |
2 | 查看图书 |
比如这样的表中,这时我们应该为用户表
中的用户配备它的权限,但是由于权限太多,我们需要在启用一个表角色表
,将对应的很多权限成组赋予这个角色,而用户表中的每个用户就只用承担对应的角色即可
一个角色可以由多个权限,一个权限可以赋予多个角色,属于多对多的关系,我们可以在创建一个关联表来实现它的关联关系
例如:
角色id | 权限id |
---|---|
1 | 1 |
1 | 2 |
2 | 2 |
而用户和角色之间也是一个多对多的关系也需要一个关联表来实现它们的关系
创建权限对应的实体类 (我们默认实体类为qxtable)
在Mapper层下实现一个查询权限信息的多表查询的操作,再将查询到的权限字符串封装到List集合当中在封装到UserDetails中
异常处理
在SpringSecurity中,如果我们在认证或者授权失败之后异常会被ExceptionTranslationFilter过滤器捕获到,
如果是认证失败出现的异常会被封装到AuthenticationException然后调用AuthenticationEntryPoint对象的方法去进行异常处理
如果是认证过程中出现的异常会被封装为AccessDeniedException然后调用AccessDenielHandler对象的方法去进行异常处理
将上述两个对象配置到SpringSecurity的配置类当中
java
//配置异常处理器
http.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.accessDeniedHandler(accessDeniedHandler)
跨域问题
跨域问题通常是由浏览器的安全策略所引起的,称为同源策略(Same-Origin Policy),它限制了页面从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。
我们通过SpringMVC提供的WebMvcConfigurer接口可以配置CORS头部,来解决跨域请求问题:
java
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
//设置允许跨域的路径
registry.addMapping("/**")
//设置允许跨域请求的域名
.allowedOrigins("*")
//设置允许的请求方式
.allowedMethods("GET", "POST", "PUT", "DELETE")
//设置允许的header属性
.allowedHeaders("Content-Type", "Authorization")
//是否允许使用cookie
.allowCredentials(true)
//跨域允许时间
.maxAge(3600);
}
}
上述是SpringBoot中允许跨域的配置,但是在使用了SpringSecurity之后,由于资源被SpringSecurity进行保护,所以我们要进行跨域访问还要让SpringSecurity运行跨域访问
这里我们仍然在SpringSecurity中的configure方法中进行跨域配置
java
//允许跨域配置
http.cors();