(转)shiro权限框架详解06-shiro与web项目整合(下)
http://blog.csdn.net/facekbook/article/details/54962975
shiro和web项目整合,实现类似真实项目的应用
- web项目中认证
- web项目中授权
- shiro缓存
- sessionManager使用
- 验证码功能实现
- 记住我功能实现
web项目中认证
实现方式
修改CustomRealm
的 doGetAuthenticationInfo
方法,从数据库中获取用户信息,CustomRealm
返回查询到的用户信息,包括(加密后的密码字符串和salt以及上文中的菜单)。
修改 doGetAuthenticationInfo
方法
@Autowired
private SysService sysService;
/**
* 用于认证
*/
@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
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="md5" />
<!-- 设置hash散列次数 -->
<property name="hashIterations" value="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
方法
/**
* 用于授权
*/
@Override
protected 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="${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="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="account"/>
<!--表单中账号的password属性的值-->
<property name="passwordParam" value="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="/login.action" />
<!-- 认证成功后统一跳转到first.action,建议不配置,shiro认证成功自动到上一个链接 -->
<property name="successUrl" value="/first.action" />
<!-- 通过unauthorizedUrl指定没有权限时跳转页面 -->
<property name="unauthorizedUrl" value="/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="${baseurl}validatecode.jsp" alt="" width="56" height="20" align='absMiddle' /> <a href=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="rememberMe"/>
<property name="maxAge" value="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="account"/>
<!--表单中账号的password属性的值-->
<property name="passwordParam" value="accountPassword"/>
<!-- 修改记住我的name属性的值 -->
<property name="rememberMeParam" value="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="/login.action" />
<!-- 认证成功后统一跳转到first.action,建议不配置,shiro认证成功自动到上一个链接 -->
<property name="successUrl" value="/first.action" />
<!-- 通过unauthorizedUrl指定没有权限时跳转页面 -->
<property name="unauthorizedUrl" value="/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项目整合(下)的更多相关文章
- (转) shiro权限框架详解06-shiro与web项目整合(上)
http://blog.csdn.net/facekbook/article/details/54947730 shiro和web项目整合,实现类似真实项目的应用 本文中使用的项目架构是springM ...
- (转)shiro权限框架详解03-shiro介绍
http://blog.csdn.net/facekbook/article/details/54893740 shiro介绍 本文正式进入主题.本文将介绍如下内容: 什么是shiro 为什么需要学习 ...
- (转)shiro权限框架详解05-shiro授权
http://blog.csdn.net/facekbook/article/details/54910606 本文介绍 授权流程 授权方式 授权测试 自定义授权realm 授权流程 开始构造Secu ...
- (转)shiro权限框架详解02-权限理论介绍
http://blog.csdn.net/facekbook/article/details/54893042 权限管理解决方案 本文主要介绍权限管理的解决方法: 粗颗粒度和细颗粒度 基于url拦截 ...
- (转) shiro权限框架详解04-shiro认证
http://blog.csdn.net/facekbook/article/details/54906635 shiro认证 本文介绍shiro的认证功能 认证流程 入门程序(用户登录和退出) 自定 ...
- (转)shiro权限框架详解01-权限理论介绍
http://blog.csdn.net/facekbook/article/details/54890365 权限管理 本文介绍权限管理的理论和权限管理的一些名词. 介绍权限管理 理解身份认证和授权 ...
- Shiro 安全框架详解二(概念+权限案例实现)
Shiro 安全框架详解二 总结内容 一.登录认证 二.Shiro 授权 1. 概念 2. 授权流程图 三.基于 ini 的授权认证案例实现 1. 实现原理图 2. 实现代码 2.1 添加 maven ...
- Shiro 安全框架详解一(概念+登录案例实现)
shiro 安全框架详细教程 总结内容 一.RBAC 的概念 二.两种常用的权限管理框架 1. Apache Shiro 2. Spring Security 3. Shiro 和 Spring Se ...
- Shiro权限管理框架详解
1 权限管理1.1 什么是权限管理 基本上涉及到用户参与的系统都要进行权限管理,权限管理属于系统安全的范畴,权限管理实现对用户访问系统的控制,按照安全规则或者安全策略控制用户可以访问而且只能访问自己被 ...
随机推荐
- [jzoj 5776]【NOIP2008模拟】小x游世界树 (树形dp)
传送门 Description 小x得到了一个(不可靠的)小道消息,传说中的神岛阿瓦隆在格陵兰海的某处,据说那里埋藏着亚瑟王的宝藏,这引起了小x的好奇,但当他想前往阿瓦隆时发现那里只有圣诞节时才能到达 ...
- Vue CLI 3.x 简单体验
文档 中文文档 补充于02月10日 vue脚手架的3.x版本已经在开发中,现在还处于alpha版本.我们来看看有哪些变化. 使用 npm install -g @vue/cli 命名方式已经改为npm ...
- Problem 48
Problem 48 The series, 11 + 22 + 33 + ... + 1010 = 10405071317. Find the last ten digits of the seri ...
- 【习题 4-4 UVA - 253】Cube painting
[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 绕(x,y,z)三个轴旋转. 枚举x,y,z各4次的结果. (4次之后能还原.可以方便上一层枚举下一个情况.) [代码] #incl ...
- 洛谷 P2709 BZOJ 3781 小B的询问
题目描述 小B有一个序列,包含N个1~K之间的整数.他一共有M个询问,每个询问给定一个区间[L..R],求$\sum_1^Kc_i^2$的值,其中$c_i$表示数字i在[L..R]中的重复次数.小B请 ...
- hdu 1598 暴力+并查集
#include<stdio.h> #include<stdlib.h> #define N 300 int pre[N]; int find(int u) { if(u!=p ...
- 心急的C小加 贪心算法
心急的C小加 时间限制:1000 ms | 内存限制:65535 KB 难度:4 描述 C小加有一些木棒,它们的长度和质量都已经知道,需要一个机器处理这些木棒,机器开启的时候需要耗费一个单位的 ...
- JQuery学习(5-AJAX1)
<? php /* * 保护AJAX请求的方式 */ /* * 1. 防止表单的自己主动提交 * 对表单的submit提交进行控制. 设置一个全局变量submitError,在进行验证的方法体中 ...
- Go语言net/http 解读.
Http包提供实现HTTP客户端和服务端的方法与函数. Get.Head.Post.PostForm配合使用实现HTTP请求: resp, err := http.Get("http://e ...
- Openstack针对nova,cinder,glance使用ceph的虚拟机创建机制优化
今天在开源中国社区看到有例如以下一个问题: 已经成功把ceph作为cinder和 glance的后端,可是假设作为nova的后端,虚拟机启动速度非常慢,网上查了一下是由于openstack创建虚 ...