SpringSecurity怎样使⽤注解控制权限
⼀般的系统在权限设计上,都会分为⾓⾊、权限(RDBC),复杂⼀点的可能会有⽤户组、组织之类的概念。
⽤户的权限是写死的,对应于后台的接⼝或者资源,是没办法改变的,⼀般不对⽤户开放修改权限。
管理员⽤户可以通过给⾓⾊分配权限的⽅式,来实现访问控制。
所以当我们写过滤器,或者⽤⼀些安全框架时(⽐如Shiro,Spring Security),也需要将可变的“⾓⾊”,转化为不可变的“权限”,注⼊到框架中。
具体的可以看我之前写的⼀篇()
注⼊当前⽤户的权限后,就需要进⾏访问控制了。
常见的做法有
1、路径⽐对
之前有个项⽬⽤过⼀次,定义⼀个过滤器,添加到security的过滤链中,在这个过滤器中做这么⼀件事:
分析当前访问路径所需要的权限,检查当前⽤户是否具有该权限,做⼀个对⽐,根据对⽐结果来决定当前⽤户是否可以访问该资源。
这种做法的好处是代码的⼊侵性不⾼,不需要再每个接⼝上加注解。但相对来说,显得不那么直观,可读性⽐较差,所以这次换个⽅法。
2、使⽤注解的⽅式
SpringSecurity使⽤注解来控制访问时,需要提前开启这个功能。
在配置类上加上注解
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
在接⼝中如此使⽤
/**
* 条件查询
*/
@PreAuthorize("hasAuthority('IMPORT:SELECT')")
@ApiOperation(value = "查询")
@GetMapping("/list")
public R<Page<Task>> list(TaskDto taskDto){
//测试阶段,随机⽣成任务
Random random = new Random();
//todo
int i = Int(4);
if(i == 2) {
}
Page<Task> list = taskService.list(taskDto);
return R.ok(list);
}
hasAuthority可以替换成hasRole,虽然可以达到相同的⽬的,但是在使⽤的⽅法上还是有些不同的。
hasRole要求内容必须以"ROLE_"开头,也是官⽅推荐的命名⽅式,否则没有效果。但是hasAuthority没有限制,数据库中怎样写的,代码⾥就怎么写。
同样的功能的注解为什么要有两个名字呢。或许这么做可能在语义上⽐较清晰明确⼀点,将⾓⾊与权限这两个概念稍加区分。
仔细想⼀下,确实有些⼩型的系统或许压根就不需要权限,只有给⽤户分配⾓⾊,没有给⾓⾊分配权
限这⼀过程。这样的话,⾓⾊也是不可变的,就可以根据⾓⾊来做访问控制了。
但考虑通⽤性,个⼈觉得⽤hasAuthority就可以了。
SpringSecurity_权限注解@PreAuthorize、@PostAuthorize
spring是如何实现对HTTP请求进⾏安全检查和资源使⽤授权的?
实现过程由类AbstractSecurityInterceptor在beforeInvocation⽅法中完成,在beforeInvocation的实现中,
⾸先,需要读取IoC容器中Bean的配置,在这些属性配置中配置了对HTTP请求资源的安全需求,
⽐如,哪个⾓⾊的⽤户可以接⼊哪些URL请求资源,具体实现逻辑见:
#与Web环境的接⼝FilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter
@PreAuthorize、@PostAuthorize注解实现逻辑
继承根节点:SecurityMetaSource
可以通过Spring注解声明,需要依赖类注⼊,实现权限灵活配置如:
@Component("securityMetadataSource")
public class MySecurityMetadataSource implements FilterInvocationSecurityMetadataSource PrePostAnnotationSecurityMetadataSource类继承关系
AbstractMethodSecurityMetadataSource类继承关系
package org.springframework.security.access.prepost;
import java.lang.annotation.Annotation;
import flect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import annotation.AnnotationUtils;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.hod.AbstractMethodSecurityMetadataSource;
import org.springframework.util.ClassUtils;
public class PrePostAnnotationSecurityMetadataSource extends AbstractMethodSecurityMetadataSource {
private final PrePostInvocationAttributeFactory attributeFactory;
public PrePostAnnotationSecurityMetadataSource(PrePostInvocationAttributeFactory attributeFactory) {
this.attributeFactory = attributeFactory;
}
public Collection<ConfigAttribute> getAttributes(Method method, Class<?> targetClass) {
if (DeclaringClass() == Object.class) {
ptyList();
} else {
ace("Looking for Pre/Post annotations for method '" + Name() + "' on target class '" + targetClass + "'");
PreFilter preFilter = (PreFilter)this.findAnnotation(method, targetClass, PreFilter.class);
PreAuthorize preAuthorize = (PreAuthorize)this.findAnnotation(method, targetClass, PreAuthorize.class);
PostFilter postFilter = (PostFilter)this.findAnnotation(method, targetClass, PostFilter.class);
PostAuthorize postAuthorize = (PostAuthorize)this.findAnnotation(method, targetClass, PostAuthorize.class);
if (preFilter == null && preAuthorize == null && postFilter == null && postAuthorize == null) {
ace("No expression annotations found");
ptyList();
} else {
String preFilterAttribute = preFilter == null ? null : preFilter.value();
String filterObject = preFilter == null ? null : preFilter.filterTarget();
String preAuthorizeAttribute = preAuthorize == null ? null : preAuthorize.value();
String postFilterAttribute = postFilter == null ? null : postFilter.value();
String postAuthorizeAttribute = postAuthorize == null ? null : postAuthorize.value();
ArrayList<ConfigAttribute> attrs = new ArrayList(2);
PreInvocationAttribute pre = atePreInvocationAttribute(preFilterAttribute, filterObject, preAuthorizeAttribute); if (pre != null) {
attrs.add(pre);
}
PostInvocationAttribute post = atePostInvocationAttribute(postFilterAttribute, postAuthorizeAttribute);
if (post != null) {
attrs.add(post);
}
return attrs;
}
}
}
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
private <A extends Annotation> A findAnnotation(Method method, Class<?> targetClass, Class<A> annotationClass) { Method specificMethod = MostSpecificMethod(method, targetClass);
spring ioc注解A annotation = AnnotationUtils.findAnnotation(specificMethod, annotationClass);
if (annotation != null) {
this.logger.debug(annotation + " found on specific method: " + specificMethod);
return annotation;
} else {
if (specificMethod != method) {
annotation = AnnotationUtils.findAnnotation(method, annotationClass);
if (annotation != null) {
this.logger.debug(annotation + " found on: " + method);
return annotation;
}
}
annotation = AnnotationUtils.DeclaringClass(), annotationClass);
if (annotation != null) {
this.logger.debug(annotation + " found on: " + DeclaringClass().getName());
return annotation;
} else {
return null;
}
}
}
}
//注解开启权限
@EnableResourceServer
@EnableGlobalMethodSecurity
SecurityContextHolder作为全局缓存,从上下⽂获取授权信息
Authentication authentication = Context().getAuthentication();
Collection<? extends GrantedAuthority> authorities = Authorities();
上⾯权限列表初始化由具体实现类实现:
public class User implements UserDetails, CredentialsContainer {
.
..
private final Set<GrantedAuthority> authorities;
...
//authorities权限列表
public User(String username, String password, Collection<? extends GrantedAuthority> authorities) {
this(username, password, true, true, true, true, authorities);
}
以上为个⼈经验,希望能给⼤家⼀个参考,也希望⼤家多多⽀持。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论