第十六章 综合实例——《跟我学Shiro》
目录贴: 跟我学Shiro目录贴
简单的实体关系图
简单数据字典
用户(sys_user)
名称 |
类型 |
长度 |
描述 |
id |
bigint |
编号 主键 |
|
username |
varchar |
100 |
用户名 |
password |
varchar |
100 |
密码 |
salt |
varchar |
50 |
盐 |
role_ids |
varchar |
100 |
角色列表 |
locked |
bool |
账户是否锁定 |
组织机构(sys_organization)
名称 |
类型 |
长度 |
描述 |
id |
bigint |
编号 主键 |
|
name |
varchar |
100 |
组织机构名 |
priority |
int |
显示顺序 |
|
parent_id |
bigint |
父编号 |
|
parent_ids |
varchar |
100 |
父编号列表 |
available |
bool |
是否可用 |
资源(sys_resource)
名称 |
类型 |
长度 |
描述 |
id |
bigint |
编号 主键 |
|
name |
varchar |
100 |
资源名称 |
type |
varchar |
50 |
资源类型, |
priority |
int |
显示顺序 |
|
parent_id |
bigint |
父编号 |
|
parent_ids |
varchar |
100 |
父编号列表 |
permission |
varchar |
100 |
权限字符串 |
available |
bool |
是否可用 |
角色(sys_role)
名称 |
类型 |
长度 |
描述 |
id |
bigint |
编号 主键 |
|
role |
varchar |
100 |
角色名称 |
description |
varchar |
100 |
角色描述 |
resource_ids |
varchar |
100 |
授权的资源 |
available |
bool |
是否可用 |
资源:表示菜单元素、页面按钮元素等;菜单元素用来显示界面菜单的,页面按钮是每个页面可进行的操作,如新增、修改、删除按钮;使用type来区分元素类型(如menu表示菜单,button代表按钮),priority是元素的排序,如菜单显示顺序;permission表示权限;如用户菜单使用user:*;也就是把菜单授权给用户后,用户就拥有了user:*权限;如用户新增按钮使用user:create,也就是把用户新增按钮授权给用户后,用户就拥有了user:create权限了;available表示资源是否可用,如菜单显示/不显示。
角色:role表示角色标识符,如admin,用于后台判断使用;description表示角色描述,如超级管理员,用于前端显示给用户使用;resource_ids表示该角色拥有的资源列表,即该角色拥有的权限列表(显示角色),即角色是权限字符串集合;available表示角色是否可用。
组织机构:name表示组织机构名称,priority是组织机构的排序,即显示顺序;available表示组织机构是否可用。
用户:username表示用户名;password表示密码;salt表示加密密码的盐;role_ids表示用户拥有的角色列表,可以通过角色再获取其权限字符串列表;locked表示用户是否锁定。
此处如资源、组织机构都是树型结构:
id |
name |
parent_id |
parent_ids |
1 |
总公司 |
0 |
0/ |
2 |
山东分公司 |
1 |
0/1/ |
3 |
河北分公司 |
1 |
0/1/ |
4 |
济南分公司 |
2 |
0/1/2/ |
parent_id表示父编号,parent_ids表示所有祖先编号;如0/1/2/表示其祖先是2、1、0;其中根节点父编号为0。
为了简单性,如用户-角色,角色-资源关系直接在实体(用户表中的role_ids,角色表中的resource_ids)里完成的,没有建立多余的关系表,如要查询拥有admin角色的用户时,建议建立关联表,否则就没必要建立了。在存储关系时如role_ids=1,2,3,;多个之间使用逗号分隔。
用户组、组织机构组本实例没有实现,即可以把一组权限授权给这些组,组中的用户/组织机构就自动拥有这些角色/权限了;另外对于用户组可以实现一个默认用户组,如论坛,不管匿名/登录用户都有查看帖子的权限。
更复杂的权限请参考我的《JavaEE项目开发脚手架》:http://github.com/zhangkaitao/es。
具体请参考com.github.zhangkaitao.shiro.chapter16.entity包下的实体,此处就不列举了。
具体请参考com.github.zhangkaitao.shiro.chapter16.dao包下的DAO接口及实现。
具体请参考com.github.zhangkaitao.shiro.chapter16.service包下的Service接口及实现。以下是出了基本CRUD之外的关键接口:
public interface ResourceService {Set<String> findPermissions(Set<Long> resourceIds); //得到资源对应的权限字符串List<Resource> findMenus(Set<String> permissions); //根据用户权限得到菜单
}
public interface RoleService {Set<String> findRoles(Long... roleIds); //根据角色编号得到角色标识符列表Set<String> findPermissions(Long[] roleIds); //根据角色编号得到权限字符串列表
}
public interface UserService {public void changePassword(Long userId, String newPassword); //修改密码public User findByUsername(String username); //根据用户名查找用户public Set<String> findRoles(String username);// 根据用户名查找其角色public Set<String> findPermissions(String username);// 根据用户名查找其权限
}
public class UserRealm extends AuthorizingRealm {@Autowired private UserService userService;protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {String username = (String)principals.getPrimaryPrincipal();SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();authorizationInfo.setRoles(userService.findRoles(username));authorizationInfo.setStringPermissions(userService.findPermissions(username));System.out.println(userService.findPermissions(username));return authorizationInfo;}protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {String username = (String)token.getPrincipal();User user = userService.findByUsername(username);if(user == null) {throw new UnknownAccountException();//没找到帐号}if(Boolean.TRUE.equals(user.getLocked())) {throw new LockedAccountException(); //帐号锁定}return new SimpleAuthenticationInfo(user.getUsername(), //用户名user.getPassword(), //密码ByteSource.Util.bytes(user.getCredentialsSalt()),//salt=username+saltgetName() //realm name);}
}
此处的UserRealm和《第六章Realm及相关对象》中的UserRealm类似,通过UserService获取帐号及角色/权限信息。
@Controller
public class IndexController {@Autowiredprivate ResourceService resourceService;@Autowiredprivate UserService userService;@RequestMapping("/")public String index(@CurrentUser User loginUser, Model model) {Set<String> permissions = userService.findPermissions(loginUser.getUsername());List<Resource> menus = resourceService.findMenus(permissions);model.addAttribute("menus", menus);return "index";}
}
IndexController中查询菜单在前台界面显示,请参考相应的jsp页面;
@Controller
public class LoginController {@RequestMapping(value = "/login")public String showLoginForm(HttpServletRequest req, Model model) {String exceptionClassName = (String)req.getAttribute("shiroLoginFailure");String error = null;if(UnknownAccountException.class.getName().equals(exceptionClassName)) {error = "用户名/密码错误";} else if(IncorrectCredentialsException.class.getName().equals(exceptionClassName)) {error = "用户名/密码错误";} else if(exceptionClassName != null) {error = "其他错误:" + exceptionClassName;}model.addAttribute("error", error);return "login";}
}
LoginController用于显示登录表单页面,其中shiro authc拦截器进行登录,登录失败的话会把错误存到shiroLoginFailure属性中,在该控制器中获取后来显示相应的错误信息。
@RequiresPermissions("resource:view")
@RequestMapping(method = RequestMethod.GET)
public String list(Model model) {model.addAttribute("resourceList", resourceService.findAll());return "resource/list";
}
在控制器方法上使用@RequiresPermissions指定需要的权限信息,其他的都是类似的,请参考源码。
@ControllerAdvice
public class DefaultExceptionHandler {@ExceptionHandler({UnauthorizedException.class})@ResponseStatus(HttpStatus.UNAUTHORIZED)public ModelAndView processUnauthenticatedException(NativeWebRequest request, UnauthorizedException e) {ModelAndView mv = new ModelAndView();mv.addObject("exception", e);mv.setViewName("unauthorized");return mv;}
}
如果抛出UnauthorizedException,将被该异常处理器截获来显示没有权限信息。
定义了context:component-scan来扫描除web层的组件、dataSource(数据源)、事务管理器及事务切面等;具体请参考配置源码。
Spring配置——spring-config-cache.xml
定义了spring通用cache,使用ehcache实现;具体请参考配置源码。
Spring配置——spring-config-shiro.xml
<bean id="userRealm" class="com.github.zhangkaitao.shiro.chapter16.realm.UserRealm"><property name="credentialsMatcher" ref="credentialsMatcher"/><property name="cachingEnabled" value="false"/>
</bean>
<bean id="sysUserFilter"
class="com.github.zhangkaitao.shiro.chapter16.web.shiro.filter.SysUserFilter"/>
sysUserFilter用于根据当前登录用户身份获取User信息放入request;然后就可以通过request获取User。
<property name="filterChainDefinitions"><value>/login = authc/logout = logout/authenticated = authc/** = user,sysUser</value>
</property>
如上是shiroFilter的filterChainDefinitions定义。
<mvc:annotation-driven><mvc:argument-resolvers><bean class="com.github.zhangkaitao.shiro.chapter16.web.bind.method.CurrentUserMethodArgumentResolver"/></mvc:argument-resolvers>
</mvc:annotation-driven>
此处注册了一个@CurrentUser参数解析器。如之前的IndexController,从request获取shiro sysUser拦截器放入的当前登录User对象。
Spring MVC配置——spring-mvc-shiro.xml
<aop:config proxy-target-class="true"></aop:config>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"><property name="securityManager" ref="securityManager"/>
</bean>
定义aop切面,用于代理如@RequiresPermissions注解的控制器,进行权限控制。
定义Spring ROOT上下文加载器、ShiroFilter、及SpringMVC拦截器。具体请参考源码。
<shiro:hasPermission name="user:create"><a href="${pageContext.request.contextPath}/user/create">用户新增</a><br/>
</shiro:hasPermission>
访问http://localhost:8080/chapter16/;
首先进入登录页面,输入用户名/密码(默认admin/123456)登录:
登录成功后到达整个页面主页,并根据当前用户权限显示相应的菜单,此处菜单比较简单,没有树型结构显示
然后就可以进行一些操作,如组织机构维护、用户修改、资源维护、角色授权
http://www.iteye.com/blogs/subjects/spring3
http://www.iteye.com/blogs/subjects/kaitao-springmvc
http://jinnianshilongnian.iteye.com/blog/1864800
《Shiro+Struts2+Spring3 加上@RequiresPermissions 后@Autowired失效》
http://jinnianshilongnian.iteye.com/blog/1850425
更复杂的权限请参考我的《JavaEE项目开发脚手架》:http://github.com/zhangkaitao/es,提供了更加复杂的实现。
示例源代码:https://github.com/zhangkaitao/shiro-example;可加群 231889722 探讨Spring/Shiro技术。
请你欣赏春天美景 http://user.qzone.qq.com/314154083/photo/V10a4ot72FxYVR?ptlang=2052
第十六章 综合实例——《跟我学Shiro》相关推荐
- pdfstamper生成pdf无法显示汉字_正点原子STM32F4/F7水星开发板资料连载第四十六章 汉字显示实验...
1)实验平台:正点原子水星 STM32F4/F7 开发板 2)摘自<STM32F7 开发指南(HAL 库版)>关注官方微信号公众号,获取更多资料:正点原子 3)全套实验源码+手册+视频下载 ...
- 【系统分析师之路】第十六章 复盘计算机网络(新技术领域)
[系统分析师之路]第十六章 复盘计算机网络(新技术领域) 复盘计算机网络之新技术领域 [系统分析师之路]第十六章 复盘计算机网络(新技术领域) 前言部分 历年真题考点分析 1)考点分析 2)重要知识点 ...
- 【正点原子STM32连载】第四十六章 FATFS实验 摘自【正点原子】MiniPro STM32H750 开发指南_V1.1
1)实验平台:正点原子MiniPro H750开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id=677017430560 3)全套实验源码+手册+视频 ...
- 光盘显示0字节可用_正点原子STM32F4/F7水星开发板资料连载第四十六章 汉字显示实验
1)实验平台:正点原子水星 STM32F4/F7 开发板 2)摘自<STM32F7 开发指南(HAL 库版)>关注官方微信号公众号,获取更多资料:正点原子 3)全套实验源码+手册+视频下载 ...
- Java基础学习——第十六章 Java8新特性
Java基础学习--第十六章 Java8 新特性 Java8(JDK8.0)较 JDK7.0 有很多变化或者说是优化,比如 interface 里可以有静态方法和默认方法,并且可以有方法体,这一点就颠 ...
- 第四十六章:SpringBoot RabbitMQ完成消息延迟消费
在2018-3-1日SpringBoot官方发版了2.0.0.RELEASE最新版本,新版本完全基于Spring5.0来构建,JDK最低支持也从原来的1.6也改成了1.8,不再兼容1.8以下的版本,更 ...
- 《Reids 设计与实现》第十六章 集群(下)
<Reids 设计与实现>第十六章 集群(下) 文章目录 <Reids 设计与实现>第十六章 集群(下) 七.复制与故障转移 1.设置从节点 2.故障检测 3.故障转移 4.选 ...
- 【JAVA SE】第十六章 进程、线程、同步锁和线程锁的简介
第十六章 进程.线程.同步锁和线程安全问题 文章目录 第十六章 进程.线程.同步锁和线程安全问题 一.进程 1.基本介绍 2.进程模型 二.线程 1.基本介绍 2.线程的生命周期 3.线程的优先级 4 ...
- abd shell关闭所有程序_第十六章 Shell编程
第十六章 Shell编程 16.1 shell脚本运行 所谓脚本,就是把众多命令写入一个文件中,让其按照一定的逻辑顺序执行,以完成一个具体的功能.而在Linux的shell编译环境下,shell编程与 ...
最新文章
- Java正則表達式入门
- mysql语句使用_MySQL语句的使用
- 进程的创建-fork
- 定时自动刷新网页在线工具分享
- 大学用编程每月多赚2000块,是种什么体验?
- oracle中查询某张表的大小
- 俄美就《中导条约》磋商 普京:不希望进行军备竞赛
- hihocoder 1866 XOR
- 如何做关键词挖掘和关键词的选择?
- 用计算机显示器主屏区域造句,显示器造句
- SpringBoot的pom详解
- 佳能2900打印机与win10不兼容_Win10系统如何解决佳能打印机打印问题?
- VR乒乓球项目Unity3D 开发经验整理,4简单而有效的AI
- java 锟斤 解决乱码_java eclipse 开发中文乱码锟斤拷小锟斤拷锟
- laravel 微博登录
- 【宏定义】#define 的使用方法
- stata学习笔记|离散被解释变量
- PX4模块设计之四十四: bmp280模块
- 飞速创软亮相南方信息大会,获评年度CIO优选数字化服务商
- 企业微信会员销售额达40%的资生堂,能带给我们怎样的数字化增长启示?