Shiro安全框架入门
Posted anyueemo
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Shiro安全框架入门相关的知识,希望对你有一定的参考价值。
完整学习网址:https://www.w3cschool.cn/shiro
一、Shiro简介
1、Shiro的概念
Apache Shiro是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理等功能。
认证(Authentication):用户身份识别,常被称为用户“登录”,判断用户是否登陆,如果未登陆,则拦截其请求。优点:可以实现单点登录,多个子系统登录一个其他子系统也自动登录。(如登录了淘宝,天猫也自动登录了)
授权(Authorization):访问控制。当用户登陆后,判断其身份是否有权限访问相应的资源,如果没有权限则拦截
密码加密(Cryptography):保护或隐藏数据防止被偷窃。将MD5进行二次封装,让其更加容易使用。注意MD5不可逆运算
会话管理(Session Management)
Shiro的三大核心组件
Subject:正与系统进行交互的人,或某一个第三方服务。所有Subject实例都被绑定到(且这是必须的)一个SecurityManager上。
SecurityManager:Shiro架构的心脏,典型的Facade模式。用来协调内部各安全组件,管理内部组件实例,并通过它来提供安全管理的各种服务。当Shiro与一个Subject进行交互时,实质上是幕后的SecurityManager处理所有繁重的Subject安全操作。
Realms:本质上是一个特定安全的DAO。当配置Shiro时,必须指定至少一个Realm用来进行身份验证和/或授权。Shiro提供了多种可用的Realms来获取安全相关的数据。如关系数据库(JDBC),INI及属性文件等。可以定义自己Realm实现来代表自定义的数据源。
Shiro 完整架构图
2、Shiro内置过滤器
Shiro的内置过滤器分为两组:
认证过滤器:anon(不认证也可以访问),authcBasic, authc(必须认证后才可访问)
授权过滤器:perms(指定资源需要哪些权限才可以访问),Roles, ssl, rest, port
过滤器名称 |
过滤器类 |
描述 |
anon |
org.apache.shiro.web.filter.authc.AnonymousFilter |
匿名过滤器 |
authc |
org.apache.shiro.web.filter.authc.FormAuthenticationFilter |
如果继续操作,需要做对应的表单验证否则不能通过 |
authcBasic |
org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter |
基本http验证过滤,如果不通过,跳转登录页 |
perms |
org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter |
权限过滤器 |
port |
org.apache.shiro.web.filter.authz.PortFilter |
端口过滤器,可以设置是否是指定端口如果不是跳转到登录页面 |
rest |
org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter |
http方法过滤器,可以指定如post不能进行访问等 |
roles |
org.apache.shiro.web.filter.authz.RolesAuthorizationFilter |
角色过滤器,判断当前用户是否指定角色 |
ssl |
org.apache.shiro.web.filter.authz.SslFilter |
请求需要通过ssl,如果不是跳转登录页 |
user |
org.apache.shiro.web.filter.authc.UserFilter |
如果访问一个已知用户,比如记住我功能,走这个过滤器 |
二、基本使用
配合Demo说明基本使用方法
项目环境:Eclipse+SSH
1、ERP整合Shiro
1)添加依赖
在erp_parent父工程的pom.xml添加依赖
<properties> <shiro.ver>1.2.3</shiro.ver> </properties> <dependencies> <!-- shiro --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>${shiro.ver}</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>${shiro.ver}</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>${shiro.ver}</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-aspectj</artifactId> <version>${shiro.ver}</version> </dependency> </dependencies>
2)配置web.xml, 添加过滤器代理DelegatingFilterProxy,要放在struts2的核心过滤器之前
<!-- 配置shiro的过滤器代理DelegatingFilterProxy --> <filter> <filter-name>shiroFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>shiroFilter</filter-name> <url-pattern>*.action</url-pattern> <url-pattern>*.html</url-pattern> <url-pattern>*</url-pattern> </filter-mapping>
Spring提供的一个简便的过滤器处理方案,它将具体的操作交给内部的Filter对象delegate去处理,而这个delegate对象通过Spring的IOC容器获取,这里采用的是Spring的FactorBean的方式获取这个对象。虽然配置了这一个filter,但是它并没做任何实际的工作,而是把这个工作交由Spring容器中一个bean的id为shiroFilter的类,即ShiroFilterFactoryBean。
3)添加shiro核心控制器的spring配置文件applicationContext_shiro.xml在erp_web的资源目录下(中间省略了部分业务相关的配置)
注意过滤链的顺序,匿名-->授权-->认证
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd "> <!-- shiro的过滤工厂,相当默认的加载了9个过滤器 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <!-- 安全管理器,shiro核心组件(大脑) Facade模式 --> <property name="securityManager" ref="securityManager" /> <property name="filters"> <map> <entry key="perms" value-ref="erpAuthorizationFilter"></entry> </map> </property> <!-- 认证相关配置:用户如果没有登陆,当他在访问资源的时候,就会自动跳转到登陆的页面 --> <property name="loginUrl" value="/login.html"></property> <!-- 授权相关配置:当用户没有访问某项资源权限的时候,跳转到该页面 --> <property name="unauthorizedUrl" value="/error.html"></property> <!-- 过滤链的定义:定义URL访问的时候对应的认证或授权时处理的过滤器 --> <property name="filterChainDefinitions"> <value> /error.html = anon /login_*.action = anon /login_* = anon /emp_*=perms["用户角色设置","重置密码"] /goodstype.html=perms["商品类型"] /goodstype_*=perms["商品类型"] /goods.html=perms["商品"] /goods_*=perms["商品"] /orders.html=perms["采购订单查询","采购订单申请","采购订单审核","采购订单确认","采购订单入库","我的采购订单","销售订单查询","销售订单录入","销售订单出库"] /orders_*=perms["采购订单查询","采购订单申请","采购订单审核","采购订单确认","采购订单入库","我的采购订单","销售订单查询","销售订单录入","销售订单出库"] /report_*.html=perms["销售统计报表","销售趋势报表"] /report_*=perms["销售统计报表","销售趋势报表"] /*.html = authc /*.action = authc /* = authc </value> </property> </bean> <!-- 安全管理器 --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="erpRealm"></property> </bean> <!-- 自定义的realm --> <bean id="erpRealm" class="cn.itcast.erp.realm.ErpRealm"> <property name="empBiz" ref="empBiz"></property> </bean> <!-- 自定义的过滤器 --> <bean id="erpAuthorizationFilter" class="cn.itcast.erp.filter.ErpAuthorizationFilter"></bean> </beans>
4)创建error.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>提示信息</title> </head> <body> 尊敬的用户,您没有访问权限! </body> </html>
2、认证功能
1)LoginAction的checkUser方法中使用Subject自带的login方法
说明:UsernamePasswordToken是AuthenticationToken的子接口的实现类,它是对用户名和密码的封装。
Subject是对当前用户执行操作的封装,此处用它来执行登陆的操作。如果用户名和密码错误,login方法会抛出AuthenticationException异常
public void checkUser() { try { //1. 创建令牌,身份证明 UsernamePasswordToken upt = new UsernamePasswordToken(username,pwd); //2. 获取主题 subject: 封装当前用户的一些操作 Subject subject = SecurityUtils.getSubject(); //3. 执行login subject.login(upt); } catch (Exception e) { e.printStackTrace(); } }
2)自定义Realm
Realm的概念:Realm是Shiro与应用安全数据间的“桥梁”或者“连接器”。实质上是一个安全相关的DAO,它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)授权。可以配置多个Realm。
作业流程:使用subject.login方法后,并不会调用登陆的业务层进行登陆的验证查询,即不会从数据库查找登陆的用户名和密码是否正确,而是将这项工作交给shiro去完成。
shiro通过Realm找到我们提供的登陆验证业务,验证登陆的用户名和密码是否正确。因此真正实现登陆验证的是Realm,而shiro只是去调Realm。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。
a)在erp_web子工程下创建包cn.itcast.erp.realm
b)创建ErpRealm类继承自AuthorizingRealm
public class ErpRealm extends AuthorizingRealm { private IEmpBiz empBiz; public void setEmpBiz(IEmpBiz empBiz) { this.empBiz = empBiz; } /** * 认证 * @return null:认证失败, AuthenticationInfo实现类,认证成功 */ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { //通过令牌得到用户名和密码 UsernamePasswordToken upt = (UsernamePasswordToken) token; //得到密码 String pwd = new String(upt.getPassword()); //调用登录查询 Emp emp = empBiz.findByUsernameAndPwd(upt.getUsername(), pwd); if(null != emp) { //构造参数1: 主角=登陆用户 //参数2:授权码:密码 //参数3:realm的名称 SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(emp,pwd,getName()); return info; } return null; } }
empBiz.findByUsernameAndPwd方法
/** * 用户登陆 * @param username * @param pwd * @return */ public Emp findByUsernameAndPwd(String username, String pwd) { //查询前先加密 pwd = encrypt(pwd, username); return empDao.findByUsernameAndPwd(username, pwd); }
empDao.findByUsernameAndPwd方法
public Emp findByUsernameAndPwd(String username, String pwd){ String hql = "from Emp where username=? and pwd=?"; List<Emp> list = (List<Emp>) this.getHibernateTemplate().find(hql, username, pwd); //能够匹配上,则返回第一个元素 if(list.size() > 0){ return list.get(0); } //如果登陆名或密码不正确 return null; }
c)applicationContext_shiro.xml中与自定义ErpRealm相关的配置
<!-- 安全管理器 --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="erpRealm"></property> </bean> <!-- 自定义的realm --> <bean id="erpRealm" class="cn.itcast.erp.realm.ErpRealm"> <property name="empBiz" ref="empBiz"></property> </bean>
注意:登录的action要配置为anon,不然由于配置了/*=authc,会强行跳转到登陆页面。
/login_*.action = anon
/login_* = anon
d)LoginAction中的loginOut方法也可以通过subject的getPrincipal方法提取主题对象。
Shiro提供了会话管理机制,实际上自定义的realm认证方法返回值对象中的主角对象就是登陆的用户,可以通过subject的getPrincipal方法将其提取出来。
/** * 退出登陆 */ public void loginOut() { //ActionContext.getContext().getSession().remove("loginUser"); SecurityUtils.getSubject().logout(); }
3、授权功能
授权就是通过设置规则,指定哪些URL需要哪些权限才可以访问。
在ErpRealm中新增授权方法作用:告诉shiro当前用户有什么权限
在applicationContext_shiro.xml增加配置信息作用:告诉shiro什么资源有什么权限才可以访问
1)授权方法与配置
a) 在ErpRealm中新增授权方法
/** * 授权 */ protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); //获取当前用户 Emp emp = (Emp) principals.getPrimaryPrincipal(); //获取当前登陆用户的菜单权限 List<Menu> menuList = empBiz.getMenusByEmpuuid(emp.getUuid()); //加入授权 for(Menu m : menuList) { //这里使用menuname来做授权里的key值,那么在配置授权访问的url=perms[菜单名称] info.addStringPermission(m.getMenuname()); } return info; }
empBiz.getMenusByEmpuuid方法
/** * 根据员工编号获取菜单 * @param uuid */ public List<Menu> getMenusByEmpuuid(Long uuid) { return empDao.getMenusByEmpuuid(uuid); }
empDao.getMenusByEmpuuid方法
public List<Menu> getMenusByEmpuuid(Long uuid) { String hql = "select m from Emp e join e.roles r join r.menus m where e.uuid=?"; return (List<Menu>) this.getHibernateTemplate().find(hql, uuid); }
b) applicationContext_shiro.xml中与配置授权控制规则相关的内容
/goodstype.html=perms["商品类型"]
/goodstype_*=perms["商品类型"]
/goods.html=perms["商品"]
/goods_*=perms["商品"]
/store.html=perms["仓库"]
/store_*=perms["仓库"]
/orders.html=perms["采购订单查询","采购订单申请","采购订单审核","采购订单确认","采购订单入库","我的采购订单","销售订单查询","销售订单录入","销售订单出库"]
/orders_*=perms["采购订单查询","采购订单申请","采购订单审核","采购订单确认","采购订单入库","我的采购订单","销售订单查询","销售订单录入","销售订单出库"]
/report_*.html=perms["销售统计报表","销售趋势报表"]
/report_*=perms["销售统计报表","销售趋势报表"]
2)自定义授权过滤器
当一个URL有多个权限需要访问的时候,如果按下面的方法来配置,系统默认使用的是and关系,即同时具备这两个权限才可以访问此URL
/orders.html=perms[“采购订单查询”,“采购订单审核”]
如果想要转换成or关系,即只要有具备一种就可以访问此URL,就需要自定义授权过滤器。
a)在erp_web子工程下创建包cn.itcast.erp.filter
b)创建自定义过滤器,继承自AuthorizationFilter
/** * 自定义授权过滤器 */ public class ErpAuthorizationFilter extends AuthorizationFilter { @Override protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { //获取主题 Subject subject = getSubject(request,response); //orders.html=perms["采购订单的查询","采购订单的审核","采购订单的确认","采购订单的入库"] //mappedValue="采购订单的查询","采购订单的审核","采购订单的确认","采购订单的入库" String[] perms = (String[]) mappedValue; boolean isPermitted = true; if(null == perms || perms.length == 0){ return isPermitted; } if(null != perms || perms.length > 0){ for(String perm : perms) { //只要有一个权限,就返回true if(subject.isPermitted(perm)) { return true; } } } return false; } }
c)applicationContext_shiro.xml中配置过滤器相关内容
<property name="filters"> <map> <entry key="perms" value-ref="erpAuthorizationFilter"></entry> </map> </property> <!-- 自定义的过滤器 --> <bean id="erpAuthorizationFilter" class="cn.itcast.erp.filter.ErpAuthorizationFilter"></bean>
以上是关于Shiro安全框架入门的主要内容,如果未能解决你的问题,请参考以下文章