首页 > 代码库 > (转)shiro权限框架详解06-shiro与web项目整合(下)
(转)shiro权限框架详解06-shiro与web项目整合(下)
http://blog.csdn.net/facekbook/article/details/54962975
shiro和web项目整合,实现类似真实项目的应用
- web项目中认证
- web项目中授权
- shiro缓存
- sessionManager使用
- 验证码功能实现
- 记住我功能实现
web项目中认证
实现方式
修改CustomRealm
的 doGetAuthenticationInfo
方法,从数据库中获取用户信息,CustomRealm
返回查询到的用户信息,包括(加密后的密码字符串和salt以及上文中的菜单)。
修改 doGetAuthenticationInfo
方法
@Autowiredprivate SysService sysService;/** * 用于认证*/@Overrideprotected 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 if(sysUser==null){ return null; } //第二步:通过获取的身份信息进行数据库查询 String password = sysUser.getPassword(); //组装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.getUsercode()); } catch (Exception e) { e.printStackTrace(); } activeUser.setMenus(menus); //得到盐 String salt = sysUser.getSalt(); //如果查询到结果返回AuthenticationInfo AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(activeUser, password,ByteSource.Util.bytes(salt), ""); return authenticationInfo;}
- 1
设置凭证匹配器
在我们的数据库存储的是MD5散列值,在自定义的realm中需要自定义设置散列算法以及散列次数。这里和前面介绍的散列认证的配置方式类似。
<!-- 自定义的realm --><bean id="customRealm" class="cn.itcast.ssm.shiro.CustomRealm"><!--将匹配器设置到realm中 --> <property name="credentialsMatcher" ref="credentialsMatcher" /></bean><!--定义凭证匹配器 --><bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"><!-- 设置hash散列算法 --> <property name="hashAlgorithmName" value=http://www.mamicode.com/"md5" /> <!-- 设置hash散列次数 --> <property name="hashIterations" value=http://www.mamicode.com/"1" /></bean>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
验证认证功能
数据库存在两条用户数据,具体如下:
其中:张三 的密码是 111111。当然也可以自己修改密码:
SELECT MD5(‘密码‘+‘盐‘)
- 1
- 1
如果可能正常登录则没有问题。
授权
实现方式
修改 CustomRealm
中的 doGetAuthorizationInfo
方法从数据库中查询授权信息。
这里讲解注解式授权和jsp标签授权方法。
修改 doGetAuthorizationInfo
方法
/** * 用于授权 */@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { //获取身份信息,这个字段是在认证通过后返回的,也就是通过执行认证方法返回的AuthenticationInfo类的第一个属性 ActiveUser activeUser = (ActiveUser) principals.getPrimaryPrincipal(); //通过userId查询数据库获取该身份信息的所有权限。 List<SysPermission> permissionList = null; try { permissionList = sysService.findPermissionListByUserId(activeUser.getUserid()); } catch (Exception e) { e.printStackTrace(); } List<String> permissions = new ArrayList<>(); if(permissionList!=null){ for(SysPermission p:permissionList){ permissions.add(p.getPercode()); } } //查询到权限信息,然后返回权限信息 SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); //将查询到的授权信息填充到SimpleAuthorizationInfo中 simpleAuthorizationInfo.addStringPermissions(permissions); return simpleAuthorizationInfo;}
- 1
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>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
在ItemsController类方法上添加注解
//商品信息方法@RequestMapping("/queryItems")@RequiresPermissions("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;}
- 1
jsp标签授权
在jsp页面添加shiro taglib
<%@ taglib uri="http://shiro.apache.org/tags" prefix="shiro" %>
- 1
- 1
shiro包括的jsp标签
标签名称 | 标签条件(均显示标签内容) |
---|---|
<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"/> | 显示用户身份中的属性值 |
修改itemsList.jsp文件
<!-- 具有item:update权限才显示修改链接,没有的话不显示。相当于if(hasPermission(item:update)) --><shiro:hasPermission name="item:update"> <a href=http://www.mamicode.com/"${pageContext.request.contextPath }/items/editItems.action?id=${item.id}">修改</a></shiro:hasPermission>
授权测试
当调用controller的一个方法时,由于该方法加了@RequiresPermissions("item:query")
注解,shiro会调用realm获取数据库中的权限信息,看 item:query
是否在权限数据中存在,如果不存在就拒绝访问,如果存在就授权通过
当展现一个jsp页面时,页面中如果遇到 <shiro:hasPermission name="item:update">
标签,shiro调用realm获取数据库中的权限信息,看item:update
是否在权限数据中存在,如果不存在就不显示标签包含内容,如果存在则显示。
在这里只要遇到注解或shiro jsp标签授权,就会调用realm查询数据库,在这里需要引入缓存解决。
shiro缓存
针对授权时频繁查询数据库的问题,引入shiro缓存。
缓存流程
用户认证通过。
用户第一次授权:调用realm查询数据库。
用户第二次授权:不调用realm查询数据库,直接从缓存中读取授权信息。
使用 ehcache
添加Ehcache的jar包
配置Ehcache配置文件:
新建shiro-ehcache.xml
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd"> <!--diskStore:缓存数据持久化的目录 地址 --> <diskStore path="E:\develop\ehcache" /> <defaultCache maxElementsInMemory="1000" maxElementsOnDisk="10000000" eternal="false" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="120" timeToLiveSeconds="120" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"> </defaultCache></ehcache>
- 1
配置cacheManager
<!--securityManager安全管理器 --><bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="customRealm" /> <property name="cacheManager" ref="cacheManager"/></bean><!-- 定义shiro缓存管理器 --><bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> <property name="cacheManagerConfigFile" value=http://www.mamicode.com/"classpath:shiro-ehcache.xml"/></bean>
- 1
清空缓存
当用户权限修改后,用户再次登录shiro会自动调用realm从数据库获取权限数据,如果在修改权限后想立即清除缓存则可以调用realm的clearCache方法清除。
在CustomRealm
中定义clearCached方法:
/** * 清除缓存方法 */ public void clearCache(){ PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals(); super.clearCache(principals); }
- 1
验证码功能实现
实现方式
shiro使用FormAuthenticationFilter进行表单认证,验证码校验的功能应该加在FormAuthenticationFilter中,在认证之前进行验证码校验。
自定义FormAuthenticationFilter
public class CustomFormAuthenticationFilter extends FormAuthenticationFilter{ /** * 原AuthenticationFilter验证方法 */ @Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { //获取正确的验证码和用户输入的验证码进行比对 HttpServletRequest httpServletRequest = (HttpServletRequest)request; HttpSession session = httpServletRequest.getSession(); //从session获取正确验证码 String validateCode = (String) session.getAttribute("validateCode"); //取出页面的验证码 String randomcode = (String) httpServletRequest.getParameter("randomcode"); if(validateCode!=null && randomcode!=null && !validateCode.equals(randomcode)){ //验证码不相同,给shiroLoginFailure属性设置值 request.setAttribute("shiroLoginFailure","randomcodeError"); //拒绝访问,不再校验账号和密码 return true; } return super.onAccessDenied(request, response); }}
- 1
配置自定义的FormAuthenticationFilter
在applicationContext-shiro.xml文件中配置
<!-- 自定义form认证过滤器 --> <bean id="formAuthenticationFilter" class="cn.itcast.ssm.shiro.CustomFormAuthenticationFilter"> <!--表单中账号的name属性的值--> <property name="usernameParam" value=http://www.mamicode.com/"account"/> <!--表单中账号的password属性的值--> <property name="passwordParam" value=http://www.mamicode.com/"accountPassword"/> </bean>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
修改 shiroFilter
配置
<!-- web.xml中shiro的filter对应的bean --> <!-- shiro的web过滤器 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager" /> <!-- logiUrl认证提交地址,如果没有认证通过将会请求此地址进行认证,请求此地址将由formAuthenticationFilter进行表单认证 --> <property name="loginUrl" value=http://www.mamicode.com/"/login.action" /> <!-- 认证成功后统一跳转到first.action,建议不配置,shiro认证成功自动到上一个链接 --> <property name="successUrl" value=http://www.mamicode.com/"/first.action" /> <!-- 通过unauthorizedUrl指定没有权限时跳转页面 --> <property name="unauthorizedUrl" value=http://www.mamicode.com/"/refuse.jsp" /> <!-- 自定义filter配置 --> <property name="filters"> <map> <entry key="authc" value-ref="formAuthenticationFilter"/> </map> </property>
- 1
修改 LoginController
的 login
方法
@RequestMapping("/login") public String login(HttpServletRequest request)throws Exception{ //如果登录失败从request中获取认证异常信息,shiroLoginFailure就是shiro异常类的全限定名 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 if("randomcodeError".equals(exceptionClassName)){ throw new CustomException("验证码错误"); }else{ throw new Exception();//最终在异常处理器生成未知错误 } } //此方法不处理登录成功(认证成功),shiro认证成功会自动跳转到上一个请求路径。 //登录失败还到login页面 return "login"; }
- 1
在登录页面添加验证码
<TR> <TD>用户名:</TD> <TD colSpan="2"><input type="text" id="usercode" name="account" style="WIDTH: 130px" /> </TD> </TR> <TR> <TD>密 码:</TD> <TD><input type="password" id="pwd" name="accountPassword" style="WIDTH: 130px" /></TD> </TR> <TR> <TD>验证码:</TD> <TD><input id="randomcode" name="randomcode" size="8" /> <img id="randomcode_img" src=http://www.mamicode.com/"${baseurl}validatecode.jsp" alt="" width="56" height="20" align=‘absMiddle‘ /> <a href=http://www.mamicode.com/javascript:randomcode_refresh()>刷新</a></TD> </TR>
- 1
实现记住我功能
用户登录选择”记住我”选项,本次登录成功会向cookie写身份信息,下次登录从cookie中取出身份信息实现自动登录。
用户身份信息相关类实现 java.io.Serializable
接口
向cookie记录身份信息的对象需要实现序列号接口,如下:
public class ActiveUser implements java.io.Serializable {public class SysPermission implements java.io.Serializable{
配置 rememberMeManager
<!--配置记住我cookie--><bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie"> <!-- rememerMe是cookie名称 --> <constructor-arg value=http://www.mamicode.com/"rememberMe"/> <property name="maxAge" value=http://www.mamicode.com/"2592000"/></bean><!-- rememberMeManager管理器,写cookie,取出cookie生成用户信息 --><bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager"> <property name="cookie" ref="rememberMeCookie"/></bean>
添加到securityManager中
<!--securityManager安全管理器 --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="customRealm" /> <property name="cacheManager" ref="cacheManager"/> <!-- 记住我 --> <property name="rememberMeManager" ref="rememberMeManager"/> </bean>
- 1
修改登录页面
<TR> <TD></TD> <TD><input type="checkbox" name="rememberMe">记住我</TD></TR>
修改rememberMe的input名称
在前面的配置中修改了账号和密码的input的name属性,”记住我”的name属性值也可以修改
<!-- 自定义form认证过滤器 --> <bean id="formAuthenticationFilter" class="cn.itcast.ssm.shiro.CustomFormAuthenticationFilter"> <!--表单中账号的name属性的值--> <property name="usernameParam" value=http://www.mamicode.com/"account"/> <!--表单中账号的password属性的值--> <property name="passwordParam" value=http://www.mamicode.com/"accountPassword"/> <!-- 修改记住我的name属性的值 --> <property name="rememberMeParam" value=http://www.mamicode.com/"rememberMe"/> </bean>
- 1
测试记住我功能
选择自动登录后,需要查看cookie是否有rememberMe
使用UserFilter
在前一篇博客中有说明UserFilter的功能如下:
user:例如/admins/user/**=user没有参数表示必须存在用户, 身份认证通过或通过记住我认证通过的可以访问,当登入操作时不做检查
我们修改applicationContext-shiro.xml配置文件
<!-- shiro的web过滤器 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager" /> <!-- logiUrl认证提交地址,如果没有认证通过将会请求此地址进行认证,请求此地址将由formAuthenticationFilter进行表单认证 --> <property name="loginUrl" value=http://www.mamicode.com/"/login.action" /> <!-- 认证成功后统一跳转到first.action,建议不配置,shiro认证成功自动到上一个链接 --> <property name="successUrl" value=http://www.mamicode.com/"/first.action" /> <!-- 通过unauthorizedUrl指定没有权限时跳转页面 --> <property name="unauthorizedUrl" value=http://www.mamicode.com/"/refuse.jsp" /> <!-- 自定义filter配置 --> <property name="filters"> <map> <entry key="authc" value-ref="formAuthenticationFilter"/> </map> </property> <!-- 过滤器链定义,从上向下顺序执行,一般将/**放在最后面 --> <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:update] --> <!-- 配置记住我或认证通过可以访问地址 --> /index.jsp = user /first.action = user /welcome.jsp = user <!-- /**=authc 表示所有的url都需要认证才能访问 --> /** = authc </value> </property> </bean>
blog项目的下载地址
点击进入下载页面
(转)shiro权限框架详解06-shiro与web项目整合(下)