前言
上次《Spring Security实战(一)-- 基于数据库认证》讲到使用Spring Security如何实现对web层的安全认证,Spring Security还可以实现对方法级别的权限控制。对方法的权限保护主要有两个应用场景:
1、由于开发疏忽导致web层的安全认证有漏洞,从而绕过了web层的安全认证。对重要的方法再进行一次权限控制,可以避免此类安全问题。
2、假设A、B两个用户都是普通用户,都有修改和删除自己数据的权限,但不能修改和删除对方的数据,web层的安全认证只是正对链接规则 很难做到如此细粒度的权限控制,但方法级别的权限认证可以实现该功能。
本次总结,首先对Spring Security如何实现方法级别的权限控制进行讲解;然后以一个用户管理页面权限控制为例,讲解如何使用页面权限结合方法验证进行细粒度的权限控制。
Spring Security方法权权限认证
已经在web层引入了Spring Security的情况下,要在方法上使用权限控制很简单,在上一篇demo xml配置文件中,添加相应配置启动“注解保护方法”即可,Spring Security支持三种不同的安全注解:
1、Spring Security自带的@Secured
2、JSR-250的@RolesAllowed
3、4个表达式驱动注解:@PreAuthorize、@PostAuthorize、@PreFilter、@PostFilter
@Secured和@RolesAllowed
其中@Secured和@RolesAllowed注解的作用相同,主要实现根据用户的角色权限判断是否有权限方法该方法,只是一个是SpringSecurity自己的规范,一个是java标准规范。
@Secured的使用
由于@Secured和@RolesAllowed注解的作用相同,这里以启用@Secured注解进行讲解,要启用@Secured注解可以在xml配置文件中添加如下配置:
<!--<security:global-method-security jsr250-annotations="enabled"/>--> <security:global-method-security secured-annotations="enabled"/>
然后在需要做权限控制的方法上添加@Secured注解即可:
@Secured({"ROLE_USER","ROLE_ADMIN"}) public void userPermit() { System.out.println("允许普通角色和管理员角色访问"); } @Secured("ROLE_ADMIN") public void adminPermit() { System.out.println("允许管理员角色访问"); }
@Secured("ROLE_ADMIN")等价于表达式@Secured("hasRole('ROLE_USER')"),Spring Security还提供了类似的其他内置表达式:
这张来自官网的表很关键,通过上述表达式可以覆盖我们遇到的大部分权限认证场景。其中hasPermission()表达式为扩展表达,用户可以自己实现PermissionEvaluator接口很方便的完成自定义扩展。
4个表达式驱动注解
@PreAuthorize、@PostAuthorize、@PreFilter、@PostFilter 4个驱动表达式注解使用SpEl表达式,可以是实现更细粒度的表达式约束。其中前两者可以用来在方法调用前或者调用后进行权限检查,后两者可以用来对集合类型的参数或者返回值进行过滤。要使它们的定义能够对我们的方法的调用产生影响我们需要设置global-method-security元素的pre-post-annotations=”enabled”,默认为disabled。
<!—开启@Secured注解,同时开启4个表达式驱动注解--> <security:global-method-security secured-annotations="enabled" pre-post-annotations="enabled"/>
在需要做权限认证的方法上添加注解:
/** * 普通管理员 只能获取自己的店铺列表 * @param username * @return */ @Override @PreAuthorize("principal.username.equals(#username)") public List<ShopData> select(String username) { List<ShopData> ret = new ArrayList<>(); for(ShopData temp:shops){ if(temp.getUsername().equals(username)){ ret.add(temp); } } return ret; }
上述注解,可以隔离不同普通用户之间的数据 彼此不可见。
再来看一个稍微复杂点的例子:
@PreAuthorize("hasAnyRole('ROLE_USER','ROLE_ADMIN')") @PreFilter("hasRole('ROLE_ADMIN') || filterObject.shopdata.username == principal.username") public void deletexx(List<ShopData> list) { //省略代码 }
@PreAuthorize("hasAnyRole('ROLE_USER','ROLE_ADMIN')")表示在方法调用前进行权限认证,具备'ROLE_USER','ROLE_ADMIN'角色的用户可以访问该方法。
@PreFilter("hasRole('ROLE_ADMIN') || filterObject.shopdata.username == principal.username")含义为:如果是超级管理员可以删除list中的所有数据,如果是普通管理员,需要对list进行过滤 只能删除自己的数据。
hasPermission表达式自定义权限
有时候通过上述常规表示式无法完成一些复杂的权限认证,比如:根据id删除数据,判断如果是超级管理员可以直接删除,如果是普通管理原型 需要根据id先查询数据,再判断数据创建者是否是当前用户。这时只能通过hasPermission表达式实现,要使用hasPermission表达式必须实现自己的PermissionEvaluator接口:
@Component("myPermissionEvaluator") public class MyPermissionEvaluator implements PermissionEvaluator { private static final GrantedAuthority ADMIN = new SimpleGrantedAuthority("ROLE_ADMIN"); @Resource private ShopDataHelper shopDataHelper; @Override public boolean hasPermission(Authentication authentication, Object o, Object o1) { if(o instanceof Integer){ //管理员拥有所有权限 if(isAdmin(authentication)){ return true; } Integer id = (Integer) o; ShopData shopData = shopDataHelper.selectById(id); String username = shopData.getUsername(); //判断普通管理员是否有删除、修改权限 if("delete".equals(o1) || "update".equals(o1)){ if(authentication.getName().equals(username)){ return true; }else{ return false; } } } throw new UnsupportedOperationException("hasPermission 第一个参数必须是Integer型,第二个参数必须为delete或者update"); } /** * 暂不实现 */ @Override public boolean hasPermission(Authentication authentication, Serializable serializable, String s, Object o) { throw new UnsupportedOperationException(); } private boolean isAdmin(Authentication authentication){ return authentication.getAuthorities().contains(ADMIN); } }
该接口有两个主要方法:
boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission); boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission);
这里MyPermissionEvaluator只实现了第一个方法,在使用hasPermission表达式时,不用传Authentication参数,默认会自动带上:
@PreAuthorize("hasPermission(#id,'update')") public void update(Integer id) { for(ShopData temp:shops){ if(temp.getId()==id){ if(temp.isState()){ temp.setState(false); }else{ temp.setState(true); } break; } } }
@PreAuthorize("hasPermission(#id,'update')")权限控制为:如果是超级管理员可以直接删除数据,如果是普通管理员首先通过id查询得到该id对应的店铺信息,然后判断该店铺的创建者是否是当前用户,如果是才允许删除操作。这部分逻辑被封装到MyPermissionEvaluator中。
有人会说,不使用hasPermission表达式直接在update方法中做这部分校验操作也可以,实际工作中有很多系统也是这样做的。但是采用hasPermission表达式可以把业务逻辑代码与权限控制代码完全隔离,而且复用性更好,其他有类似权限控制的地方直接使用这个hasPermission表达式即可。
Demo演示
这里笔者根据上述讲解写了一份demo演示代码,github地址为:https://github.com/gantianxing/spring-security1.git。在运行代码之前,首先执行下列sql语句:
-- ---------------------------- -- Table structure for `authorities` -- ---------------------------- DROP TABLE IF EXISTS `authorities`; CREATE TABLE `authorities` ( `username` varchar(50) COLLATE utf8_bin NOT NULL, `authority` varchar(50) COLLATE utf8_bin NOT NULL, UNIQUE KEY `ix_auth_username` (`username`,`authority`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin; -- ---------------------------- -- Records of authorities -- ---------------------------- INSERT INTO `authorities` VALUES ('A', 'ROLE_ADMIN'); INSERT INTO `authorities` VALUES ('A', 'ROLE_USER'); INSERT INTO `authorities` VALUES ('B', 'ROLE_USER'); INSERT INTO `authorities` VALUES ('C', 'ROLE_USER'); -- ---------------------------- -- Table structure for `users` -- ---------------------------- DROP TABLE IF EXISTS `users`; CREATE TABLE `users` ( `username` varchar(50) COLLATE utf8_bin NOT NULL, `password` varchar(50) COLLATE utf8_bin NOT NULL, `enabled` tinyint(1) NOT NULL, PRIMARY KEY (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin; -- ---------------------------- -- Records of users -- ---------------------------- INSERT INTO `users` VALUES ('A', '123456', '1'); INSERT INTO `users` VALUES ('B', '123456', '1'); INSERT INTO `users` VALUES ('C', '123456', '1');
上述sql语句创建了三个用户A、B、C,其中A用户是超级管理员具备:ROLE_USER、ROLE_ADMIN角色;B、C用户都是普通管理员,只具备ROLE_USER角色。
程序启动后,会在内存里初始化3个店铺信息数据(ShopData),创建者分别为A、B、C。访问登陆页http://localhost/login,首先使用A账号登陆:
再访问店铺列表页,由于A账号是超级管理员,可以看到所有的三条数据:
如果切换成B用户登陆,再次访问店铺列表页,只能看到B用户创建的店铺信息:
访问http://localhost/shop/add,可以在当前用户下创建一个新店铺(再次访问店铺列表页):
访问http://localhost/shop/update?id=2,可以修改店铺上下线状态,再次访问店铺列表页:
如果访问http://localhost/shop/update?id=1,由于当前登录用户是B,但id=1的店铺是A用户创建的,所以没有权限,执行结果为:
同理还可以执行http://localhost/shop/delete?id=2,对B用户自己的数据进行删除,但缺无法删除别人的数据,如果是A用户登陆可以修改和删除所有数据。这部分测试内容,不再演示,读者可以自行测试。
转载请注明处在:
相关推荐
SpringSecurity实战代码,实现用户认证与授权、注销、权限限制、记住我、首页定制
Spring Security实战例子资源里面有4个小项目,都是利用maven搭建的。数据库要建立一个security的表
Spring Security权限控制实现 Spring Security与Web应用安全 Spring Security用户认证流程 Spring Security用户授权管理 Spring Security与Spring集成 Spring Security最佳实践 Spring Security安全漏洞与防范
刚来公司的时候的入职培训实战项目以及现在正在做的项目都用到了 Spring Security 这个强大的安全验证框架,可以看出这个框架在身份验证以及权限验证领域可以说应该是比较不错的选择。由于之前经历项目的这部分模块...
该项目包含helloworld(快速入门)、web(ssh项目快速搭建)、aop(切面编程)、data-redis(redis缓存)、quartz(集群任务实现)、shiro(权限管理)、oauth2(四种认证模式)、shign(接口参数防篡改重放)、encoder(用户...
本篇文章主要介绍了java中自定义Spring Security权限控制管理示例(实战篇) ,具有一定的参考价值,感兴趣的小伙伴们可以参考一下。
无状态统一权限认证的解决方案,实现了异常和日志的统一管理,实现了MQ落地保证100%到达的解决方案。 核心框架:springcloud Edgware全家桶 安全框架:Spring Security Spring Cloud Oauth2 分布式任务调度:...
用户管理模块基于Spring Security的用户登录、退出操作,以及用户查询、添加、详情等操作;角色管理模块,通过权限关联与控制给用户赋予角色(管理员角色和普通用户角色);资源权限管理模块给不同的角色关联不同的...
这是一款基于SpringBoot+SpringSecurity的RBAC权限管理系统。原本只想着做成基于SpringSecurity的权限管理系统,但随着功能的增加感觉有些刹不住车了,之后可能会往后台管理系统方向发展。无任何重度依赖,非常适合...
11-3,11-4 -Spring Security 实战-前后台编码 第12章 博客系统的整体框架实现 12-1 -整体的需求回顾 12-2 -后台整体控制层、API 实现 12-3 -前台整体布局实现 12-4 -API 讲解 第13章 博客系统的用户管理实现...
报表后端采用技术: SpringBoot整合SSM(Spring+Mybatis-plus+ SpringMvc),spring security 全注解式的权限管理和JWT方式禁用Session,采用redis存储token及权限信息 报表前端采用Bootstrap框架,结合Jquery Ajax,...
springboot3.x springsecurity6.x 实战教程,可用于生产项目工程 本地账号、手机号、邮箱多账号登录,账号与用户信息分离表结构设计,区别于常见表设计方法
架构技术: struts2+spring3+hibernate4 + UI 组件(easyui)+Spring_security权限 配置思想: Convention 零配置(不需要任何配置文件) 优点; [1].代码生成器(单表的增删改查完美生成) 注意:包括JSP页面的生成...
SpringSecurity5+Element?UI+Vue?Admin?Template+蚂蚁可视化AntV?等技术栈开发的项目,采用分布式,多模块,前后端分离开发。包括图形展示、权限管理、用户管理等功能。【后端技术】技术说明Spring?Boot2MVC框架?...
layui的table数据表格的用法可以在layui官网上找到示例,我这里对于前端部分就不详细解释了,因为前端我也不咋会,都是根据别人的代码改了改。 我直接贴上完整的power.html完整代码
springboot+security+jwt+redis 实现微信小程序登录及token权限鉴定 tips:这是实战篇,默认各位看官具备相应的基础(文中使用了Lombok插件,如果使用源码请先安装插件) @[TOC] 项目配置 依赖 <groupId>org.spring...
1、通过spring security实现的RBAC权限的模型基础上实现权限、角色、资源的管理,实现根据数据库动态分配权限的功能,对未登录及未授权的操作进行拦截。在用户管理中心,管理员可添加用户代替了自行注册方式。 2、...
全网最新的Cloud 权限系统 基于Spring Boot 2.0.4.RELEASE ...基于 Spring Security oAuth 深度定制,支持社交登录等 完整的OAuth 2.0 流程,资源服务器控制权限 去除了部分对于开发不友好的中间件,快速上手
Spring Cloud 实战项目 项目介绍 功能点: 模拟商城,完整的购物流程、后端运营平台对前端业务的支撑,和对项目的运维,有各项的监控指标和运维指标。 技术点: 核心技术为springcloud+vue两个全家桶实现,采取了...
job(分布式定时任务)、swagger(API接口管理测试)、security(基于RBAC的动态权限认证)、SpringSession(Session共享)、Zookeeper(结合AOP实现分布式锁)、RabbitMQ(消息队列)、Kafka(消息队列)、websocket(服务端推送...