一、apache shiro简介
1、shiro框架的核心功能:
认证、授权、会话管理、加密
2、shiro框架认证流程
Application Code:应用程序代码,由开发人员负责开发的
Subject:框架提供的接口,代表当前用户对象
SecurityManager:框架提供的接口,代表安全管理器对象
Realm:可以开发人员编写,框架也提供一些,类似于DAO,用于访问权限数据
二、shiro的使用(认证)
shiro在spring中使用
1、在项目中导入shiro相关的jar包
2、在web.xml中配置spring提供的整合shiro的过滤器
<!-- 配置shiro的过滤器 -->
<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>/*</url-pattern>
</filter-mapping>
注意过滤器先后,如果该过滤器配置在struts2的核心过滤器后面,调用realm进行认证就会抛异常:org.apache.shiro.UnavailableSecurityManagerException
3、在spring配置文件中配置bean,name为shiroFilter,这里的name要和第二步中的过滤器的名字相同,否则启动项目会报错
<!-- 配置shiro框架的过滤器工厂 -->
<bean name="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref=""/>
<property name="loginUrl" value="/login.jsp"/>
<property name="successUrl" value="/index.jsp"/>
<property name="unauthorizedUrl" value="/unauthorized.jsp"/>
<property name="filterChainDefinitionMap" >
<value>
/css/** = anon
/js/** = anon
/images/** = anon
/login.jsp* = anon
/userAction_login.action* = anon
/userAction_userList.action = perms["userList"]
/* = authc
</value>
</property>
</bean>
4、配置安全管理器,并注入到shiroFilterFactoryBean中
<!-- 安全管理器 -->
<bean name="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"></bean>
5、在登录方法中使用shiro认证
public class UserAction implements ModelDriven<User> {
private User user = new User();
public String login() {
System.out.println("success");
System.out.println(user.getUserName()+" "+user.getPassword());
/*-----------------通过shiro进行认证----------------------*/
//获得安全管理器
Subject subject = SecurityUtils.getSubject();
//用户名密码令牌,用于认证
UsernamePasswordToken token = new UsernamePasswordToken(user.getUserName(), user.getPassword());
try {
//通过安全管理器进行认证,认证不通过抛异常
subject.login(token);
User user2 = (User) subject.getPrincipal();
ServletActionContext.getRequest().getSession().setAttribute("user", user2);
} catch (Exception e) {
e.printStackTrace();
return "login";
}
/*-----------------通过shiro进行认证----------------------*/
return "success";
}
@Override
public User getModel() {
return user;
}
}
6、自定义Realm,在realm中认证
认证逻辑:通过用户名查用户;未查到返回null;查到了返回密码,shiro会自行对比。可以把查到了user放入返回对象中,在login中取出
public class MyRealm extends AuthorizingRealm {
private UserDao userDao;
//认证方法
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//强转为用户名密码令牌,用来获得用户名
UsernamePasswordToken myToken = (UsernamePasswordToken)token;
//从令牌中获得用户名
String username = myToken.getUsername();
User user = UserDao.getUserByUsername(username);
//没有查到用户,返回null
if(user == null) {
return null;
}
//查到用户,返回密码,shiro会进行对比
AuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), this.getName());
return info;
}
//授权方法
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
// TODO Auto-generated method stub
return null;
}
}
7、配置安全管理器并注入Realm
<!-- 安全管理器 -->
<bean name="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!-- 注入自定义realm -->
<property name="realm" ref="myRealm"></property>
</bean>
<!-- 注册自定义realm -->
<bean name="myRealm" class="club.ityuchao.web.realm.MyRealm" />
当用户不存在时:抛异常org.apache.shiro.authc.UnknownAccountException
当用户存在密码错误时:org.apache.shiro.authc.IncorrectCredentialsException
三、在realm中授权
//授权方法
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("调用授权方法。。。。");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//为用户授权(项目中使用需要从数据库中查询权限,然后一个一个添加)
info.addStringPermission("userList");
return info;
}
四、shiro的四种权限控制方式
1、url控制(基于过滤器)
<property name="filterChainDefinitionMap" >
<value>
/css/** = anon
/js/** = anon
/images/** = anon
/login.jsp* = anon
/userAction_login.action* = anon
/userAction_userList.action = perms["userList"]
/* = authc
</value>
</property>
2、使用注解进行权限控制(基于代理)
(1)在spring配置文件中开启shiro注解
<!-- 开启shiro注解 -->
<bean name="defaultAdvisorAutoProxyCreator" class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
<!-- 必须使用cglib方式给action创建代理对象 -->
<property name="proxyTargetClass" value="true"></property>
</bean>
<!-- 配置shiro框架提供的切面类,用于创建代理对象 -->
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"/>
(2)在方法上使用
@RequiresPermissions("delete")
public String delete() {
return "success";
}
(3)struts2中捕获异常
当用户没有该方法的权限的时候,抛出异常:org.apache.shiro.authz.UnauthorizedException
没有认证抛异常:org.apache.shiro.authz.UnauthenticatedException
因为注解使用的是代理方式,url拦截使用的是过滤器,原理不一样,所以application.xml中的配置没认证和没授权跳转的页面在该方式中不起作用,必须在struts2中定义全局异常进行跳转
<!-- 全局结果集定义 -->
<global-results>
<result name="login">/login.jsp</result>
<result name="unauthorized">/unauthorized.jsp</result>
</global-results>
<global-exception-mappings>
<exception-mapping result="unauthorized" exception="org.apache.shiro.authz.UnauthorizedException"></exception-mapping>
</global-exception-mappings>
3、使用页面标签进行控制
(1)在jsp页面中引入shiro的标签库
<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
(2)使用shiro的标签控制页面元素展示
<shiro:hasPermission name="update">
<input type="button" value="修改" />
</shiro:hasPermission>
4、方法中代码控制(基于代理)
public String userList() {
//使用代码检查是否有该权限
Subject subject = SecurityUtils.getSubject();
subject.checkPermission("userList");
return "success";
}
五、使用ehcache缓存权限数据
ehcache是专门缓存插件,可以缓存Java对象,提高系统性能。
如果不适用,每次访问需要授权的路径或方法的时候都需要连接一次数据库获得授权:
使用步骤:
1、导入jar
2、在项目中配置ehcache.xml文件
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<diskStore path="java.io.tmpdir"/>
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
maxElementsOnDisk="10000000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
/>
</ehcache>
java.io.tmpdir:操作系统的临时目录
maxElementsInMemory:最多的对象个数
eternal="false":表示对象不是永久有效,有生命周期
timeToIdleSeconds="120":120没有调用就自动清理掉对象
timeToLiveSeconds="120":存活时间
overflowToDisk="true":溢出到磁盘,最多存10000个,第10001个就放到java.io.tempdir上
maxElementsOnDisk="10000000":磁盘最多存放对象个数
diskPersistent="false":Tomcat重启后,磁盘上的数据就不要了
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU":淘汰策略;LRU最近最少使用;FIFO:先进先出
3、在spring配置文件中配置缓存管理器对象,并注入给安全管理器对象
<!-- 注册缓存管理器 -->
<bean name="ehCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<!-- 注入ehcache配置文件 -->
<property name="cacheManagerConfigFile" value="classpath:ehcache.xml"></property>
</bean>
<!-- 安全管理器 -->
<bean name="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!-- 注入自定义realm -->
<property name="realm" ref="myRealm"></property>
<!-- 注入缓存管理器 -->
<property name="cacheManager" ref="ehCacheManager"></property>
</bean>