Apache Shiro是Java的一个安全框架,官网为shiro.apache.org,主要场景为控制登录,判断用户是否有访问某个功能的权限等等。css
认证java
受权web
会话管理spring
加密数据库
引入Shiro对应的jar包,下面给出Mavenapache
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-all</artifactId> <version>1.2.2</version> </dependency>
在web.xml中配置spring框架提供的用于整合shiro框架的过滤器缓存
<!-- 配置shiro过滤器 --> <filter> <filter-name>shiroFilter</filter-name> // 须要在spring的配置文件中建立一个bean(shiroFilter) <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>shiroFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
下面咱们将要进行Shiro的认证功能和受权功能的实现,代码比较多。安全
Application Code:应用程序代码, 即登陆方法(登陆方法不是直接查询数据库,而是调用Shiro框架提供的接口来实现)服务器
Subject:框架提供的接口,表明当前用户对象app
SecurityManager:框架提供的接口,表明安全管理器对象
Realm:能够开发人员编写(即认证和受权方法)
咱们首先将登陆方法按照Shiro指定的方式进行改进
public String login() { // 获取验证码 String validateCode = (String) ServletActionContext.getRequest().getSession().getAttribute("key"); // 判断验证码 if (StringUtils.isNotBlank(checkcode) && checkcode.equals(validateCode)) { // 获取getSubject对象,Shiro中表明当前用户对象 Subject subject = SecurityUtils.getSubject(); // 令牌 传递进去前台接受的帐号和密码 AuthenticationToken token = new UsernamePasswordToken(model.getUsername(), MD5Utils.md5(model.getPassword()));// 建立用户名密码令牌对象 try { subject.login(token); // 调用内置的登陆方法来实现检验 若是登录错误就会抛出异常,返回登陆页面 } catch (Exception e) { e.printStackTrace(); return LOGIN; } User user = (User) subject.getPrincipal(); // 登陆成功后能够从subject取得登陆对象 ServletActionContext.getRequest().getSession().setAttribute("loginUser", user); return HOME; } else { this.addActionError("输入验证码错误"); return LOGIN; } }
而后编写Realm
继承AuthorizingRealm
,即编写具体的认证和受权方法
public class BOSRealm extends AuthorizingRealm { @Autowired private IUserDao userDao // 受权方法 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) { // 能够在这里将用户所属的权限查询出来,而后遍历赋值给info对象,这样就能够实现了受权 // 判断访问路径或者方法有没有权限的时候就是根据info中的数据来判断 info.addStringPermission("staff-list"); return info; } // 认证方法(登录方法) protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException { System.out.println("认证..."); UsernamePasswordToken passwordToken = (UsernamePasswordToken) arg0; // 对象转换 String username = passwordToken.getUsername(); // 得到username User user = userDao.findByUsername(username); // 经过username从数据库中获取到User对象 if (user == null) { return null; } // 内置验证方法 (数据库中获取的对象,对象的密码, this.getName()) AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user, user.getPassword(), this.getName()); return authenticationInfo; } }
到上面之后编码工做就完成了,剩下的就是进行配置了,首先将编写的Realm
注入到安全管理器,整合的是Spring,因此下面的配置都是在Spring配置文件中。
<!-- 注册realm --> <bean id="bosRealm" class="lyh.bos.realm.BOSRealm"></bean> <!-- 注册安全管理器对象 --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="bosRealm"/> </bean>
配置ShiroFilterFactoryBean
,同时还能够配置一下URL拦截规则,注意这里的id要和web.xml
中<filter-name>shiroFilter</filter-name>
相同
<!-- shiro 配置 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <!-- 此处为web.xml配置的拦截器 --> <!-- 注入安全管理器对象 --> <property name="securityManager" ref="securityManager"/> <!-- 注入相关页面访问URL --> <property name="loginUrl" value="/login.jsp"/> <!-- 登陆页面 --> <property name="successUrl" value="/index.jsp"/> <!-- 登陆成功的主页 --> <property name="unauthorizedUrl" value="/unauthorized.jsp"/> <!-- 权限不足转向的错误页面 --> <property name="unauthorizedUrl" value="/unauthorized.jsp"/> <!--注入URL拦截规则, --> <!-- anon 均可以访问 perms["staff-list"] 是否有staff-list权限 authc 登陆才能够访问 --> <property name="filterChainDefinitions"> <value> /css/** = anon /admin/logout = logout <!-- 注销,访问这个路径,自动注销不须要本身编写方法 --> /images/** = anon /validatecode.jsp* = anon /login.jsp = anon /userAction_login.action = anon /page_base_staff.action = perms["staff-list"] <!-- 表示page_base_staff.action路径须要有staff-list权限才可访问 --> /* = authc </value> </property> </bean>
Shiro还提供了使用注解
控制权限的方法,开启方式以下,同时,使用的注解权限还须要配置全局异常,用来捕获当权限不足抛出异常时转向的页面,这里使用的是SpringMVC
,开启方式@RequiresPermissions("staff-list")
<!-- 开启注解配置权限 --> <bean id="defaultAdvisorAutoProxyCreator" class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"> <!-- 必须使用cglib方式为 Action对象建立代理对象 --> <property name="proxyTargetClass" value="true"/> </bean> <!-- 配置shiro框架提供的切面类,用于建立代理对象 --> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"/>
// 配置全局异常处理器 <!-- 须要进行权限控制的页面访问 --> <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>
URL权限拦截控制
方法注解权限控制
页面标签权限控制(当有对应的权限就显示对应的页面元素,没有权限则不显示), 须要在对应的页面引入Shiro的标签库
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
,能够用在HTMl也能够用在JS中。
代码级别权限控制(在方法内添加代码)
若是访问一个页面就执行一次受权,就会访问数据库,浪费资源,因此咱们可使用ehcache来进行缓存权限,只要登陆时进行一次受权,后面无需再次受权,直接使用缓存。
shiro自动整合ehcache,只须要简单配置就能使用。
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="600" timeToLiveSeconds="600" overflowToDisk="true" maxElementsOnDisk="10000000" diskPersistent="false" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU" /> <!-- 内存中最多能够存储多少个数据 是否永久有效 空闲时间 存活时间 内存空间不够是否存储到磁盘 磁盘最大存储个数 服务器重启,磁盘数据是否须要 线程 淘汰策略(最近最少使用) --> </ehcache>
<!-- 注册ehcache --> <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> <property name="cacheManagerConfigFile" value="classpath:ehcache.xml" /> </bean>
<property name="cacheManager" ref="cacheManager"/>
一条语句便可。<!-- 注册安全管理器对象 --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="bosRealm"/> <!-- 将ehcache注入shiro --> <property name="cacheManager" ref="cacheManager"/> </bean>