06-Spring集成Shiro(ssm+Shiro)

这一章能解决的问题:(目录)

需求1:spring整合shiro

需求2:集成shiro的认证操作
需求3:
   3.1.登录成功后,在页头显示“欢迎XXX”
   3.2.把该用户的菜单显示出来
需求4:把自定义Realm中的用户信息改成动态的,并使用md5散列
需求5:授权---从数据库授权
需求6:使用注解的方式和shiro标签的方式配置
需求7:使用注解方式进行授权
需求8:shiro的缓存(针对上面授权的频繁查询数据库的问题,这里要使用shiro缓存来解决)
需求9:管理员一旦修改了用户的某个权限,要立即生效,即使用户不退出系统和不关闭浏览器,也要立即生效!

需求10:会话的实现
需求11:验证码的实现
需求12:记住我的实现

-------------------------------------------------------------------------

流程:

 

在项目的基础上演示:(项目的搭建这里就不演示,直接在一个项目上演示,注:传智播客的项目)

===========================================================================

需求1:spring整合shiro

web中实现权限访问控制是通过认证拦截器和授权拦截器来实现的,而shiro中也是使用shiro的拦截器来实现的,而且shiro中是使用很多拦截器:

比如:认证拦截器、授权拦截器、sessionManager、ehcacheManager等等

加入jar包:

shiro-spring-1.2.3.jar、shiro-web-1.2.3.jar、commons-beanutils-1.8.3.jar、commons-logging-1.1.1.jar、

junit-4.9.jar、log4j-1.2.17.jar、shiro-core-1.2.3.jar、slf4j-api-1.7.5.jar、slf4j-log4j12-1.7.5.jar

一、在web.xml中配置shiro的Filter

	<!-- Shiro的Filter:DelegatingFilterProx会从spring容器中找shiroFilter -->
	<filter>
	<filter-name>shiroFilter</filter-name>
	<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
		<init-param>
			<param-name>targetFilterLifecycle</param-name>
			<param-value>true</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>shiroFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

二、自定义Realm,CustomRealm.java(还需要修改)

package cn.itcast.ssm.realm;

import java.util.ArrayList;
import java.util.List;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

// 自定义Realm
public class CustomRealm extends AuthorizingRealm {

	/**
	 * 认证 token:用户输入的令牌,可以是字符串,也可以是对象
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken token) throws AuthenticationException {
		// 从token中获取用户名
		String username = (String) token.getPrincipal();

		// 根据用户输入的用户名从数据库中查找用户信息(密码),这里只是模拟的静态数据
		// 如果查不到就返回null...
		// 假设数据库中的用户名是PeterDB
		if (!"zhang".equals(username)) {
			return null; // 返回null之后,下面的代码都不执行
		}

		// 如果查到就返回password
		String password = "123";

		// 返回认证信息
		SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
				username, password, this.getName());

		return simpleAuthenticationInfo;
	}

	// Realm的名称(自定义),可以不定义
	@Override
	public void setName(String name) {
		super.setName("customRealm");
	}

	/**
	 * 授权
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(
			PrincipalCollection principals) {
		// 授权是在认证通过后的操作,principals是认证通过后返回的SimpleAuthenticationInfo对象的第一个参数
		String username = (String) principals.getPrimaryPrincipal();
		// 模拟数据库的静态数据(开发中要从数据库中获取)
		List<String> permissions = new ArrayList<String>();
		permissions.add("user:create"); // 添加用户
		permissions.add("items:add"); // 添加商品
		// 返回权限信息
		SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
		simpleAuthorizationInfo.addStringPermissions(permissions);
		return simpleAuthorizationInfo;
	}
}

 

三、创建applicationContext-shiro.xml(还需要修改)

securityManager:这个属性是必须的。

loginUrl:没有登录认证的用户请求将跳转到此地址,不是必须的属性,不输入地址的话会自动寻找项目web项目的根目录下的”/login.jsp”页面。

unauthorizedUrl:没有权限默认跳转的页面。

 

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
		http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 
		http://www.springframework.org/schema/mvc 
		http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd 
		http://www.springframework.org/schema/context 
		http://www.springframework.org/schema/context/spring-context-3.2.xsd 
		http://www.springframework.org/schema/aop 
		http://www.springframework.org/schema/aop/spring-aop-3.2.xsd 
		http://www.springframework.org/schema/tx 
		http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">

	<!-- Shiro 的Web过滤器 -->
	<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
		<property name="securityManager" ref="securityManager" />
		<!-- 如果没有认证将要跳转的登陆地址,http可访问的url,如果不在表单认证过虑器FormAuthenticationFilter中指定此地址就为身份认证地址 -->
		<property name="loginUrl" value="/login.action" />
		<!-- 认证成功后的跳转路径,但建议不配置,因为shiro如果认证成功会自动跳转到上一个请求路径 -->
		<!-- <property name="successUrl" value="/first.action"/> -->
		<!-- 没有权限跳转的地址 -->
		<property name="unauthorizedUrl" value="/refuse.jsp" />
		<property name="filterChainDefinitions">
			<value>
				<!-- /** = anon 所有的URL都可以匿名访问 -->
				/**=anon
			</value>
		</property>
	</bean>

	<!-- 安全管理器 -->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="realm" ref="userRealm" />
	</bean>

	<!-- 自定义 realm -->
	<bean id="userRealm" class="cn.itcast.ssm.realm.CustomRealm"></bean>
</beans>

  运行测试:

因为以上去掉了拦截器,并且使用了shiro的拦截器,因为所有请求都配置成了/**=anon,即所有URL都匿名访问,所以这时候直接输入:

http://localhost:8080/ssm_shiro/first.action回车就会进入到主页面,并不用登录:

需求2:集成shiro的认证操作

认证通过的用户使用自定义po类接收:ActiveUser.java(getter/setter省略)

public class ActiveUser implements java.io.Serializable {
	private String userid;//用户id(主键)
	private String usercode;// 用户账号
	private String username;// 用户名称

	private List<SysPermission> menus;// 菜单
	private List<SysPermission> permissions;// 权限

 LoginController.java:

@Controller
public class LoginController {
	@RequestMapping("/login")
	public String login(HttpServletRequest request) throws Exception {
		// 如果登录失败,从request中获取认证失败的信息,shiroLoginFailure
		String exceptionClassName = (String) request.getAttribute("shiroLoginFailure");
		
		// 异常信息处理
		if (exceptionClassName != null) {
			if (UnknownAccountException.class.getName().equals(exceptionClassName)) {
				throw new CustomException("账号不存在");
			} else if (IncorrectCredentialsException.class.getName().equals(exceptionClassName)) {
				throw new CustomException("密码错误");
			} else {
				throw new Exception();	// 未知错误
			}
		}
		// 认证成功,shiro会自动跳转到上一个请求路径
		
		// 认证失败会跳转到登录
		return "login";
	}   
}

 修改applicationContext-shiro.xml:

<!-- 请求logou.action地址,shiro去清除session  -->
/logout.action=logout
<!-- 处理静态资源被拦截 -->
/images/**=anon
/js/**=anon
/styles/**=anon
<!-- /** = authc 所有的URL都要验证 -->
/**=authc

 注意:登录页面login.jsp中的用户名和密码控件的name必须是username和password,这是shiro框架源码要求的(也可以换成别的名字,需要在applicationContext-shiro.xml中配置即可)

运行:在浏览器地址栏直接输入http://localhost:8080/ssm_shiro/first.action回车就会被shiro拦截器拦截到login.action到登录页面:

这时候如果输入自定义的realm中的模拟数据库的用户名zhang和密码123就可以登录,其他都会被抛出异常页面(账号不存在,密码不存在等等)

-------------------------------------------------------------------------

需求3:

3.1.登录成功后,在页头显示“欢迎XXX”

3.2.把该用户的菜单显示出来

----------------------------------------

一、修改自定义Realm

     @Autowired
      private SysService sysService;
  
    /** * 认证 token:用户输入的令牌,可以是字符串,也可以是对象 */ @Override protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken token) throws AuthenticationException { // 从token中获取用户名 String username = (String) token.getPrincipal(); // 根据用户输入的用户名从数据库中查找用户信息(密码),这里只是模拟的静态数据 // 如果查不到就返回null... // 假设数据库中的用户名是PeterDB if (!"zhangsan".equals(username)) { return null; // 返回null之后,下面的代码都不执行 } // 如果查到就返回password String password = "123"; // 认证通过用ActiveUser接收,这里先全部写成静态的数据,等到后面再一次改成动态的 ActiveUser activeUser = new ActiveUser(); activeUser.setUserid("zhangsan"); activeUser.setUsercode("zhangsan"); // 获取菜单 List<SysPermission> menus = null; try { menus = sysService.findMenuListByUserId("zhangsan"); } catch (Exception e) { e.printStackTrace(); } activeUser.setMenus(menus); // 返回认证信息 SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo( activeUser, password, this.getName()); return simpleAuthenticationInfo; } // Realm的名称(自定义),可以不定义 @Override public void setName(String name) { super.setName("customRealm"); }

 这时候登录,并没有菜单,因为登录成功后浏览器地址是http://localhost:8080/ssm_shiro/first.action,而first.action中并没有将菜单添加到jsp,修改first.action

FirstAction.java

@Controller
public class FirstAction {
	//系统首页
	@RequestMapping("/first")
	public String first(Model model)throws Exception{
          // 获取通过认证的用户 Subject subject = SecurityUtils.getSubject();     ActiveUser activeUser = (ActiveUser) subject.getPrincipal();
         // 带到页面 model.addAttribute("activeUser", activeUser); return "/first"; } }

这时候有菜单了,但是欢迎标题还没有,是因为在认证通过之后,并没有将用户名设置到activeUser中,所以在jsp页面用EL表达式没有获取到用户的信息;

修改自定义Realm:CustomRealm.java

activeUser.setUsername("张三");

  

-------------------------------------------------

需求4:把自定义Realm中的用户信息改成动态的,并使用md5散列

	/**
	 * 认证 token:用户输入的令牌,可以是字符串,也可以是对象
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken token) throws AuthenticationException {
		// 从token中获取用户名
		String usercode = (String) token.getPrincipal();
		
		SysUser sysUser = null;
		try {
			sysUser = sysService.findSysUserByUserCode(usercode);
		} catch (Exception e1) {
			e1.printStackTrace();
		}

		// 如果查不到就返回null...
		// 假设数据库中的用户名是PeterDB
		if (sysUser == null) {
			return null; 	// 返回null之后,下面的代码都不执行
		}

		// 如果查到就返回password
		String password = sysUser.getPassword();
		// 盐
		String salt = sysUser.getSalt();
		
		// 认证通过用ActiveUser接收,这里先全部写成静态的数据,等到后面再一次改成动态的
		ActiveUser activeUser = new ActiveUser();
		activeUser.setUserid(sysUser.getId());
		activeUser.setUsercode(sysUser.getUsercode());
		activeUser.setUsername(sysUser.getUsername());
		
		// 获取菜单
		List<SysPermission> menus = null;
		try {
			menus = sysService.findMenuListByUserId(sysUser.getId());
		} catch (Exception e) {
			e.printStackTrace();
		}
		activeUser.setMenus(menus);

		// 返回认证信息
		SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
				activeUser, password, ByteSource.Util.bytes(salt), this.getName());

		return simpleAuthenticationInfo;
	}

 以上使用了md5进行加密,所以还需要在spring中配置凭证匹配器:

applicationContext-shiro.xml

	<!-- 自定义 realm -->
	<bean id="customRealm" class="cn.itcast.ssm.realm.CustomRealm">
		<!-- 将凭证匹配器设置到自定义Realm中 -->
		<property name="credentialsMatcher" ref="credentialsMatcher"/>
	</bean>
	
	<!-- 凭证匹配器 -->
	<bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
		<!-- 使用什么算法 -->
		<property name="hashAlgorithmName" value="md5"/>
		<!-- 散列几次 -->
		<property name="hashIterations" value="1"/>
	</bean>

原始密码111111,加盐hello进行md5加密后的密码是:4ab06e04b551df5f06878a1713a7a090

这时候输入zhangsan,111111,登录成功!

-------------------------------------------------------------------------------------

需求5:授权---从数据库授权

 CustomRealm.java:

	/**
	 * 授权
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(
			PrincipalCollection principals) {
		// 授权是在认证通过后的操作,principals是认证通过后返回的SimpleAuthenticationInfo对象的第一个参数
		ActiveUser activeUser = (ActiveUser) principals.getPrimaryPrincipal();
		// 开发中要从数据库中获取
		List<SysPermission> permissionList = null;
		List<String> permissions = null;
		try {
			permissionList = sysService.findPermissionListByUserId(activeUser.getUserid());
			if (permissionList != null) {
				permissions = new ArrayList<String>();
				for (SysPermission permission : permissionList) {
					permissions.add(permission.getPercode());
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		// 返回权限信息
		SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
		simpleAuthorizationInfo.addStringPermissions(permissions);
		return simpleAuthorizationInfo;
	}

 需求6:使用注解的方式和shiro标签的方式配置

以上在applicatonContext-shiro.xml中配置url比较麻烦,因为要一个一个的配置,也不实际:

7。使用下面的注解方式进行授权:

因为shiro是基于aop代理的,所以需要在springmvc.xml(因为在这个文件集中扫描controller,所以配置到这里)中开启aop注解:

	<!-- 开启aop,对类代理:这是spring的配置 -->
	<aop:config proxy-target-class="true"/>
	<!-- 开启shiro注解支持:这个才是shiro的配置 -->
	<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
		<property name="securityManager" ref="securityManager"/>
	</bean>

 一下是shiro标签:

 在itemsLIst.jsp中添加:

<%@ taglib uri="http://shiro.apache.org/tags" prefix="shiro" %>

	<td>
		<!-- 有item:update权限才能进行修改,相当于if(hasPermission(item:update)) -->
		<shiro:hasPermission name="item:update">
			<a href="${pageContext.request.contextPath }/items/editItems.action?id=${item.id}">修改</a>
		</shiro:hasPermission>
	</td>

 

以上的shiro标签,如果跟踪源代码就会发现,Jsp页面有多少次循环,ItemsController的修改商品的方法就会执行多少次,并且这个方法要多次去查询数据库,数据库的

压力大,所以这里要使用缓存。但是基于注解@RequiresPermissions和shiro标签的方式进行授权确实会比较方便。开发中建议使用这种方式

----------------------------------------------------------------

需求8:shiro的缓存(针对上面授权的频繁查询数据库的问题,这里要使用shiro缓存来解决)

  shiro框架中的认证信息的缓存默认是关闭的(因为信息不大,只要一次认证通过就即刻,不会多次操作数据库),而授权的信息是很大的(可能需要多次频繁操作

数据库),所以shiro框架对授权信息的缓存默认是开启的。

  shiro缓存的原理:

加入jar包:

  

配置缓存管理器:cacheManager,修改applicationContext-shiro.xml

	<!-- 安全管理器 -->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<!-- 自定义Realm -->
		<property name="realm" ref="customRealm" />
		<!-- 缓存管理器 -->
		<property name="cacheManager" ref="cacheManager"/>
	</bean>
	<!-- 缓存管理器 -->
	<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
		<property name="cacheManagerConfigFile" value="classpath:shiro-ehcache.xml"/>
	</bean>

 创建shiro-ehcache.xml,这个文件配置了缓存的参数:

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
	<!--diskStore:缓存数据持久化的目录 地址 -->
	<diskStore path="F:\develop\ehcache" />
	<defaultCache maxElementsInMemory="1000"
		maxElementsOnDisk="10000000" 
		eternal="false" 
		overflowToDisk="false"
		diskPersistent="false" 
		timeToIdleSeconds="120" 
		timeToLiveSeconds="120"
		diskExpiryThreadIntervalSeconds="120" 
		memoryStoreEvictionPolicy="LRU">
	</defaultCache>
</ehcache>

 启动测试:在自定义Realm的认证方法中打断点测试

(1)登录进去,点击:商品管理,进入断点测试,执行完,查询出商品列表,这时候再次点击商品管理,不会进入断点测试,因为商品列表的数据已经被缓存放在内存中,

直接从缓存中获取商品列表数据

 (2)当“退出系统”(属于正常退出),再次进入又会从数据库中获取信息

 (3)如果直接关闭浏览器(属于不正常退出),重新打开一个窗口进入,也会从数据库中获取信息

----------------------------

总结以上操作,不管用户是否正常退出系统,shiro都会清空缓存。

问题:如果管理员修改了用户的某个权限,只要用户不退出系统,或不关闭浏览器,修改的权限就无法立即生效(在缓存时间内)!

需求9:管理员一旦修改了用户的某个权限,要立即生效,即使用户不退出系统和不关闭浏览器,也要立即生效!

做法:在权限修改(在service中修改)完成后,手动编程,调用realm的clearCache方法清除缓存

注意:实际开发中以下方法要写在service中,这里为了方便就直接调用一个controller清空缓存

在CustomRealm.java中添加:

	// 清除缓存
	public void clearCached() {
		PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals();
		super.clearCache(principals);
	}

 创建一个controller,只是用于测试,并没有什么卵用:ClearCache.java

package cn.itcast.ssm.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import cn.itcast.ssm.realm.CustomRealm;

// 清空缓存的controller,只是测试,并没有什么卵用
@Controller
public class ClearCache {
	// 注入Realm
	@Autowired
	private CustomRealm customRealm;
	
	// 清空缓存
	@RequestMapping("/clearShiroCache")
	public String clearShiroCache() {
		// 正常开发中,这个方法要写在service中
		customRealm.clearCached();
		return "success";
	}
}

  启动测试:

第一次进入系统,点击商品管理,会查询数据库,再次点击商品管理,不会查询数据库,这时候如果直接在浏览器的地址栏中输入:

http://localhost:8080/ssm_shiro/clearShiroCache.action,回车,就会清空缓存,后退浏览器,回到系统主页,这时候再点击商品管理,又会查找数据库,因为缓存已被清空

------------------------------------------------------------

需求10:会话的实现

 在applicationContext-shiro.xml中添加shiro的会话管理器

	<!-- 会话管理器 -->
	<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
		<!-- session的失效时长,单位:毫秒 -->
		<property name="globalSessionTimeout" value="600000"/>
		<!-- 删除失效的session -->
		<property name="deleteInvalidSessions" value="true"/>
	</bean>

 注入:

	<!-- 安全管理器 -->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<!-- 自定义Realm -->
		<property name="realm" ref="customRealm" />
		<!-- 缓存管理器 -->
		<property name="cacheManager" ref="cacheManager"/>
		<!-- 会话管理器 -->
		<property name="sessionManager" ref="sessionManager"/>
	</bean>

 启动测试:

进入系统,如果以上配置的<property name="globalSessionTimeout" value="5000"/>,即5秒后session将失效,这时候再刷新页面就会被拦截到登录页面:

需求11:验证码的实现

(1)自定义FormAuthenticationFilter,CustomFormAuthenticationFilter.java

package cn.itcast.ssm.realm;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;

// 自定义的FormAuthenticationFilter,认证之前实现验证码校验
public class CustomFormAuthenticationFilter extends FormAuthenticationFilter {

	// 原FormAuthenticationFilter的认证方法,这里修改将它改写,使得在认证之前进行验证码的校验
	@Override
	protected boolean onAccessDenied(ServletRequest request,
			ServletResponse response) throws Exception {
		// 在这里进行验证码的校验
		
		// 从session中获取正确的验证码(即页面生成的验证码)
		HttpServletRequest httpServletRequest = (HttpServletRequest) request;
		HttpSession session = httpServletRequest.getSession();
		String validateCode = (String) session.getAttribute("validateCode");
		// 获取用户输入的验证码
		String randomcode = httpServletRequest.getParameter("randomcode");
		if (randomcode != null && validateCode != null && !randomcode.equalsIgnoreCase(validateCode)) {
			// 如果校验失败,将验证码错误的失败信息,通过shiroLoginFailure设置到request域中,将来在登录Controller中获取
			httpServletRequest.setAttribute("shiroLoginFailure", "randomcodeError");
			// 返回true,拒绝访问,不再进行后面的用户名和密码的校验
			return true;
		}
		
		return super.onAccessDenied(request, response);
	}
	
}

修改LoginController.java:

 

 (2)配置自定义的formAuthenticationFilter,在applicationContext-shiro.xml中配置:

	<!-- 配置自定义的formAuthenticationFilter,如果不配置,username和password的名称将使用shiro的默认配置 -->
	<bean id="formAuthenticationFilter" class="cn.itcast.ssm.realm.CustomFormAuthenticationFilter">
		<!-- 表单中账号的input的name名称 -->
		<property name="usernameParam" value="username"/>
		<!-- 表单中密码的input的name名称 -->
		<property name="passwordParam" value="password"/>
	</bean>

 页面的验证码:

<TR>
	<TD>验证码:</TD>
	<TD><input id="randomcode" name="randomcode" size="8" /> <img
		id="randomcode_img" src="${baseurl}validatecode.jsp" alt=""
		width="56" height="20" align='absMiddle' /> <a
		href=javascript:randomcode_refresh()>刷新</a></TD>
</TR>

 启动测试:

发现这个验证码的jsp页面被shiro框架拦截了,在applicationContext-shiro.xml中配置静态资源:

/validatecode.jsp=anon

再一次测试:

注意:applicationContext-shiro.xml中的session的失效时长改成600000,否则按以前的5000即5秒失效就会测试不成功,因为session中刚放入的验证码由于5秒后失效了

导致验证码校验失败!

以上只有用户名和密码和验证码都正确的情况下才能进入系统,三者校验的顺序:验证码-->用户名-->密码

如果验证码都校验失败,那么就没有必要去校验用户名和密码了;

 需求12:记住我的实现

1.POJO类要实现序列化接口

2.在applicationContext-shiro.xml中配置cookie管理器

	<!-- 记住我cookie管理器 -->
	<bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
		<property name="cookie" ref="remenberMeCookie"/>
	</bean>
	<!-- 记住我cookie -->
	<bean id="remenberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
		<!-- remenberMe是cookie的名字 -->
		<constructor-arg value="remenberMe"/>
		<!-- 记住我的cookie生效时间,30天,单位:秒 -->
		<property name="maxAge" value="2592000"/>
	</bean>

将remenberMeManager注入到SecurityManager中

	<!-- 安全管理器 -->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<!-- 自定义Realm -->
		<property name="realm" ref="customRealm" />
		<!-- 缓存管理器 -->
		<property name="cacheManager" ref="cacheManager"/>
		<!-- 会话管理器 -->
		<property name="sessionManager" ref="sessionManager"/>
		<!-- 记住我 -->
		<property name="rememberMeManager" ref="rememberMeManager"/>
	</bean>

 3.写页面

<tr>
	<td></td>
	<td><input type="checkbox" name="remenberMe"/> 记住我</td>
</tr>

 4.在applicationContext-shiro.xml中的表单认证bean中注入rememberMe

	<!-- 配置自定义的formAuthenticationFilter,如果不配置,username和password的名称将使用shiro的默认配置 -->
	<bean id="formAuthenticationFilter" class="cn.itcast.ssm.realm.CustomFormAuthenticationFilter">
		<!-- 表单中账号的input的name名称 -->
		<property name="usernameParam" value="username"/>
		<!-- 表单中密码的input的name名称 -->
		<property name="passwordParam" value="password"/>
		<!-- 记住我, input的name名称 -->
		<property name="rememberMeParam" value="remenberMe"/>
	</bean>

 启动测试:没有问题

但是退出系统后,直接在浏览器地址栏输入某些url(记住我的url),应该直接跳转到那个url(无需登录),但是现在不行,解决方法:

使用UserFilter,将记住我即可访问的URL地址配置让UserFilter拦截。

 

posted @ 2017-07-23 20:56  半生戎马,共话桑麻、  阅读(195)  评论(0)    收藏  举报
levels of contents