前言
上次《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用户登陆可以修改和删除所有数据。这部分测试内容,不再演示,读者可以自行测试。
转载请注明处在:
相关推荐
1. GroupSearchFilter:Spring Security LDAP提供了GroupSearchFilter,可以在认证成功后搜索用户所属的组信息,实现基于组的角色分配和权限控制。 2. AuthoritiesPopulator:此接口用于将从LDAP获取的用户角色转换...
本实战代码将带你深入理解并实际操作SpringSecurity的核心功能,包括用户认证与授权、注销、权限限制、"记住我"功能以及首页定制。 1. 用户认证与授权:在SpringSecurity中,认证过程是识别用户身份,而授权则是...
通过这个Spring Security实战例子,你可以深入了解Spring Security的配置、认证和授权机制,以及如何与数据库集成。实践是最好的老师,动手完成这四个小项目将有助于巩固理解,并为你在实际项目中应用Spring ...
《Spring Security与JWT整合实战详解》 在现代Web应用程序中,安全性和用户认证是至关重要的。Spring Security作为Java领域中最受欢迎的安全框架,为开发者提供了强大的安全解决方案。而JSON Web Token(JWT)则是...
1. 认证流程:Spring Security的认证过程包括了认证请求、凭证匹配、权限检查等步骤。当用户尝试访问受保护的资源时,Spring Security会启动认证过程,判断用户是否具有合法的身份。 2. 用户详情服务...
二、Spring Security架构 Spring Security的架构主要由以下几部分组成: 1. **Filter Chain**:Spring Security通过一系列Filter来处理HTTP请求,每个Filter执行特定的安全任务,如身份验证、会话管理等。 2. **...
在实际项目中,Spring Security可以用于保护RESTful API、实现单点登录(SSO)、保护静态资源、控制对Controller方法的访问等。通过配置不同的过滤器、提供自定义访问决策策略,开发者能够精确控制用户的访问权限。 ...
《Spring Cloud Security实战详解》 在微服务架构中,安全问题尤为重要,而Spring Cloud Security作为Spring Cloud生态的一部分,为开发者提供了强大的安全管理和身份验证功能。本文将深入探讨Spring Cloud ...
Spring Security是Java平台上广泛使用的安全框架,其3.1.0.RELEASE版本是该框架的一个重要里程碑,为开发者提供了全面的安全控制,从Web应用到企业级服务,涵盖了认证、授权和会话管理等多个层面。本文将深入探讨...
这三份资料——"实战Spring Security 3.x.pdf"、"Spring Security 3.pdf" 和 "Spring Security使用手册.pdf" 将深入探讨这些概念,并提供实践指导,帮助读者掌握如何在实际项目中应用Spring Security。通过学习这些...
- **springsecurity-sample**:这个示例项目可能包含了从创建用户、角色到实现登录、权限控制的完整流程。你可以通过它学习如何配置Spring Security XML或Java配置,如何编写自定义的认证和授权逻辑,以及如何在...
**二、Spring Security与LDAP集成** 1. **配置LDAP连接**:首先,我们需要在Spring Security的配置类中添加LDAP服务器的连接信息,包括URL、基础DN(Distinguished Name)以及管理员凭证。 2. **定义LDAP认证提供者*...
2. **授权机制**:通过Access Decision Manager和Access Decision Voter,Spring Security支持细粒度的访问控制,如基于角色的访问控制(RBAC)和基于权限的访问控制(ABAC)。此外,它可以与Spring AOP结合,实现...
这个"spring-security-sample"案例代码提供了一个实战教程,帮助开发者理解和应用Spring Security的核心概念。在这个项目中,我们将深入探讨Spring Security的基本配置、认证流程以及授权机制。 首先,Spring ...
7. **实战应用**:对于初学者,可以首先创建一个简单的登录页面,然后配置SpringSecurity来处理登录请求。接着,定义角色和权限,将它们分配给用户。最后,通过注解或配置文件设定哪些URL需要特定角色才能访问。 综...
Spring Security 是Spring生态系统中的一个组件,用于提供认证(Authentication)和授权(Authorization)功能。它为Web应用程序提供了全面的安全解决方案,包括登录、权限控制、会话管理、CSRF防护等。 2. **核心...
### 第二章:Spring Security起步 这一章重点介绍了如何快速集成Spring Security到现有的项目中。 - **安全的核心概念**:深入浅出地解释了安全领域的重要概念,如认证、授权等。 - **实现Spring Security的XML...
Spring Security 是一个强大的安全框架,用于为 Java 应用程序提供认证、授权和安全防护功能。这个实战项目针对初学者,旨在帮助理解 Spring Security 的基本概念和配置过程。在这个小型的示例项目中,我们将探讨...
本课程"Spring Security+OAuth2 精讲,打造企业级认证与授权"深入浅出地讲解了这两个框架的使用和集成,旨在帮助开发者构建安全、高效的应用系统。 Spring Security是Spring生态系统中的一个强大安全框架,它提供了...
### Spring Security 全套入门到项目实战课程知识点详解 #### 一、Spring Security 概述 **1.1 Spring Security 介绍** - **定义**: Spring Security 是一款基于 Spring 框架的身份认证(Authentication)与用户...