zoukankan      html  css  js  c++  java
  • Shiro与Spring、Springmvc的整合

    1、在web.xml中配置Shiro的filter

    在web系统中,shiro也通过filter进行拦截。filter拦截后将操作权交给spring中配置的filterChain(过虑链儿)
    shiro提供很多filter。

    <!-- shiro的filter -->
    <!-- shiro过虑器,DelegatingFilterProxy通过代理模式将spring容器中的bean和filter关联起来 -->
    <filter>
    	<filter-name>shiroFilter</filter-name>
    	<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    	<!-- 设置true由servlet容器控制filter的生命周期 -->
    	<init-param>
    		<param-name>targetFilterLifecycle</param-name>
    		<param-value>true</param-value>
    	</init-param>
    	<!-- 设置spring容器filter的bean id,如果不设置则找与filter-name一致的bean-->
    	<init-param>
    		<param-name>targetBeanName</param-name>
    		<param-value>shiroFilter</param-value>
    	</init-param>
    </filter>
    <filter-mapping>
    	<filter-name>shiroFilter</filter-name>
    	<url-pattern>/*</url-pattern>
    </filter-mapping>
    

    2、在applicationContext-shiro.xml 中配置web.xml中fitler对应spring容器中的bean。##

    <!-- web.xml中shiro的filter对应的bean -->
    <!-- Shiro 的Web过滤器 -->
    	<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    		<property name="securityManager" ref="securityManager" />
    		<!-- loginUrl认证提交地址,如果没有认证将会请求此地址进行认证,请求此地址将由formAuthenticationFilter进行表单认证 -->
    		<property name="loginUrl" value="/login.action" />
    		<!-- 认证成功统一跳转到first.action,建议不配置,shiro认证成功自动到上一个请求路径 -->
    		<property name="successUrl" value="/first.action"/>
    		<!-- 通过unauthorizedUrl指定没有权限操作时跳转页面-->
    		<property name="unauthorizedUrl" value="/refuse.jsp" />
    		<!-- 自定义filter配置 -->
    		<property name="filters">
    			<map>
    				<!-- 将自定义 的FormAuthenticationFilter注入shiroFilter中-->
    				<entry key="authc" value-ref="formAuthenticationFilter" />
    			</map>
    		</property>
    		
    		<!-- 过虑器链定义,从上向下顺序执行,一般将/**放在最下边 -->
    		<property name="filterChainDefinitions">
    			<value>
    			</value>
    		</property>
    	</bean>
    
    <!-- securityManager安全管理器 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
    		<property name="realm" ref="customRealm" />	
    	</bean>
    
    <!-- realm -->
    <bean id="customRealm" class="cn.itcast.ssm.shiro.CustomRealm">
    	<!-- 将凭证匹配器设置到realm中,realm按照凭证匹配器的要求进行散列 -->
    	<property name="credentialsMatcher" ref="credentialsMatcher"/>
    </bean>
    

    3、配置静态资源的匿名访问

    <!-- 过虑器链定义,从上向下顺序执行,一般将/**放在最下边 -->
    		<property name="filterChainDefinitions">
    			<value>
    <!-- 对静态资源设置匿名访问 -->
    			/images/** = anon
    			/js/** = anon
    			/styles/** = anon
    			</value>
    		</property>
    

    4、登录认证的实现

    一、原理

    使用FormAuthenticationFilter过虑器实现 ,原理如下:


     将用户没有认证时,请求loginurl进行认证,用户身份和用户密码提交数据到loginurl

     FormAuthenticationFilter拦截住取出request中的username和password(两个参数名称是可以配置的)

     FormAuthenticationFilter调用realm传入一个token(username和password)

     realm认证时根据username查询用户信息(在Activeuser中存储,包括 userid、usercode、username、menus)。

     如果查询不到,realm返回null,FormAuthenticationFilter向request域中填充一个参数(记录了异常信息)

    二、代码实现

    //登陆提交地址,和applicationContext-shiro.xml中配置的loginurl一致
    	@RequestMapping("login")
    	public String login(HttpServletRequest request)throws Exception{
    		
    		//如果登陆失败从request中获取认证异常信息,shiroLoginFailure就是shiro异常类的全限定名
    		String exceptionClassName = (String) request.getAttribute("shiroLoginFailure");
    		//根据shiro返回的异常类路径判断,抛出指定异常信息
    		if(exceptionClassName!=null){
    			if (UnknownAccountException.class.getName().equals(exceptionClassName)) {
    				//最终会抛给异常处理器
    				throw new CustomException("账号不存在");
    			} else if (IncorrectCredentialsException.class.getName().equals(
    					exceptionClassName)) {
    				throw new CustomException("用户名/密码错误");
    			} else if("randomCodeError".equals(exceptionClassName)){
    				throw new CustomException("验证码错误 ");
    			}else {
    				throw new Exception();//最终在异常处理器生成未知错误
    			}
    		}
    		//此方法不处理登陆成功(认证成功),shiro认证成功会自动跳转到上一个请求路径
    		//登陆失败还到login页面
    		return "login";
    	}	
    

    三、配置登录的拦截器

    <!-- 过虑器链定义,从上向下顺序执行,一般将/**放在最下边 -->
    	<property name="filterChainDefinitions">
    		<value>
    			<!-- 对静态资源设置匿名访问 -->
    			/images/** = anon
    			/js/** = anon
    			/styles/** = anon
    			<!-- /** = authc 所有url都必须认证通过才可以访问-->
    			/** = authc
    			<!-- /** = anon所有url都可以匿名访问 -->
    			
    		</value>
    

    四、退出

    不用我们去实现退出,只要去访问一个退出的url(该 url是可以不存在),由LogoutFilter拦截住,清除session。

    在applicationContext-shiro.xml配置LogoutFilter

    <property name="filterChainDefinitions">
    		<value>
    			<!-- 对静态资源设置匿名访问 -->
    			/images/** = anon
    			/js/** = anon
    			/styles/** = anon
    			<!-- /** = authc 所有url都必须认证通过才可以访问-->
    			/** = authc
    			<!-- /** = anon所有url都可以匿名访问 -->
    			<!-- 请求 logout.action地址,shiro去清除session-->
    			/logout.action = logout
    		</value>
    

    五、编写认证的realm

    //realm的认证方法,从数据库查询用户信息
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
    		AuthenticationToken token) throws AuthenticationException {
    	
    	// token是用户输入的用户名和密码 
    	// 第一步从token中取出用户名
    	String userCode = (String) token.getPrincipal();
    
    	// 第二步:根据用户输入的userCode从数据库查询
    	SysUser sysUser = null;
    	try {
    		sysUser = sysService.findSysUserByUserCode(userCode);
    	} catch (Exception e1) {
    		// TODO Auto-generated catch block
    		e1.printStackTrace();
    	}
    
    	// 如果查询不到返回null
    	if(sysUser==null){//
    		return null;
    	}
    	// 从数据库查询到密码
    	String password = sysUser.getPassword();
    	
    	//盐
    	String salt = sysUser.getSalt();
    
    	// 如果查询到返回认证信息AuthenticationInfo
    	
    	//activeUser就是用户身份信息
    	ActiveUser activeUser = new ActiveUser();
    	
    	activeUser.setUserid(sysUser.getId());
    	activeUser.setUsercode(sysUser.getUsercode());
    	activeUser.setUsername(sysUser.getUsername());
    	//..
    	
    	//根据用户id取出菜单
    	List<SysPermission> menus  = null;
    	try {
    		//通过service取出菜单 
    		menus = sysService.findMenuListByUserId(sysUser.getId());
    	} catch (Exception e) {
    		// TODO Auto-generated catch block
    		e.printStackTrace();
    	}
    	//将用户菜单 设置到activeUser
    	activeUser.setMenus(menus);
    
    	//将activeUser设置simpleAuthenticationInfo
    	SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
    			activeUser, password,ByteSource.Util.bytes(salt), this.getName());
    
    	return simpleAuthenticationInfo;
    }
    

    六、设置凭证匹配器###

    数据库中存储到的md5的散列值,在realm中需要设置数据库中的散列值它使用散列算法 及散列次数,让shiro进行散列对比时和原始数据库中的散列值使用的算法 一致。

     <!-- 凭证匹配器 -->
    <bean id="credentialsMatcher"
    	class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
    	<property name="hashAlgorithmName" value="md5" />
    	<property name="hashIterations" value="1" />
    </bean>
    

    七、认证通过的Controller

    //系统首页
    @RequestMapping("/first")
    public String first(Model model)throws Exception{
    	
    	//从shiro的session中取activeUser
    	Subject subject = SecurityUtils.getSubject();
    	//取身份信息
    	ActiveUser activeUser = (ActiveUser) subject.getPrincipal();
    	//通过model传到页面
    	model.addAttribute("activeUser", activeUser);
    	
    	return "/first";
    }
    

    5、授权的实现

    一、授权流程

    在applicationContext-shiro.xml中配置url所对应的权限。

    授权流程:
    1、在applicationContext-shiro.xml中配置filter规则

    /items/queryItems.action = perms[item:query]
    2、用户在认证通过后,请求/items/queryItems.action
    3、被PermissionsAuthorizationFilter拦截,发现需要“item:query”权限
    4、PermissionsAuthorizationFilter调用realm中的doGetAuthorizationInfo获取数据库中正确的权限
    5、PermissionsAuthorizationFilter对item:query 和从realm中获取权限进行对比,如果“item:query”在realm返回的权限列表中,授权通过。

    二、配置授权不通过的拒绝页面

    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    		<property name="securityManager" ref="securityManager" />
    		<!-- loginUrl认证提交地址,如果没有认证将会请求此地址进行认证,请求此地址将由formAuthenticationFilter进行表单认证 -->
    		<property name="loginUrl" value="/login.action" />
    		<!-- 认证成功统一跳转到first.action,建议不配置,shiro认证成功自动到上一个请求路径 -->
    		<property name="successUrl" value="/first.action"/>
    		<!-- 通过unauthorizedUrl指定没有权限操作时跳转页面-->
    		<property name="unauthorizedUrl" value="/refuse.jsp" />
    

    三、此种授权方式存在的问题

    1、在applicationContext-shiro.xml中配置过虑器链接,需要将全部的url和权限对应起来进行配置,比较发麻不方便使用。

    2、每次授权都需要调用realm查询数据库,对于系统性能有很大影响,可以通过shiro缓存来解决

    四、Shiro的过滤器

    过滤器简称     对应的java类
     Anon     org.apache.shiro.web.filter.authc.AnonymousFilter
     Authc     org.apache.shiro.web.filter.authc.FormAuthenticationFilter
     authcBasic     org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
     perms     org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
     port     org.apache.shiro.web.filter.authz.PortFilter
     rest     org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
     roles     org.apache.shiro.web.filter.authz.RolesAuthorizationFilter
     ssl     org.apache.shiro.web.filter.authz.SslFilter
     user     org.apache.shiro.web.filter.authc.UserFilter
     logout     org.apache.shiro.web.filter.authc.LogoutFilter

    anon:例子/admins/**=anon 没有参数,表示可以匿名使用。
    authc:例如/admins/user/**=authc表示需要认证(登录)才能使用,FormAuthenticationFilter是表单认证,没有参数
    perms:例子/admins/user/**=perms[user:add:*],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/**=perms ["user:add:*,user:modify:*"],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。
    user:例如/admins/user/**=user没有参数表示必须存在用户, 身份认证通过或通过记住我认证通过的可以访问,当登入操作时不做检查

    五、授权realm的实现

    // 用于授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(
    		PrincipalCollection principals) {
    	
    	//从 principals获取主身份信息
    	//将getPrimaryPrincipal方法返回值转为真实身份类型(在上边的doGetAuthenticationInfo认证通过填充到SimpleAuthenticationInfo中身份类型),
    	ActiveUser activeUser =  (ActiveUser) principals.getPrimaryPrincipal();
    	
    	//根据身份信息获取权限信息
    	//从数据库获取到权限数据
    	List<SysPermission> permissionList = null;
    	try {
    		permissionList = sysService.findPermissionListByUserId(activeUser.getUserid());
    	} catch (Exception e) {
    		// TODO Auto-generated catch block
    		e.printStackTrace();
    	}
    	//单独定一个集合对象 
    	List<String> permissions = new ArrayList<String>();
    	if(permissionList!=null){
    		for(SysPermission sysPermission:permissionList){
    			//将数据库中的权限标签 符放入集合
    			permissions.add(sysPermission.getPercode());
    		}
    	}
    	
    	
    /*	List<String> permissions = new ArrayList<String>();
    	permissions.add("user:create");//用户的创建
    	permissions.add("item:query");//商品查询权限
    	permissions.add("item:add");//商品添加权限
    	permissions.add("item:edit");//商品修改权限**/
    	//查到权限数据,返回授权信息(要包括 上边的permissions)
    	SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
    	//将上边查询到授权信息填充到simpleAuthorizationInfo对象中
    	simpleAuthorizationInfo.addStringPermissions(permissions);
    
    	return simpleAuthorizationInfo;
    }
    

    六、开启controller的aop支持

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

    七、在controller中添加注解完成授权

    //商品信息方法
    	@RequestMapping("/queryItems")
    	@RequiresPermissions("item:query")//执行queryItems需要"item:query"权限
    	public ModelAndView queryItems(HttpServletRequest request) throws Exception {
    		
    		System.out.println(request.getParameter("id"));
    	
    		//调用service查询商品列表
    		List<ItemsCustom> itemsList = itemsService.findItemsList(null);
    
    		ModelAndView modelAndView = new ModelAndView();
    		modelAndView.addObject("itemsList", itemsList);
    		// 指定逻辑视图名
    		modelAndView.setViewName("itemsList");
    
    		return modelAndView;
    	}	
    

    八、jsp页面通过标签授权

    Jsp页面添加:
    <%@ tagliburi="http://shiro.apache.org/tags" prefix="shiro" %>

    标签名称     标签条件(均是显示标签内容)
    shiro:authenticated 登录之后
    shiro:notAuthenticated 不在登录状态时
    shiro:guest 用户在没有RememberMe时
    shiro:user 用户在RememberMe时
    <shiro:hasAnyRoles name="abc,123" > 在有abc或者123角色时
    <shiro:hasRole name="abc"> 拥有角色abc
    <shiro:lacksRole name="abc"> 没有角色abc
    <shiro:hasPermission name="abc"> 拥有权限资源abc
    <shiro:lacksPermission name="abc"> 没有abc权限资源
    shiro:principal 显示用户身份名称
    <shiro:principal property="username"/> 显示用户身份中的属性值
    jsp页面实例:

    <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>
    

    6、配置session管理器

    和shiro整合后,使用shiro的session管理,shiro提供sessionDao操作 会话数据。

    配置sessionManager

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

    7、shiro缓存

    一、缓存流程

    shiro中提供了对认证信息和授权信息的缓存。shiro默认是关闭认证信息缓存的,对于授权信息的缓存shiro默认开启的。主要研究授权信息缓存,因为授权的数据量大。

    用户认证通过。
    该 用户第一次授权:调用realm查询数据库
    该 用户第二次授权:不调用realm查询数据库,直接从缓存中取出授权信息(权限标识符)。

    二、配置缓存管理器

    使用ehcache进行缓存管理。

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

    三、编写ehcache的配置文件

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

    四、缓存清空

    如果用户正常退出,缓存自动清空。

    如果用户非正常退出,缓存自动清空。

    如果修改了用户的权限,而用户不退出系统,修改的权限无法立即生效。
    需要手动进行编程实现:
    在权限修改后调用realm的clearCache方法清除缓存。
    下边的代码正常开发时要放在service中调用。
    在service中,权限修改后调用realm的方法。

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

    编写controller

    //注入realm
    @Autowired
    private CustomRealm customRealm;
    
    @RequestMapping("/clearShiroCache")
    public String clearShiroCache(){
    	
    	//清除缓存,将来正常开发要在service调用customRealm.clearCached()
    	customRealm.clearCached();
    	
    	return "success";
    }
    

    8、验证码的实现

    一、验证码的生成

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <%@ page import="java.util.Random"%>
    <%@ page import="java.io.OutputStream"%>
    <%@ page import="java.awt.Color"%>
    <%@ page import="java.awt.Font"%>
    <%@ page import="java.awt.Graphics"%>
    <%@ page import="java.awt.image.BufferedImage"%>
    <%@ page import="javax.imageio.ImageIO"%>
    <%
    	int width = 60;
    	int height = 32;
    	//create the image
    	BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    	Graphics g = image.getGraphics();
    	// set the background color
    	g.setColor(new Color(0xDCDCDC));
    	g.fillRect(0, 0, width, height);
    	// draw the border
    	g.setColor(Color.black);
    	g.drawRect(0, 0, width - 1, height - 1);
    	// create a random instance to generate the codes
    	Random rdm = new Random();
    	String hash1 = Integer.toHexString(rdm.nextInt());
    	System.out.print(hash1);
    	// make some confusion
    	for (int i = 0; i < 50; i++) {
    		int x = rdm.nextInt(width);
    		int y = rdm.nextInt(height);
    		g.drawOval(x, y, 0, 0);
    	}
    	// generate a random code
    	String capstr = hash1.substring(0, 4);
    	//将生成的验证码存入session
    	session.setAttribute("validateCode", capstr);
    	g.setColor(new Color(0, 100, 0));
    	g.setFont(new Font("Candara", Font.BOLD, 24));
    	g.drawString(capstr, 8, 24);
    	g.dispose();
    	//输出图片
    	response.setContentType("image/jpeg");
    	out.clear();
    	out = pageContext.pushBody();
    	OutputStream strm = response.getOutputStream();
    	ImageIO.write(image, "jpeg", strm);
    	strm.close();
    %>
    

    二、在登录页面加入验证码

    <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>
    

    三、自定义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();
    		//取出session的验证码(正确的验证码)
    		String validateCode = (String) session.getAttribute("validateCode");
    		
    		//取出页面的验证码
    		//输入的验证和session中的验证进行对比 
    		String randomcode = httpServletRequest.getParameter("randomcode");
    		if(randomcode!=null && validateCode!=null && !randomcode.equals(validateCode)){
    			//如果校验失败,将验证码错误失败信息,通过shiroLoginFailure设置到request中
    			httpServletRequest.setAttribute("shiroLoginFailure", "randomCodeError");
    			//拒绝访问,不再校验账号和密码 
    			return true; 
    		}
    		return super.onAccessDenied(request, response);
    	}
    
    		
    }
    

    四、配置自定义FormAuthenticationFilter过滤器

    <!-- Shiro 的Web过滤器 -->
    	<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    		<property name="securityManager" ref="securityManager" />
    		<!-- loginUrl认证提交地址,如果没有认证将会请求此地址进行认证,请求此地址将由formAuthenticationFilter进行表单认证 -->
    		<property name="loginUrl" value="/login.action" />
    		<!-- 认证成功统一跳转到first.action,建议不配置,shiro认证成功自动到上一个请求路径 -->
    		<property name="successUrl" value="/first.action"/>
    		<!-- 通过unauthorizedUrl指定没有权限操作时跳转页面-->
    		<property name="unauthorizedUrl" value="/refuse.jsp" />
    		<!-- 自定义filter配置 -->
    		<property name="filters">
    			<map>
    				<!-- 将自定义 的FormAuthenticationFilter注入shiroFilter中-->
    				<entry key="authc" value-ref="formAuthenticationFilter" />
    			</map>
    		</property>
    
    <!-- 自定义form认证过虑器 -->
    <!-- 基于Form表单的身份验证过滤器,不配置将也会注册此过虑器,表单中的用户账号、密码及loginurl将采用默认值,建议配置 -->
    	<bean id="formAuthenticationFilter" 
    	class="cn.itcast.ssm.shiro.CustomFormAuthenticationFilter ">
    		<!-- 表单中账号的input名称 -->
    		<property name="usernameParam" value="username" />
    		<!-- 表单中密码的input名称 -->
    		<property name="passwordParam" value="password" />
     </bean>
    

    五、在login.action对验证错误 进行解析

    public String login(HttpServletRequest request)throws Exception{
    		
    		//如果登陆失败从request中获取认证异常信息,shiroLoginFailure就是shiro异常类的全限定名
    		String exceptionClassName = (String) request.getAttribute("shiroLoginFailure");
    		//根据shiro返回的异常类路径判断,抛出指定异常信息
    		if(exceptionClassName!=null){
    			if (UnknownAccountException.class.getName().equals(exceptionClassName)) {
    				//最终会抛给异常处理器
    				throw new CustomException("账号不存在");
    			} else if (IncorrectCredentialsException.class.getName().equals(
    					exceptionClassName)) {
    				throw new CustomException("用户名/密码错误");
    			} else if("randomCodeError".equals(exceptionClassName)){
    				throw new CustomException("验证码错误 ");
    			}else {
    				throw new Exception();//最终在异常处理器生成未知错误
    			}
    		}
    		//此方法不处理登陆成功(认证成功),shiro认证成功会自动跳转到上一个请求路径
    		//登陆失败还到login页面
    		return "login";
    	}		
    

    六、在filter配置匿名访问验证码jsp

    <!-- 过虑器链定义,从上向下顺序执行,一般将/**放在最下边 -->
    	<property name="filterChainDefinitions">
    		<value>
    			<!-- 对静态资源设置匿名访问 -->
    			/images/** = anon
    			/js/** = anon
    			/styles/** = anon
    			<!-- 验证码,可匿名访问 -->
    			/validatecode.jsp = anon
    

    9、记住我实现

    一、 用户身份实现java.io.Serializable接口

    二、配置rememeberMeManager

    <!-- rememberMeManager管理器,写cookie,取出cookie生成用户信息 -->
    	<bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
    		<property name="cookie" ref="rememberMeCookie" />
    	</bean>
    	<!-- 记住我cookie -->
    	<bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
    		<!-- rememberMe是cookie的名字 -->
    		<constructor-arg value="rememberMe" />
    		<!-- 记住我cookie生效时间30天 -->
    		<property name="maxAge" value="2592000" />
    	</bean>
    
    <!-- securityManager安全管理器 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
    		<property name="realm" ref="customRealm" />
    		<!-- 注入缓存管理器 -->
    		<property name="cacheManager" ref="cacheManager"/>
    		<!-- 注入session管理器 -->
    		<property name="sessionManager" ref="sessionManager" />
    		<!-- 记住我 -->
    		<property name="rememberMeManager" ref="rememberMeManager"/>
    		
    	</bean>
    

    三、在登录页面加入记住我选项

    <tr>
    		<TD></TD>
    		<td><input type="checkbox" name="rememberMe" />自动登陆</td>
    </tr>
    

    四、配置rememberMe的input名称

    <!-- 自定义form认证过虑器 -->
    <!-- 基于Form表单的身份验证过滤器,不配置将也会注册此过虑器,表单中的用户账号、密码及loginurl将采用默认值,建议配置 -->
    	<bean id="formAuthenticationFilter" 
    	class="cn.itcast.ssm.shiro.CustomFormAuthenticationFilter ">
    		<!-- 表单中账号的input名称 -->
    		<property name="usernameParam" value="username" />
    		<!-- 表单中密码的input名称 -->
    		<property name="passwordParam" value="password" />
    		<!-- 记住我input的名称 -->
    		<property name="rememberMeParam" value="rememberMe"/>
     </bean>
    

    五、使用UserFilter

    如果设置记住我,下次访问某些url时可以不用登陆。将记住我即可访问的地址配置让UserFilter拦截。

    	<!-- 过虑器链定义,从上向下顺序执行,一般将/**放在最下边 -->
    	<property name="filterChainDefinitions">
    		<value>
    			<!-- 对静态资源设置匿名访问 -->
    			/images/** = anon
    			/js/** = anon
    			/styles/** = anon
    			<!-- 验证码,可匿名访问 -->
    			/validatecode.jsp = anon
    			
    			<!-- 请求 logout.action地址,shiro去清除session-->
    			/logout.action = logout
    			<!--商品查询需要商品查询权限 ,取消url拦截配置,使用注解授权方式 -->
    			<!-- /items/queryItems.action = perms[item:query]
    			/items/editItems.action = perms[item:edit] -->
    			<!-- 配置记住我或认证通过可以访问的地址 -->
    			/index.jsp  = user
    			/first.action = user
    			/welcome.jsp = user
    			<!-- /** = authc 所有url都必须认证通过才可以访问-->
    			/** = authc
    			<!-- /** = anon所有url都可以匿名访问 -->
    			
    		</value>
    	</property>
  • 相关阅读:
    理解OpenShift(5):从 Docker Volume 到 OpenShift Persistent Volume
    理解OpenShift(4):用户及权限管理
    理解OpenShift(3):网络之 SDN
    理解OpenShift(2):网络之 DNS(域名服务)
    理解OpenShift(1):网络之 Router 和 Route
    HTML盒子模型
    架构系统的雪崩理解
    C++11 lambda表达式学习
    C++11 std::shared_ptr总结与使用
    Kafka学习笔记
  • 原文地址:https://www.cnblogs.com/jack1995/p/7450401.html
Copyright © 2011-2022 走看看