==================================
Shiro 的加深理解:
==================================
1. Shiro 和 Spring 系组件的对标, Shiro = Spring Security + Spring Session. 就是说 Shiro 不仅仅是一个安全框架, 同时也是一个 Session 管理框架. 其实这也是很自然的事情, Shiro 会拦截所有的 web 请求, 通过 SecurityManager 进行安全审核, 接着 SecurityManager 又委托 SessionManager 进行 session 管理. 当然 Shiro 并没有强制我们一定要使用它来进行 Session 管理.
2. Shiro Session 管理: 就是和普通的 web session 管理一样, 管理 session 属性, 默认是保存在内存中, 也可以保存在分布式的存储中, 比如 redis.
3. Shiro cache 的内容是: 仅仅缓存 Realm 的认证和鉴权相关的数据, 不会包含 Session 属性数据.

默认情况下, Web 服务器端的 Session 都是保存在内存中, 如果服务重启升级, Session 就丢了, 如果是集群部署, session 保存在单机内存中也不太合适, 这时就需要引入集中 session 存储了, 现在比较流行使用 redis 存储.

==================================
Shiro 内置的 Session Manager
==================================
Session Manager, Shiro 使用 session 来管理 subject 的生命周期, 包括: 创建/停止/过期 等等, shiro 内置了三个实现:
1. DefaultSessionManager
    DefaultSecurityManager 使用的默认 Session Manager, 用于 JavaSE 环境.
2. ServletContainerSessionManager
    DefaultWebSecurityManager 默认使用的 Session Manager, 使用 Servlet 容器服务器的会话. 如果运行在 Tomcat 中, HttpSession 接口的实现类是 org.apache.catalina.session.StandardSessionFacade. 用于 Web 环境.
3. DefaultWebSessionManager
    可以替代 ServletContainerSessionManager, 自定义 Session 的管理策略, 甚至包括自定义持久化方案, Shiro 提供了一个默认的 MemorySessionDAO 持久化 DAO 实现, 也有 redis 持久化方案, 比如这个开源项目, http://alexxiyang.github.io/shiro-redis/

下面是启用 DefaultWebSessionManager 的简单示例, 采用了默认的 MemorySessionDAO 来持久化 (应该是基于并发 HashMap 类).

@Bean
public DefaultWebSessionManager sessionManager() {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setGlobalSessionTimeout(1800000l);
sessionManager.setDeleteInvalidSessions(true);
sessionManager.setSessionValidationSchedulerEnabled(true);
sessionManager.setSessionIdCookieEnabled(true);
SimpleCookie cookie = new SimpleCookie(ShiroHttpSession.DEFAULT_SESSION_ID_NAME); //cookie 名为: JSESSIONID
cookie.setHttpOnly(true);
cookie.setMaxAge(1800000);
sessionManager.setSessionIdCookie(cookie);
return sessionManager;
}

==================================
使用 Shiro 框架后几个常用对象的具体实现类
==================================
Shiro 不管是采用 ServletContainerSessionManager 还是 DefaultWebSessionManager, 使用 Shiro 框架后, 视图方法注入的 HttpServletRequest 参数都会被替换为 shiro 的 ShiroHttpServletRequest 实现类.

另外需要注意, Shiro subject.getSession() 返回的 session 对象, 实现的接口是 org.apache.shiro.session.Session, 这个接口和标准 HttpSession 接口没有父子关系, 不能简单强转, Shiro 内部有相互转换的代码.
1. subject.getSession() 返回值的具体类: org.apache.shiro.subject.support.DelegatingSubject$StoppingAwareProxiedSession
2. Controller 视图方法注入的 HttpSession 接口参数, 具体实现类是 org.apache.catalina.session.StandardSessionFacade
3. Controller 视图方法中, 通过 request.getSession() 方法, 返回值的类型和 视图方法注入的 HttpSession 参数类型一致.

详情对象的类型如下:

subject.getSession()     : org.apache.shiro.subject.support.DelegatingSubject$StoppingAwareProxiedSession
httpSession : org.apache.catalina.session.StandardSessionFacade
httpRequest.getSession() : org.apache.catalina.session.StandardSessionFacade
httpRequest : org.apache.shiro.web.servlet.ShiroHttpServletRequest
httpResponse : org.apache.catalina.connector.ResponseFacade

下图是几个Session类之间的关系:

==================================
Shiro Session 和标准 HttpSession 的互操作性
==================================
虽然 org.apache.shiro.session.Session 接口和标准的 javax.servlet.http.HttpSession 接口没有父子关系, 它们不能简单强转, 但是对于 Session 属性管理是相通的, 经我测试, Shiro 不管是采用 ServletContainerSessionManager 作为 session 管理器, 还是采用 DefaultWebSessionManager, 两套 session 对象的属性读写是相通的.

这为我们提供了极大的便利, 也就是说我们既可以使用标准的 HttpSession 对象来读写 session 属性, 也可以使用 Shiro 的 Session 对象来读写.

// HttpSession 主动写 Session 属性示例
@RequestMapping("/springWrite")
@ResponseBody
public String springWrite(HttpServletRequest httpRequest, HttpSession httpSession){
httpSession.setAttribute("Spring_Attr", "Spring_Attr_Value");
System.out.println(httpSession.getAttribute("Spring_Attr")); System.out.println("=======");
Session session = SecurityUtils.getSubject()
.getSession();
System.out.println(session.getAttribute("Spring_Attr"));
return "springWrite";
} // Shiro Session 主动写 Session 属性示例
@RequestMapping("/shiroWrite")
@ResponseBody
public String shiroWrite(HttpServletRequest httpRequest, HttpSession httpSession){
Session session = SecurityUtils.getSubject()
.getSession();
session.setAttribute("Shiro_Attr", "Shiro_Attr_value");
System.out.println(session.getAttribute("Shiro_Attr")); System.out.println("=======");
System.out.println(httpSession.getAttribute("Shiro_Attr"));
return "shiroWrite";
}

下面是 shiro session 支持的方法:

    Session session = subject.getSession();
session.getId()
session.getTimeout())
session.getStartTimestamp();
session.getLastAccessTime();
session.setAttribute("key", "123");
session.removeAttribute("key");
session.touch();
session.stop();

在 J2SE 项目中, 使用 Subject.logout() 会自动调用 Session.stop() 方法来销毁会话, 如果在 web 中, 调用 javax.servlet.http.HttpSession.invalidate() 也会自动调用 Session.stop() 方法进行销毁 Shiro 的会话.

==================================
到底要不要让 Shiro 全面接管 Session 管理?
==================================
前面已经提到: Shiro = Spring Security + Spring Session, 在安全管理这块, 我觉得 Shiro 比 Spring Security 强太多了, Spring Security 搞的太复杂了. 现在的问题是, 要不要让 Shiro 全面接管 Session 管理?

推荐方案是 ServletContainerSessionManager + Spring Session, 也就是说 Session 管理还是交给 Spring Session, Session 属性操作还是使用标准的HttpSession注入对象来完成,  不太推荐使用 DefaultWebSessionManager 使用持久化 Session 信息.

说明如下:
1. 推荐 ServletContainerSessionManager 而不是 DefaultWebSessionManager, 这样就能更好利用 Spring Session 生态. 如果使用 DefaultWebSessionManager, 开箱即用的 SessionDAO 只有 MemorySessionDAO 和 开源的 shiro-redis (地址 http://alexxiyang.github.io/) , 生态要比 Spring Session 差.
比如, 我们我们
2. 有克制地使用 SecurityUtils.getSubject().getSession(), 虽然 Shiro Session 和标准 HttpSession 具有很好的互操作性, 但在视图方法中, 还是推荐使用 HttpSession 参数注入对象, 在 Shiro realm 实现类中, 如果要读写 session, 直接用 SecurityUtils.getSubject().getSession() 非常方便.

===========================
shiro 对 Basic和 JWT 认证的支持
===========================
上面的介绍都是基于 session-cookie 这样的安全验证, 这个小节看看shiro对于其他安全验证的支持, 常用web鉴权机制有: Login Form(session-cookie), 还有 Basic Auth, 还有jwt token或hmac token等无状态认证(即不涉及session管理), 还有 OAUTH2 (集成第三方认证).

Shiro 默认支持Login Form 和 BasicAuth 两种鉴证方式, 同时因为shiro灵活的架构, 基于shiro也很容易支持基于jwt token或hmac token等无状态认证.
比如要支持基于jwt token的无状态认证也很容易, 需要定制的地方有:
1. 定制一个 JwtToken 类(继承自 AuthenticationToken), 用来代替常用的 user name /password token 类.
2. 在 Realm 使用该 JwtToken 类完成验证
3. 定义一个 JwtFilter 类(继承自AccessControlFilter), 从Request中提取 jwt token 并调用验证方法.
4. 自定义 DefaultWebSubjectFactory 类, 关闭默认的session持久化功能.

详细过程和源码, 可参考下面几个文章:
   张开涛博客<<shiro 无状态Web集成>>
       https://www.vxzsk.com/1233.html
   详细shiro jwt 构建无状态分布式鉴权体系
       https://www.jianshu.com/p/0a5d3d07a151
       https://www.jianshu.com/p/b0a577708a7b
   Shiro+JWT+Spring Boot Restful简易教程
       https://www.jianshu.com/p/f37f8c295057

==================================
参考
==================================
使用 redis 进行基于 shiro 的 session 集群共享
http://www.cnblogs.com/sunshine-2015/p/5686750.html
shiro 实现 APP、web 统一登录认证和权限管理
http://www.cnblogs.com/sunshine-2015/p/5515429.html

SpringBoot系列: SpringBoot Web项目中使用Shiro 之二的更多相关文章

  1. Java Web学习系列——Maven Web项目中集成使用Spring、MyBatis实现对MySQL的数据访问

    本篇内容还是建立在上一篇Java Web学习系列——Maven Web项目中集成使用Spring基础之上,对之前的Maven Web项目进行升级改造,实现对MySQL的数据访问. 添加依赖Jar包 这 ...

  2. 在web项目中使用shiro(认证、授权)

    一.在web项目中实现认证 第一步,在web项目中导入shiro依赖的包 第二步,在web.xml中声明shiro拦截权限的过滤器 <filter> <filter-name> ...

  3. SpringBoot系列: SpringBoot Web项目中使用Shiro

    注意点有:1. 不要启用 spring-boot-devtools, 如果启用 devtools 后, 不管是热启动还是手工重启, devtools总是试图重新恢复之前的session数据, 很有可能 ...

  4. Java Web学习系列——Maven Web项目中集成使用Spring

    参考Java Web学习系列——创建基于Maven的Web项目一文,创建一个名为LockMIS的Maven Web项目. 添加依赖Jar包 推荐在http://mvnrepository.com/.h ...

  5. 在web项目中使用shiro(记住我功能)

    第一步,添加“记住我”复选框,rememberMe要设置参数 第二步,配置shiro的主配置文件 注意 rememberMeCookie对应的bean中要声明 <constructor-arg ...

  6. SpringBoot Web项目中中如何使用Junit

    Junit这种老技术,现在又拿出来说,不为别的,某种程度上来说,更是为了要说明它在项目中的重要性. 凭本人的感觉和经验来说,在项目中完全按标准都写Junit用例覆盖大部分业务代码的,应该不会超过一半. ...

  7. 在前后端分离的SpringBoot项目中集成Shiro权限框架

    参考[1].在前后端分离的SpringBoot项目中集成Shiro权限框架 参考[2]. Springboot + Vue + shiro 实现前后端分离.权限控制   以及跨域的问题也有涉及

  8. SpringBoot系列教程web篇之过滤器Filter使用指南

    web三大组件之一Filter,可以说是很多小伙伴学习java web时最早接触的知识点了,然而学得早不代表就用得多.基本上,如果不是让你从0到1写一个web应用(或者说即便从0到1写一个web应用) ...

  9. SpringBoot系列教程web篇之自定义异常处理HandlerExceptionResolver

    关于Web应用的全局异常处理,上一篇介绍了ControllerAdvice结合@ExceptionHandler的方式来实现web应用的全局异常管理: 本篇博文则带来另外一种并不常见的使用方式,通过实 ...

随机推荐

  1. const int *p;int *const p;区别及用法

    在单片机程序设计中,我们经常会用到const这个关键字,在有些单片机的编译器中可能会是code(比如51系列单片机),但我们在学习C语言的时候,首先还是先学到的const.我们知道,const关键字的 ...

  2. mysql的分组

    以下是根据老师的视屏写的总结,要自己实际操作以下. 首先老师一顿操作猛如虎,得到以下的表. 然后进行以下的操作: 发现筛选时报错了,老师的解释实说,分组是因为mysql不知道选择谁而出现报错,因为pa ...

  3. Tree 树形结构

    一.树的基本概念 (1)树(Tree)的概念:树是一种递归定义的数据结构,是一种重要的非线性数据结构. 树可以是一棵空树,它没有任何的结点:也可以是一棵非空树,至少含有一个结点. (2)根(Root) ...

  4. Python开发【第五篇】内置函数

    abs() 函数返回数字的绝对值 __author__ = "Tang" a = -30 all() 函数用于判断给定的可迭代参数iterable中的所有元素是否都为True,如果 ...

  5. 回顾servlet生命周期(代码测试),读取初始化参数

    servlet生命周期 为简洁,本例使用注解方式来测试,代码部分很简单,只需要新建一个serlet,继承自HttpServlet,重写init,doGet,doPost,destory方法即可,使用注 ...

  6. richedit缩放

    本程序下载地址: 要使文本在richedit控件上进行放大缩小,可以通过设置字体的大小,从而来达到缩放文本的目的. 下面是一个例子:通过调整滚动条,从而控制richedit文本缩放. 程序运行结果如下 ...

  7. DependencyInjection源码解读之ServiceProvider

    var services = new ServiceCollection(); var _serviceProvider = services.BuildServiceProvider(); serv ...

  8. 如何用ABP框架快速完成项目(6) - 用ABP一个人快速完成项目(2) - 使用多个成熟控件框架

    正如我在<office365的开发者训练营,免费,在微软广州举办>课程里面所讲的, 站在巨人的肩膀上的其中一项就是, 尽量使用别人成熟的框架. 其中也包括了控件框架   abp和52abp ...

  9. vue脚手架搭建项目引用百度地图--出坑

    这是官网地址 https://dafrok.github.io/vue-baidu-map/#/zh/start/installation 需要声明注意的是 BaiduMap 组件容器本身是一个空的块 ...

  10. Kivy 中文教程 实例入门 简易画板 (Simple Paint App):1. 自定义窗口部件 (widget)

    1. 框架代码 用 PyCharm 新建一个名为 SimplePaintApp 的项目,然后新建一个名为 simple_paint_app.py 的 Python 源文件, 在代码编辑器中,输入以下框 ...