1、Shiro 框架简介

Shiro 概述
Shiro 是Apache公司推出一个权限管理框架,其内部封装了项目中认证,授权,加密,会话等逻辑操作,通过Shiro框架可以简化我们项目权限控制逻辑的代码的编写。其认证和授权业务分析,如图所示:

Shiro 框架概要架构
Shiro 框架中主要通过Subject,SecurityManager,Realm对象完整认证和授权业务,其简要架构如下:

其中:
Subject 此对象负责提交用户身份、权限等信息
SecurityManager 负责完成认证、授权等核心业务
Realm 负责通过数据逻辑对象获取数据库或文件中的数据。

Shiro 框架详细架构分析
Shiro 框架进行权限管理时,要涉及到的一些核心对象,主要包括:认证管理对象,授权管理对象,会话管理对象,缓存管理对象,加密管理对象以及 Realm 管理对象(领域对象:负责处理认证和授权领域的数据访问题)等,其具体架构如图- 所示:

其中:

  1. Subject(主体):与软件交互的一个特定的实体(用户、第三方服务等)。
  2. SecurityManager(安全管理器) :Shiro 的核心,用来协调管理组件工作。
  3. Authenticator(认证管理器):负责执行认证操作。
  4. Authorizer(授权管理器):负责授权检测。
  5. SessionManager(会话管理):负责创建并管理用户 Session 生命周期,提供一
    个强有力的 Session 体验。
  6. SessionDAO:代表 SessionManager 执行 Session 持久(CRUD)动作,它允
    许任何存储的数据挂接到 session 管理基础上。
  7. CacheManager(缓存管理器):提供创建缓存实例和管理缓存生命周期的功能。
  8. Cryptography(加密管理器):提供了加密方式的设计及管理。
  9. Realms(领域对象):是 shiro 和你的应用程序安全数据之间的桥梁。

2、Shiro 框架基础配置

Shiro 依赖
在项目中添加Shiro相关依赖(参考官网http://shiro.apache.org/spring-boot.html),假如项目中添加过shiro-spring依赖,将shiro-spring依赖替换掉即可。

<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.7.0</version>
</dependency>

说明,添加完此依赖,直接启动项目会启动失败,还需要额外的配置。

Shiro 基本配置
第一步:创建一个Realm类型的实现类(基于此类通过DAO访问数据库),关键代码如下:

package com.cy.pj.sys.service.realm;
public class ShiroRealm extends AuthorizingRealm {
/**此方法负责获取并封装授权信息*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo( PrincipalCollection principalCollection) {
return null;
}
/**此方法负责获取并封装认证信息*/ @Override
protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken authenticationToken) throws AuthenticationException {return null; } }

第二步:在项目启动类中添加Realm对象配置,关键代码如下:

@Bean
public Realm realm(){//org.apache.shiro.realm.Realm
return new ShiroRealm();
}

第三步:在启动类中定义过滤规则(哪些访问路径要进行认证才可以访问),关键代码如下:

@Beanpublic ShiroFilterChainDefinition shiroFilterChainDefinition() { DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition(); LinkedHashMap<String,String> map=new LinkedHashMap<>(); //设置允许匿名访问的资源路径(不需要登录即可访问) map.put("/bower_components/**","anon");//anon对应shiro中的一个匿名过滤器 map.put("/build/**","anon"); map.put("/dist/**","anon"); map.put("/plugins/**","anon"); //设置需认证以后才可以访问的资源(注意这里的顺序,匿名访问资源放在上面) map.put("/**","authc");//authc 对应一个认证过滤器,表示认证以后才可以访问 chainDefinition.addPathDefinitions(map); return chainDefinition;}

第四步:配置认证页面(登录页面)
在spring的配置文件(application.yml)中,添加登录页面的配置,关键代码如下:

shiro: loginUrl: /login.html

其中,login.html页面为项目中static目录定义好的一个页面。

第五步:启动服务进行访问测试
打开浏览器,输入http://localhost/doIndexUI检测是否会出现登录窗口,如图所示:

3、Shiro认证业务分析及实现

认证流程分析
当我们在登录页面,输入用户信息,提交到服务端进行认证,其中shiro框架的认证时序如图所示:

其中:

  1. token :封装用户提交的认证信息(例如用户名和密码)的一个对象。
  2. Subject: 负责将认证信息提交给SecurityManager对象的一个主体对象。
  3. SecurityManager是shiro框架的核心,负责完成其认证、授权等业务。
  4. Authenticator 认证管理器对象,SecurityManager继承了此接口。
  5. Realm 负责从数据库获取认证信息并交给认证管理器。

Shiro框架认证业务实现
第一步:在SysUserDao中定义基于用户名查询用户信息的方法,关键代码如下:

@Select("select * from sys_users where username=#{username}")
SysUser findUserByUsername(String username);

第二步:修改ShiroRealm中获取认证信息的方法,关键代码如下:

/**此方法负责获取并封装认证信息*/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {//1、获取用户提交提交的认证用户信息UsernamePasswordToken upToken=(UsernamePasswordToken) authenticationToken;String username = upToken.getUsername();//2、基于用户名查询从数据库用户信息SysUser sysUser = sysUserDao.findUserByUsername(username);//3、判断用户是否存在if (sysUser==null)throw new UnknownAccountException();//账户不存在//4、判断用户是否被禁用if (sysUser.getValid()==0)throw new LockedAccountException();//5、封装认证信息并返回ByteSource credentialsSalt=ByteSource.Util.bytes(sysUser.getSalt());SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(sysUser,//principal 传入的用户身份sysUser.getPassword(),//hashedCredentialscredentialsSalt,//credentialsSaltgetName());return info;//返回给认证管理器}

第三步:在ShiroRealm中重谢获取凭证加密算法的方法,关键代码如下:

@Overridepublic CredentialsMatcher getCredentialsMatcher() {HashedCredentialsMatcher matcher=new HashedCredentialsMatcher();matcher.setHashAlgorithmName("MD5");//加密算法matcher.setHashIterations(1);//加密次数return matcher;}

第四步:在SysUserController中添加处理登录请求的方法,关键代码如下:

@RequestMapping("doLogin")public JsonResult doLogin(String username,String password){//将账号和密码封装到token对象UsernamePasswordToken token =//参考官网new UsernamePasswordToken(username, password);//基于subject对象将token提交给securityManagerSubject currentUser = SecurityUtils.getSubject();currentUser.login(token);//提交给securityManagerreturn new JsonResult("login ok");}

第五步:统一异常处理类中添加shiro异常处理代码,关键如下:

@ExceptionHandler(ShiroException.class)public JsonResult doShiroException(ShiroException e){JsonResult r=new JsonResult();r.setState(0);if (e instanceof UnknownAccountException){r.setMessage("用户名不存在");}else if (e instanceof IncorrectCredentialsException){r.setMessage("密码不正确");}else if (e instanceof LockedAccountException){r.setMessage("账户被锁定");}else  if (e instanceof AuthorizationException){r.setMessage("没有权限");}else {r.setMessage("认证或授权失败");}return r;}

第五步:在过滤配置中允许登录时的url匿名访问,关键代码如下:

...
map.put("/user/doLogin","anon");
...

第六步:再过滤配置中配置登出url操作,关键代码如下:

..
map.put("/doLogout","logout");//logout是shiro框架给出一个登出过滤器...

第六步:启动服务器,进行登录访问测试

第七步:Shiro框架认证流程总结分析
Step01:登录客户端(login.html)中的用户输入的登录信息提交SysUserController对象
Step02:SysUserController对象基于doLogin方法处理登录请求.
Step03:SysUserController中的doLogin方法将用户信息封装token中, 然后基于subject对象将
token提交给SecurityManager对象。
Step04:SecurityManager对象调用认证方法(authenticate)去完成认证,在此方法内部会调用
ShiroRealm中的doGetAuthenticationInfo获取数据库中的用户信息,然后再与客户端提交的
token中的信息进行比对,比对时会调用getCredentialsMatcher方法获取凭证加密对象,通过此对
象对用户提交的token中的密码进行加密。

4、Shiro授权业务分析及实现

授权流程分析
已认证用户,在进行系统资源的访问时,我们还要检查用户是否有这个资源的访问权限。并不是所有认证用户都可以访问系统内所有资源,也应该是受限访问的,具体授权流程如图所示:

Shiro框架中的授权实现
第一步: 在SysMenuDao中定义查询用户权限标识的方法,关键代码分析:

Set<String> findUserPermissions(Integer userId);

第二步:在SysMenuMapper中添加查询用户权限标识的SQL映射,关键代码如下:

<select id="findUserPermissions" resultType="String">select distinct permissionfrom sys_user_roles ur join sys_role_menus rm join sys_menus mon ur.role_id=rm.role_id and rm.menu_id=m.idwhere ur.user_id=#{userId} and trim(m.permission)!='' and m.permission is not null</select>

第三步:修改ShiroRealm中获取权限并封装权限信息的方法,关键代码如下

/**此方法负责获取授权信息*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {//1、获取登录用户idSysUser user =(SysUser) principalCollection.getPrimaryPrincipal();//2、基于登录用户id获取用户权限标识Set<String> stringPermissions=sysMenuDao.findUserPermissions(user.getId());//3、封装数据并返回SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();info.setStringPermissions(stringPermissions);return info;//返回给授权管理器}

第四步:定义授权切入点方法,示例代码如下:
在shiro框架中,授权切入点方法需要通过**@RequiresPermissions**注解进行描述,例如:

@RequiresPermissions("sys:user:update")
public int validById(Integer id,Integer valid){
int rows=sysUserDao.validById(id,valid);
if(rows==0)throw new ServiceException("记录可能已经不存在");return rows; }

其中, @RequiresPermissions注解中定义的内容为,访问此方法需要的权限.
@RequiresPermissions 描述的方法为切入点方法,此方法在执行时需要在“通知方法”中判定用户是否由访问此方法的权限(检测用户权限中是否包含@RequiresPermissions注解内部包含的限),假如有权限,则授权访问.

第五步:启动服务进行访问测试和原理分析
在访问时首先要检测一下用户有什么权限,检测过程,先查询用户有什么角色,再查看角色有什么菜单的访问权限.
授权原理分析:(底层基于AOP实现)
Step01 页面上用户通过菜单触发对服务端资源的访问.
Step02 服务端Controller处理客户端的资源访问请求
Step03 假如客户端请求访问的资源业务放上有@RequiresPermissions注解描述则底层Controller
对象会调用Service的代理对象,代理对象会调用AOP中通知方法,在通知方法中获取@RequiresPermissions上的定义的权限标识.
Step04 通过Subject 对象提交@RequiresPermissions注解中的授权标识给SecurityManager对象,
此对象会调用ShiroRealm中的获取用户权限的方法,最终会将从数据权限信息与
@RequiresPermissions中的定义的权限信息做一个比对.

5、Shiro认证授权业务进阶实现

呈现登录用户信息
Controller 方法定义及实现
修改PageController中的doIndex方法(主页调用的方法),关键代码如下:

 @RequestMapping("doIndexUI")public String doIndexUI(Model model) {//获取登录用户信息(shiro框架给定的固定写法)SysUser user=(SysUser)SecurityUtils.getSubject().getPrincipal();//存储用户信息model.addAttribute("username", user.getUsername());return "starter";}

页面Thymeleaf 表达式应用
打开starter.html页面,找到用户名对应的位置,然后通过[[${}]]表达式获取服务端model中数据,呈现在页面上,关键代码如下:

<span class="hidden-xs" id="loginUserId">[[${username}]]</span>

用户菜单的动态化呈现
业务分析
我们希望不同登录用户,登录系统以后,看到的用户菜单是不一样的。登录用户只能看到自己可以访问的一些菜单选项

服务端设计及实现
第一步:定义pojo对象存储用户菜单信息,关键代码如下:

package com.cy.pj.sys.pojo;
@Data
public class SysUserMenu implements Serializable{
private static final long serialVersionUID = -410105494012229800L;
private Integer id;
private String name;
private String url;
private List<SysUserMenu> childMenus;
}

第二步:在SysMenuDao中定义查询用户一级和二级菜单信息的方法,关键代码如下:

List<SysUserMenu> findUserMenus(Integer userId);

第三步:在SysMenuMapper中定义查询用户一级和二级菜单信息时对应的sql映射,关键代码如下:

<resultMap id="sysUserMenu" type="com.cy.pj.sys.pojo.SysUserMenu"><id property="id" column="id"/><result property="name" column="name"/><result property="url" column="url"/><collection property="childMenus" ofType="com.cy.pj.sys.pojo.SysUserMenu"><id property="id" column="cid"/><result property="name" column="cname"/><result property="url" column="curl"/></collection></resultMap><select id="findUserMenus" resultMap="sysUserMenu">select p.id,p.name,p.url,c.id cid,c.name cname,c.url curlfrom sys_menus p join sys_menus con p.id=c.parentIdwhere p.parentId is null and c.id in (select rm.menu_id from sys_user_roles ur join sys_role_menus rmon ur.role_id=rm.role_idwhere ur.user_id=#{userId})</select>

注:查询多级菜单可以根据递归查询,比如,先查询根据userid查询出全部菜单信息进行封装,然后根据一级菜单id查询出一级菜单进行封装,然后定义递归方法根据父菜单id进行查询子菜单信息封装到父级菜单,直到子菜单没有数据,结束。

第四步:在SysMenuService接口中添加查询用户菜单信息的方法,关键代码如下:

List<SysUserMenu> findUserMenus(Integer userId);

第五步:在SysMenuServiceImpl类中添加查询用户菜单信息的方法,关键代码如下:

public List<SysUserMenu> findUserMenus(Integer userId){return sysMenuDao.findUserMenus(userId); }

第六步:修改PageController中的doIndexUI方法,关键代码如下:

@Autowired private SysMenuService sysMenuService;@RequestMapping("doIndexUI")public String doIndexUI(Model model) {//获取登录用户信息(shiro框架给定的固定写法)SysUser user=(SysUser)SecurityUtils.getSubject().getPrincipal();//存储用户信息model.addAttribute("username", user.getUsername());//查询用户菜单List<SysUserMenu> userMenus = sysMenuService.findUserMenus(user.getId());model.addAttribute("userMenus", userMenus);return "starter";}

客户端设计及实现
第一步:修改starter页面菜单呈现部分的内容,关键代码如下:

<li class="treeview" th:each="p:${userMenus}">   <a href="#"><i class="fa fa-link"></i>            <span>[[${p.name}]]</span><span class="pull-right-container"> <i class="fa fa-angle-left pull-right"></i> </span></a><ul class="treeview-menu"> <li th:each="c:${p.childMenus}"> <a th:onclick="doLoadRS([[${c.url}]])">[[${c.name}]]</a></li> </ul>
</li>

第二步:添加菜单事件处理函数,关键代码如下

function doLoadRS(url){ $("#mainContentId").load(url); }

第三步:启动服务进行访问测试

Shiro框架在项目中的应用相关推荐

  1. 关于Shiro框架权限标识符中*使用的总结

    2019独角兽企业重金招聘Python工程师标准>>> #关于Shiro框架权限标识符中*使用的总结 Shiro框架中独创的权限标识符(Wildcard Perssions)可以很优 ...

  2. prism项目搭建 wpf_WPF Step By Step 系列-Prism框架在项目中使用

    Prism是一个强大的Mvvm框架,下面我们将重点讲解如何在项目使用Prism提供的基础功能,完成基于MVVM的WPF项目的框架设计和开发,包括应用程序的架构. 项目的解决方案结构,项目采用Prism ...

  3. 使用shiro框架的项目增加忘记密码功能遇到的一些问题

    开发忘记密码功能遇到的问题 后端验证功能用的是shiro框架,当前端发送AJax请求到后端,如果在application.yml文件中没有将找回页面所需要调用的接口配置为匿名,那么这些接口不会不调用, ...

  4. ssm框架的项目中用户图片的上传功能

    后台管理的web项目中,需要完成图片的上传功能 工具准备:一.图片上传的必要JS文件:二:当前页面中JS重写提交图片的方法:三.后台接收和处理图片的方法 整体思路:一.HTML中添加文件上传按钮: 二 ...

  5. SLF4J日志框架在项目中使用

    介绍 SLF4J全称"Simple Logging Facade for Java",作为各种日志框架的简单门面.例如: java.util.logging.logback . r ...

  6. shiro框架---关于多项目之间验证为什么需要共享session

    关于多项目之间登录验证为什么需要共享session 服务器上部署了两个项目,登录之后才可以访问其中的接口.现在想要实现,当登录项目1成功后,可以不需要再登录项目2,直接可以访问项目2中的接口. 大部分 ...

  7. dwz框架在网站群项目中的应用(2)—html拓展之页面形式

    "dzw框架"在项目中展示界面时支持HTML扩展方式来调用DWZ组件,如此便很方便的拓展出各种界面.下面将以内容管理模块为例说明"dwz框架"在项目中如何以各种 ...

  8. shiro框架---shiro配置介绍(一)

    接上一篇文章shiro框架-通过系统介绍shiro框架中的实现逻辑   项目已分享到GitHub上,如果需要的可以看下,springboot+shiro项目Git下载地址. shiro在springb ...

  9. security框架工作笔记001--- Shiro和Spring Security对比_智慧城市项目中选型用Spring Security

    JAVA技术交流QQ群:170933152 Shiro简介 Apache Shiro是Java的一个安全框架.目前,使用Apache Shiro的人越来越多,因为它相当简单,对比Spring Secu ...

最新文章

  1. 剑指offer:面试题36. 二叉搜索树与双向链表
  2. 磁盘管理及文件系统管理
  3. 浅谈lastIndexOf
  4. 【计算机组成原理】定点数的表示和运算
  5. Linux查看centos版本 - 命令篇
  6. HTML元素和标签的区别
  7. 个盘子的汉诺塔需要移动几步_坨——理解递归实现quot;汉诺塔quot;代码的关键...
  8. NUC1840 Graveyard Design【尺取法】
  9. Web前端:javascript实现图片轮播
  10. 上面两点下面一个三角形_【人人都能欣赏的数学证明】为什么三角形的三个内角相加是180度?...
  11. 采用Zigbee和Raspberry Pi的太阳能/燃气热水器自动控制系统
  12. 香农编码(C语言实现香农编码,并计算信源熵、平均码长、编码效率)
  13. php短视频源码,流式布局(代码)
  14. java 比较日期大小(方法之一compare to 备忘)
  15. MATLAB(1)MATLAB工作环境
  16. PISCES: A Programmable, Protocol-Independent Software Switch(总结)
  17. 如何提高研发部门工作效率的探讨
  18. 远程文件包含 php,php本地及远程文件包含漏洞
  19. 从今天起我想要热爱生活
  20. 大数据风控系统主要功能分析

热门文章

  1. fdtd算法的matlab程序,一维FDTD的matlab程序(最新整理)
  2. elastic-job分片规则
  3. python算法完整教程专栏完整目录
  4. 超级电容容量及放电时间计算方法
  5. 中国纺织业漂泊难定,如今向东南亚转移该担心还是惋惜?
  6. Linux系统—第13章—网络管理实践1
  7. 飞机大战java_java版飞机大战实战项目详细步骤
  8. ThinkPad E14 G2 笔记本声音无法外放问题的解决办法
  9. opencv自适应二值化原理
  10. BeanUtils拷贝对象