Shiro权限控制+整合shiro
Shiro权限控制
0.1传统的权限认证方式
特点:为每个人单独的分配权限模块,能够实现权限控制,但是当公司人员庞大之后,非常难管理
上述权限控制如何设计表?
关系:员工和菜单权限的关系:多对多
员工id | 菜单名称 |
---|---|
1 | 取派管理 |
2 | 快递员管理 |
2 | 运单管理 |
好处:可以方便的 实现权限控制
缺陷:比如当修改权限的时候,公司统一的给组长级别的人 加一个“计算工资”权限,这时候,得修改权限表中所有组长的权限,每个组长在数据库中都得增加一条“计算工资”记录的权限
后来,这个“计算工资”的功能,在给组长之后,发现,这个权限不合适,得收回这个权限,这个时候,需要删除多条记录
0.2 RBAC认证方式:
Role Based Access Controller :基于角色的访问控制
前无古人后无来者
0.3 RBAC认证方式下的数据库设计
数据库设计:
1. 权限控制
1.1 概述
1.1.1 什么是认证和授权
- 认证和授权,控制项目资源的访问。
- 认证:先进行认证。例如:是否是QQ会员-----在程序中指的就是:登录
- 授权:再进行授权。例如:QQ会员级别(级别不同权限不同)-------对菜单的访问控制
1.1.2 权限控制的解决方案
- 方式1:自定义实现
- 自己如何实现一个认证?写一个Filter过滤器
- 方式2:采用框架,例如:shiro、Spring Security 等
- Shiro:轻量、简单,apache
- Spring Security:轻量、简单,Spring
1.1.3 Shiro概述
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
官网:http://shiro.apache.org/
Authentication:身份认证/登录,验证用户是不是拥有相应的身份;
Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;
Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境的,也可以是如Web环境的;
Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
Web Support:Web支持,可以非常容易的集成到Web环境;
Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率;
Concurrency:shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;
Testing:提供测试支持;
Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。
1.1.4 三个核心组件
- 三个核心组件:Subject, SecurityManager 和 Realms.
- Subject,主体,即“当前操作用户”。Subject代表了当前用户的安全操作。(需要被认证的对象)
- SecurityManager:它是Shiro框架的核心,管理所有用户的安全操作,并提供安全管理的各种服务。
- Realm: Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。
1.1.5 传统的登录和shiro登录的比较
shiro的使用不依赖Spring框架,javase可以用,javaee也可以用,移动应用程序可以用,大型的网络和企业应用程序也可以使用Shiro。
传统登录方式:
Shiro安全框架实现登录
传统的方式,客户端发出请求给Controller,Controller接受用户的用户名和密码,然后由Controller调用业务逻辑,在Controller中调用Service,然后service负责调用数据库进行处理,如果用户名和密码正确,则将用户信息保存到session中,并且进入主页面
如果用户名和密码错误,则保存错误信息,然后将信息输出到客户端。
答:客户端发送请求到Controller,Controller接受用户的用户名和密码,第一步还是一样的,但是第二步不一样了,以前第二步是Controller直接调用Service处理,现在Controller调用Shiro安全框架去处理,也就是说将认证授权抽取出来,有一个框架专门为你做认证做授权,这里有框架去帮我们完成认证和授权,然后告诉你这个用户名和密码是否可用还是不可用,当然此时,认证成功之后,只要从shiro中取出认证的结果,如果成功的话,将用户保存至session中,然后在跳转页面
这个过程相比于早期的操作,相当于验证用户名和密码的业务逻辑交给shiro安全框架来做。并且加密也交给shiro安全框架来做,然后由shiro安全框架来加密,由shiro拿密文与数据库中的密文进行比较。
总结一下:shiro就是一个安全框架,帮助我们解决认证、授权、加密和密码比较的过程。
1.2 整合shiro
1.2.1 maven坐标
步骤1:在common-parent项目中,添加坐标
<!--shiro start--><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-ehcache</artifactId><version>1.3.2</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.3.2</version></dependency><!--shiro end-->
1.2.2 配置过滤器
使用Shiro时,需要配置的相关权限过滤器如下(共10个):
anon: 匿名过滤器,未登陆也可以访问
authc: 认证过滤器, 登陆后访问
perms : 需要xx权限,才能访问
roles: 需要xx角色,才能访问
user: 需要xx用户,才能访问
port:指定端口才能访问
ssl:必须使用https协议才能访问
logout :登出功能
rest :根据指定HTTP请求访问才能访问 ,get方式提交 或者 post方式提交才能访问
可
1.2.3 配置config类
shiro的配置步骤
1 配置安全管理器SecurityManager2 realm域配置:由于SecurityManger需要使用realm域,涉及到用户信息、权限信息,处理用户信息的时候需要加密 3 密码比较器:用户输入的铭文进行加密,并且与数据库中的密文进行比较 4 配置生成过滤器的工厂类
/*** 在ShiroConfig中做什么事情呢?* 1 配置shiro安全管理器,向安全管理器中注入Realm域* 2 配置Realm域:注入密码比较器* 3 配置密码比较器* 4 配置拦截路径和放行路径*/
@Configuration
public class ShiroConfig {/*** 配置安全管理器,并且注入Realm域* @param realm* @return*/@Beanpublic SecurityManager securityManager(Realm realm){DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(realm);return securityManager;}/*** Credentials:凭证/证书 ---** 配置Realm域,注入密码比较器* @param credentialsMatcher* @return*/@Beanpublic BosRealm realm(CredentialsMatcher credentialsMatcher){BosRealm bosRealm = new BosRealm();bosRealm.setCredentialsMatcher(credentialsMatcher);return bosRealm;}/*** 密码比较器** @return*/@Beanpublic CredentialsMatcher credentialsMatcher(){// return new HashedCredentialsMatcher("MD5");return new BosCredentialsMatcher();}/*** 配置拦截路径和放行路径* @param securityManager* @return*/@Beanpublic ShiroFilterFactoryBean shirFilter(SecurityManager securityManager){System.out.println("ShiroConfiguration.shirFilter()");// shiro过滤器工厂类ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();// 必须设置 SecurityManagershiroFilterFactoryBean.setSecurityManager(securityManager);//拦截器----Map集合Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();//配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了filterChainDefinitionMap.put("/login*", "anon");filterChainDefinitionMap.put("/user/login*", "anon");filterChainDefinitionMap.put("/validatecode.jsp*", "anon");filterChainDefinitionMap.put("/css/**", "anon");filterChainDefinitionMap.put("/js/**", "anon");filterChainDefinitionMap.put("/images/**", "anon");filterChainDefinitionMap.put("/data/**", "anon");// /** 匹配所有的路径// 通过Map集合组成了一个拦截器链 ,自顶向下过滤,一旦匹配,则不再执行下面的过滤// 如果下面的定义与上面冲突,那按照了谁先定义谁说了算// /** 一定要配置在最后filterChainDefinitionMap.put("/**", "authc");// 将拦截器链设置到shiro中shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面shiroFilterFactoryBean.setLoginUrl("/login.html");// 登录成功后要跳转的链接shiroFilterFactoryBean.setSuccessUrl("/index.html");//未授权界面;shiroFilterFactoryBean.setUnauthorizedUrl("/403");return shiroFilterFactoryBean;}/*** 开启shiro aop注解支持* 使用代理方式;所以需要开启代码支持* @param securityManager* @return*/@Beanpublic AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);return authorizationAttributeSourceAdvisor;}/*** 开启cglib代理* @return*/@Beanpublic DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();creator.setProxyTargetClass(true);return creator;}}
根据配置文件报错显示,需要创建如下文件:
1 Realm域
2 CredentialsMatcher密码比较器
1.2.4 创建类Realm类
//自定义Realm ,实现安全数据 连接
public class BosRealm extends AuthorizingRealm {// 认证...@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) {System.out.println("shiro 认证管理... ");return null;}@Override// 授权...protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection pc) {System.out.println("shiro 授权管理...");return null;}
}
1.2.5 编写密码比较器
public class BosCredentialsMatcher extends SimpleCredentialsMatcher {@Overridepublic boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {System.out.println("密码比较器");return false;}
}
1.2.6 实现认证方法
//自定义Realm ,实现安全数据 连接
public class BosRealm extends AuthorizingRealm {@Autowiredprivate UserService userService;// 认证...@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) {System.out.println("shiro 认证管理... ");//1 用户信息UsernamePasswordToken upToken = (UsernamePasswordToken) token;//2 通过username从数据库中查找 User对象,如果找到,没找到.User user = userService.findUserByUsername(upToken.getUsername());if(user == null){//返回null表示账号不存在return null;}return new SimpleAuthenticationInfo(user, user.getPassword(), getName());}@Override// 授权...protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection pc) {System.out.println("shiro 授权管理...");return null;}
}
1.2.7编写加密工具类
public class Encrypt {/** 散列算法一般用于生成数据的摘要信息,是一种不可逆的算法,一般适合存储密码之类的数据,* 常见的散列算法如MD5、SHA等。一般进行散列时最好提供一个salt(盐),比如加密密码“admin”,* 产生的散列值是“21232f297a57a5a743894a0e4a801fc3”,* 可以到一些md5解密网站很容易的通过散列值得到密码“admin”,* 即如果直接对密码进行散列相对来说破解更容易,此时我们可以加一些只有系统知道的干扰数据,* 如用户名和ID(即盐);这样散列的对象是“密码+用户名+ID”,这样生成的散列值相对来说更难破解。*///高强度加密算法,不可逆public static String md5(String password, String salt){return new Md5Hash(password,salt,2).toString();}public static void main(String[] args) {/*** new Md5Hash("123456","lisi",1) :1b539b60601b934441308049a9526e7d* new Md5Hash("123456","lisi",2) :42bd4e7685cb11d3ba02716c313cb04b* new Md5Hash("123456","lisi",3) :16f807d62105b4896034552ee5caeb8a* new Md5Hash("123456","KMNO4",3):8bd35dc14dc07f756478bb44513694f6*///System.out.println(new Md5Hash("123456","KMNO4",3).toString());/*** sha家族加密算法* sha1:aca1eb31d2dcf8f1fcf3fd7a7104232785afad41 40 位* sha256: 616a47d8e1e42f23693bb3a85749bf18d4b6e5380ddfd5717aafa61e33d5211e* sha384:84f5cbb18e2d9f1c81b8cec6f443a2b229993689a2ebae97db37e13af1dfb00ec6168713a53fe19d33a63d4d30889553* sha512:c3e5102b6a7ec6caa5b255dae2895b11c2ef0c7b9bfea8e848653372b53f3ef665d96ea283a21eac683cc0fe5c4b1f64692c2056a8a9636ee1931151043d2b5d*/System.out.println("sha1:"+new Sha1Hash("123456","lisi",2));System.out.println("sha256:"+new Sha256Hash("123456","lisi",2));System.out.println("sha384:"+new Sha384Hash("123456","lisi",2));System.out.println("sha512:"+new Sha512Hash("123456","lisi",2));}
}
1.2.8 编写密码比较器
public class BosCredentialsMatcher extends SimpleCredentialsMatcher {@Overridepublic boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {//向下转型UsernamePasswordToken upToken = (UsernamePasswordToken)token;//获取用户页面输入的密码String pwd = new String(upToken.getPassword());//加密String newPwd =Encrypt.md5(pwd, upToken.getUsername()).toString();//获取数据库密码String dbPwd = info.getCredentials().toString();return equals(newPwd, dbPwd);}
}
5 散列加密算法:加密的时候撒盐
MD5:
SHA:
1.5.6 修改BosRealm
//自定义Realm ,实现安全数据 连接
public class BosRealm extends AuthorizingRealm {@Resourceprivate UserService userService;@Resourceprivate RoleService roleService;@Resourceprivate PermissionService permissionService;@Overridepublic String getName() {return "bosRealm";}@Override// 认证...protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {System.out.println("shiro 认证管理... ");//1 获得密码String username = (String)token.getPrincipal();//2 通过username从数据库中查找 User对象,如果找到,没找到.User user = userService.findUserByUsername(username);;if(user == null){//返回null表示账号不存在return null;}return new SimpleAuthenticationInfo(user, user.getPassword(), getName());}@Override// 授权...protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection pc) {System.out.println("shiro 授权管理...");SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();// 根据当前登录用户 查询对应角色和权限
// User user = (User) SecurityUtils.getSubject().getPrincipal();User user = (User) pc.getPrimaryPrincipal();// 调用业务层,查询角色List<Role> roles = roleService.findByUser(user);for (Role role : roles) {authorizationInfo.addRole(role.getKeyword());}// 调用业务层,查询权限List<Permission> permissions = permissionService.findByUser(user);for (Permission permission : permissions) {authorizationInfo.addStringPermission(permission.getKeyword());}return authorizationInfo;}
}
1.5.7 确定权限不足时,显示未授权页面
- 确定权限过滤器配置信息
- 确定页面
//拦截器.
Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();//配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
filterChainDefinitionMap.put("/login*", "anon");
filterChainDefinitionMap.put("/validatecode.jsp*", "anon");
filterChainDefinitionMap.put("/user/login*", "anon");
filterChainDefinitionMap.put("/css/**", "anon");
filterChainDefinitionMap.put("/js/**", "anon");
filterChainDefinitionMap.put("/images/**", "anon");
filterChainDefinitionMap.put("/services/**", "anon");
filterChainDefinitionMap.put("/pages/base/courier**", "perms[courier:list]");
filterChainDefinitionMap.put("/pages/base/area**", "roles[base]");
//<!-- 过滤链定义,从上向下顺序执行,一般将 /**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;
//<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
filterChainDefinitionMap.put("/**", "authc");// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
shiroFilterFactoryBean.setLoginUrl("/login.html");
// 登录成功后要跳转的链接
shiroFilterFactoryBean.setSuccessUrl("/index.html");
//未授权界面;
shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized.html");
1.5.9 细粒度方法权限控制
需求:张三可以查看快递员列表信息,但是无法添加快递员
Shiro提供了若干注解用于在方法上进行权限控制
注解 | 描述 |
---|---|
@RequiresPermissions() | 用户必须具有指定【权限】,才可以访问被注解修饰的方法。 |
@RequiresRoles() | 用户必须具有指定【角色】,才可以访问被注解修饰的方法。 |
- 步骤1:在授权方法中获取该用户的所有的权限:包括role权限和Permission权限
- 步骤2:在运单 CourierController的添加方法上添加courier:list权限标识。
测试:无权限访问
1.6 动态菜单
动态菜单: 不同用户登录后,应该看到不同菜单结构
1、 修改index.html 加载基本菜单 url路径
// 基本功能菜单加载
$.get("/menu/showMenu",function(data){$.fn.zTree.init($("#treeMenu"), setting, data);
},"json");
2、 在MenuController 添加 showMenu方法
@RestController
@RequestMapping("/menu")
public class MenuController {@Autowiredprivate MenuService menuService;// 加载左侧的菜单功能@GetMapping(value = "/showMenu")public ResponseEntity<List<Menu>> showMenu(){// 调用业务层,查询当前用户具有菜单列表Subject subject = SecurityUtils.getSubject();User user = (User)subject.getPrincipal();// 查询菜单列表List<Menu> result = menuService.findByUser(user);return new ResponseEntity<List<Menu>>(result,HttpStatus.OK);}
}
3、 编写MenuService.java业务层
@Service
@Transactional
public class MenuService {@Autowiredprivate MenuMapper menuMapper;/**查询用户*/public List<Menu> findByUser(User user) {// 针对admin用户显示所有的菜单if(user.getUsername().equals("admin")){return menuMapper.selectAll();}else{// 使用用户ID,查询当前用户具有的菜单列表return menuMapper.findByUser(user.getId());}}
}
4、调用DAO
@org.apache.ibatis.annotations.Mapper
public interface MenuMapper extends Mapper<Menu> {@Select("select m.* from t_menu m,t_user u,t_user_role ur,t_role r,t_role_menu rm "+ "where m.id = rm.menu_id and rm.role_id = r.id "+ "and r.id = ur.role_id and ur.user_id = u.id "+ "and u.id=#{id} order by m.priority")List<Menu> findByUser(Integer id);
}
Shiro权限控制+整合shiro相关推荐
- Apache Shiro权限控制框架简介
Apache Shiro权限控制框架简介 要想实现权限控制,可以自己写代码实现,蓄力都的权限控制可以通过过滤器Filter实现,细粒度的权限控制是基于代理对象结合自定义的注解和反射技术来实现,反射技术 ...
- Shiro权限控制(二)
之前写过Shiro的文章,但是当回过头来整理的时候,发现缺了好多东西,今天重新整理一下. 我们都知道Shiro和secitity都是安全的框架,但是相对于Shiro来说,比较入门简单,所需要的功能基本 ...
- 权限控制框架Shiro简单介绍及配置实例
Shiro是什么 Apache Shiro是一个非常易用的Java安全框架它能提供验证.授权.加密和Session控制.Shiro非常轻量级而且API也非常易于理解可以使用Shiro完成从APP到企业 ...
- shiro权限控制登陆成功页面跳转问题
在开发中使用了shiro进行权限控制,遇到一个页面跳转问题:当用户账号密码都正确的时候并没有跳转到登陆成功页面. 在shiroFilter过滤器中配置了登陆成功路径没有反应.注意:我使用的是表单验证. ...
- 权限控制框架 shiro
第一章 Shiro简介--<跟我学Shiro> 博客分类: 跟我学Shiro 跟我学shiro 目录贴: 跟我学Shiro目录贴 1.1 简介 Apache Shiro是Java的一个 ...
- 一个jsp能取到父类jsp的值吗_「Javaweb」ssm整合权限控制框架shiro,你知道怎么做吗?...
为美好而努力--羊羽科技说. 最近在开发自己的网站,需要权限控制功能,在网上找了一下,找到了我接下来要介绍的shiro框架. shiro框架是Apache公司维护的开源产品之一,其官网对其的简介是这样 ...
- JFinal配合Shiro权限控制在FreeMarker模板引擎中控制到按钮粒度的使用
实现在FreeMarker模板中控制对应按钮的显示隐藏主要用到了Shiro中的hasRole, hasAnyRoles, hasPermission以及Authenticated等方法,我们可以实现T ...
- Shiro权限控制笔记要点
一.Shiro 1.1 权限管理过滤器解释: Authentication :身份认证/登录,验证用户是不是拥有相应的身份: Authorization :授权,即权限验证,验证某个已认证的用户是否拥 ...
- 【Spring-boot】shiro权限控制
缓存使用redis,自定义ShiroRedisCache.ShiroRedisCacheManager用于存储用户缓存信息 自定义MyShiroToken继承自AuthenticationToken. ...
最新文章
- pytorch 与 numpy 的数组广播机制
- 互联网为什么需要全局唯一ID?
- [BZOJ 2038][2009国家集训队]小Z的袜子(hose)(莫队)
- 设置超链接在新的窗口中打开,而不是在本窗口中打开
- 使用android frame动画定义自己的ProgressBar
- (一)docker run 命令参数
- easyUI tree 多选框设置是否级联选中
- oracle多表查询while,oracle while的用法示例分享
- 总结了一下初学者对Go错误处理的四个误解!
- [转]tensorflow中的gather
- 押错宝!一次性将百万行代码从 Flow 迁移至 TypeScript
- android BaseAdapter优化
- 2.4g 无线键鼠对码软件_RK526无线键鼠套装开箱体验
- yocto 编译与bb的语法
- 关闭Windows Defender Service工具
- hazelcast java_Hazelcast
- 为什么弃用lofter
- Android系统开发:短信的号码拦截
- Thymeleaf是干什么的
- python 分行读取txt文件