SpringBoot之SpringSecurity权限注解在方法上进行权限认证多种方式
前言
Spring Security
支持方法级别的权限控制。在此机制上,我们可以在任意层的任意方法上加入权限注解,加入注解的方法将自动被Spring Security
保护起来,仅仅允许特定的用户访问,从而还到权限控制的目的, 当然如果现有的权限注解不满足我们也可以自定义
快速开始
- 首先加入security依赖如下
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
2.接着新建安全配置类
Spring Security
默认是禁用注解的,要想开启注解,要在继承WebSecurityConfigurerAdapter
的类加@EnableMethodSecurity
注解,并在该类中将AuthenticationManager
定义为Bean。
@EnableWebSecurity
@Configuration
@EnableGlobalMethodSecurity(
prePostEnabled = true,
securedEnabled = true,
jsr250Enabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
我们看到@EnableGlobalMethodSecurity
分别有prePostEnabled 、securedEnabled、jsr250Enabled
三个字段,其中每个字段代码一种注解支持,默认为false,true为开启
。那么我们就一一来说一下这三总注解支持。
prePostEnabled = true 的作用的是启用Spring Security的@PreAuthorize 以及@PostAuthorize 注解。
securedEnabled = true 的作用是启用Spring Security的@Secured 注解。
jsr250Enabled = true 的作用是启用@RoleAllowed 注解
更详细使用整合请参考我这两篇
轻松上手SpringBoot+SpringSecurity+JWT实RESTfulAPI权限控制实战
Spring Security核心接口用户权限获取,鉴权流程执行原理
在方法上设置权限认证
JSR-250注解
遵守了JSR-250标准注解
主要注解
- @DenyAll
- @RolesAllowed
- @PermitAll
这里面@DenyAll
和 @PermitAll
相信就不用多说了 代表拒绝和通过。
@RolesAllowed
使用示例
@RolesAllowed("ROLE_VIEWER")
public String getUsername2() {
//...
}
@RolesAllowed({ "USER", "ADMIN" })
public boolean isValidUsername2(String username) {
//...
}
代表标注的方法只要具有USER, ADMIN任意一种权限就可以访问。这里可以省略前缀ROLE_,实际的权限可能是ROLE_ADMIN
在功能及使用方法上与 @Secured
完全相同
securedEnabled注解
主要注解
@Secured
Spring Security的
@Secured
注解。注解规定了访问访方法的角色列表,在列表中最少指定一种角色@Secured
在方法上指定安全性,要求 角色/权限等 只有对应 角色/权限 的用户才可以调用这些方法。 如果有人试图调用一个方法,但是不拥有所需的 角色/权限,那会将会拒绝访问将引发异常。
比如:
@Secured("ROLE_VIEWER")
public String getUsername() {
SecurityContext securityContext = SecurityContextHolder.getContext();
return securityContext.getAuthentication().getName();
}
@Secured({ "ROLE_DBA", "ROLE_ADMIN" })
public String getUsername2() {
//...
}
@Secured("ROLE_VIEWER")
表示只有拥有ROLE_VIEWER
角色的用户,才能够访问getUsername()
方法。
@Secured({ "ROLE_DBA", "ROLE_ADMIN" })
表示用户拥有"ROLE_DBA", "ROLE_ADMIN"
两个角色中的任意一个角色,均可访问 getUsername2
方法。
还有一点就是@Secured,不支持Spring EL表达式
prePostEnabled注解
这个开启后支持Spring EL表达式 算是蛮厉害的。如果没有访问方法的权限,会抛出AccessDeniedException。
主要注解
@PreAuthorize
--适合进入方法之前验证授权@PostAuthorize
--检查授权方法之后才被执行并且可以影响执行方法的返回值
3. @PostFilter
--在方法执行之后执行,而且这里可以调用方法的返回值,然后对返回值进行过滤或处理或修改并返回
@PreFilter
--在方法执行之前执行,而且这里可以调用方法的参数,然后对参数值进行过滤或处理或修改
@PreAuthorize注解使用
@PreAuthorize("hasRole('ROLE_VIEWER')")
public String getUsernameInUpperCase() {
return getUsername().toUpperCase();
}
@PreAuthorize("hasRole('ROLE_VIEWER')") 相当于@Secured(“ROLE_VIEWER”)。
同样的 @Secured({“ROLE_VIEWER”,”ROLE_EDITOR”})
也可以替换为:@PreAuthorize(“hasRole(‘ROLE_VIEWER') or hasRole(‘ROLE_EDITOR')”)
。
除此以外,我们还可以在方法的参数上使用表达式:
在方法执行之前执行,这里可以调用方法的参数,也可以得到参数值,这里利用JAVA8的参数名反射特性,如果没有JAVA8,那么也可以利用Spring Secuirty的@P标注参数,或利用Spring Data的@Param标注参数。
//无java8
@PreAuthorize("#userId == authentication.principal.userId or hasAuthority(‘ADMIN’)")
void changePassword(@P("userId") long userId ){}
//有java8
@PreAuthorize("#userId == authentication.principal.userId or hasAuthority(‘ADMIN’)")
void changePassword(long userId ){}
这里表示在changePassword
方法执行之前,判断方法参数userId的值是否等于principal中保存的当前用户的userId,或者当前用户是否具有ROLE_ADMIN权限,两种符合其一,就可以访问该 方法。
@PostAuthorize注解使用
在方法执行之后执行可,以获取到方法的返回值,并且可以根据该方法来决定最终的授权结果(是允许访问还是不允许访问):
@PostAuthorize
("returnObject.username == authentication.principal.nickName")
public CustomUser loadUserDetail(String username) {
return userRoleRepository.loadUserByUserName(username);
}
上述代码中,仅当loadUserDetail
方法的返回值中的username与当前登录用户的username相同时才被允许访问
注意如果EL为false,那么该方法也已经执行完了,可能会回滚。EL变量returnObject表示返回的对象。
@PreFilter以及@PostFilter注解使用
Spring Security提供了一个@PreFilter
注解来对传入的参数进行过滤:
@PreFilter("filterObject != authentication.principal.username")
public String joinUsernames(List<String> usernames) {
return usernames.stream().collect(Collectors.joining(";"));
}
当usernames中的子项与当前登录用户的用户名不同时,则保留;当usernames中的子项与当前登录用户的用户名相同时,则移除。比如当前使用用户的用户名为zhangsan,此时usernames的值为{"zhangsan", "lisi", "wangwu"}
,则经@PreFilter过滤后,实际传入的usernames的值为{"lisi", "wangwu"}
如果执行方法中包含有多个类型为Collection的参数,filterObject 就不太清楚是对哪个Collection参数进行过滤了。此时,便需要加入 filterTarget 属性来指定具体的参数名称:
@PreFilter
(value = "filterObject != authentication.principal.username",
filterTarget = "usernames")
public String joinUsernamesAndRoles(
List<String> usernames, List<String> roles) {
return usernames.stream().collect(Collectors.joining(";"))
+ ":" + roles.stream().collect(Collectors.joining(";"));
}
同样的我们还可以使用@PostFilter
注解来过返回
的Collection进行过滤:
@PostFilter("filterObject != authentication.principal.username")
public List<String> getAllUsernamesExceptCurrent() {
return userRoleRepository.getAllUsernames();
}
此时 filterObject 代表返回值。如果按照上述代码则实现了:移除掉返回值中与当前登录用户的用户名相同的子项。
自定义元注解
如果我们需要在多个方法中使用相同的安全注解,则可以通过创建元注解的方式来提升项目的可维护性。
比如创建以下元注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@PreAuthorize("hasRole('ROLE_VIEWER')")
public @interface IsViewer {
}
然后可以直接将该注解添加到对应的方法上:
@IsViewer
public String getUsername4() {
//...
}
在生产项目中,由于元注解分离了业务逻辑与安全框架,所以使用元注解是一个非常不错的选择。
类上使用安全注解
如果一个类中的所有的方法我们全部都是应用的同一个安全注解,那么此时则应该把安全注解提升到类的级别上:
@Service
@PreAuthorize("hasRole('ROLE_ADMIN')")
public class SystemService {
public String getSystemYear(){
//...
}
public String getSystemDate(){
//...
}
}
上述代码实现了:访问getSystemYear 以及getSystemDate 方法均需要ROLE_ADMIN权限。
方法上应用多个安全注解
在一个安全注解无法满足我们的需求时,还可以应用多个安全注解:
@PreAuthorize("#username == authentication.principal.username")
@PostAuthorize("returnObject.username == authentication.principal.nickName")
public CustomUser securedLoadUserDetail(String username) {
return userRoleRepository.loadUserByUserName(username);
}
此时Spring Security将在执行方法前执行@PreAuthorize的安全策略,在执行方法后执行@PostAuthorize的安全策略。
总结
在此结合我们的使用经验,给出以下两点提示:
默认情况下,在方法中使用安全注解是由Spring AOP代理实现的,这意味着:如果我们在方法1中去调用同类中的使用安全注解的方法2,则方法2上的安全注解将失效。
Spring Security上下文是线程绑定的,这意味着:安全上下文将不会传递给子线程。
public boolean isValidUsername4(String username) {
// 以下的方法将会跳过安全认证
this.getUsername();
return true;
}
SpringBoot之SpringSecurity权限注解在方法上进行权限认证多种方式的更多相关文章
- springBoot2.0+redis+fastJson+自定义注解实现方法上添加过期时间
springBoot2.0集成redis实例 一.首先引入项目依赖的maven jar包,主要包括 spring-boot-starter-data-redis包,这个再springBoot2.0之前 ...
- 【MyEcplise SVN】myEcplise上安装SVN的多种方式
第一种:SVN的在线安装 1.打开MyEclipse,找到顶部菜单栏 Help(帮助)-Install from Site-(从网站安装),如下图 2. 然后: 点击Install from Site ...
- WCF权限认证多种方式
WCF身份验证一般常见的方式有:自定义用户名及密码验证.X509证书验证.ASP.NET成员资格(membership)验证.SOAP Header验证.Windows集成验证.WCF身份验证服务(A ...
- spring security 在controller层 方法级别使用注解 @PreAuthorize("hasRole('ROLE_xxx')")设置权限拦截 ,无权限则返回403
1.前言 以前学习的时候使用权限的拦截,一般都是对路径进行拦截 ,要么用拦截器设置拦截信息,要么是在配置文件内设置拦截信息, spring security 支持使用注解的形式 ,写在方法和接口上拦截 ...
- SpringBootSecurity学习(06)网页版登录方法级别的权限
用户授权 前面讨论过,Web应用的安全管理,主要包括两个方面的内容,一个是用户身份的认证,即用户登录的设计,二是用户授权,即一个用户在一个应用系统中能够执行哪些操作的权限管理.前面介绍了登录,下面简单 ...
- SpringBoot学习之@Configuration注解和@Bean注解
@Configuration 1.@Configuration注解底层是含有@Component ,所以@Configuration 具有和 @Component 的作用. 2.@Configurat ...
- 【归纳】springboot中的IOC注解:注册bean和使用bean
目前了解的springboot中IOC注解主要分为两类: 1. 注册bean:@Component和@Repository.@Service.@Controller .@Configuration 共 ...
- springcloud项目实现自定义权限注解进行接口权限验证
一般在项目开发中会根据登录人员的权限大小对接口也会设置权限,那么对接口权限是怎么实现的呢,大多数都是用自定义权限注解,只需要在接口上加上一个注解就可以实现对接口的权限拦截,是否对该接口有权调用 接下来 ...
- springboot之使用@ConfigurationProperties注解
springboot之使用@ConfigurationProperties注解 代码已经上传至github https://github.com/gittorlight/springboot-exam ...
随机推荐
- 容器平台(kubernetes)架构设计
一眨眼距离上次发文好几年过去了,今天翻未读邮件看到博客有文章回复,猛然想起将博客遗忘在角落好几年了,赶紧访问博客.找回密码.翻翻文章,想写点什么但是又不知道从哪下手,N年前的第一篇文章是一个crm设计 ...
- 安全|常见的Web攻击手段之CSRF攻击
对于常规的Web攻击手段,如XSS.CRSF.SQL注入.(常规的不包括文件上传漏洞.DDoS攻击)等,防范措施相对来说比较容易,对症下药即可,比如XSS的防范需要转义掉输入的尖括号,防止CRSF攻击 ...
- 【小技巧】java的List分页
今天,工作上,由于业务的一些特殊性,需要拿到数据后在java代码中进行分页. 写了一个工具类,记录如下: import java.util.ArrayList; import java.util.Li ...
- xv6学习笔记(5) : 锁与管道与多cpu
xv6学习笔记(5) : 锁与管道与多cpu 1. xv6锁结构 1. xv6操作系统要求在内核临界区操作时中断必须关闭. 如果此时中断开启,那么可能会出现以下死锁情况: 进程A在内核态运行并拿下了p ...
- docker容器 如何精简镜像减小体积
写在前面 我们在上篇<Docker容器 关于镜像构建的安全问题>一起学习了如何构建一个基于安全的镜像,这篇小作文我们会学习镜像构建的另一个关键性问题,为何别人打造的镜像只有10MB而我的有 ...
- 并发编程之:Atomic
大家好,我是小黑,一个在互联网苟且偷生的农民工. 在开始讲今天的内容之前,先问一个问题,使用int类型做加减操作是不是线程安全的呢?比如 i++ ,++i,i=i+1这样的操作在并发情况下是否会有问题 ...
- 实例说明C++的virtual function的作用以及内部工作机制初探
C++为何要引入virtual function? 来看一个基类的实现: 1 class CBase 2 { 3 public: 4 CBase(int id) : m_nId(id), m_pBas ...
- 巧用模糊实现视觉的 3D 效果
本文较短,将介绍巧用模糊实现视觉 3D 效果的技巧. 我们都知道,在正常的视觉效果中,离我们越近的通常我们会看的越清晰,而离我们较远则相对没那么清晰~ 我们可以利用清晰与模糊两种状态来构建视差效果.像 ...
- vue系统总结2
注册组件 组件其他补充 组件数据存放 父子组件通信 父级向子级传递信息 子级向父级传递信息 插槽slot 1.1什么是组件化 1.2 注册组件的基本步骤 创建组件构造器 注册组件 使用组件 <d ...
- JavaScript高级程序设计读书笔记之JSON
JSON(JavaScript Object Notation)JavaScript对象表示法.JSON是JavaScript的一个严格的子集,利用了JavaScript中的一些模式来表示结构化数据. ...