本文基于shiro的web环境,用宏观(也就是不精确)的角度去理解shiro的工作流程,先看shiro官方的一张图。

和应用程序直接交互的对象是Subject,securitymanager为Subject服务。可以把Subject看成一个用户,你的所有的代码都由用户来执行。suject.execute(callable),这个callable里面就是你的代码。

一、shiro如何介入你的webapp

它是如何初始化的?servletContextListener。它是如何在每个http请求中介入的?ServletFilter.

<listener>
<listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
</listener> <filter>
<filter-name>ShiroFilter</filter-name>
<filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
</filter> <filter-mapping>
<filter-name>ShiroFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>

EnvironmentLoaderListener会根据你在web.xml中的配置读取对应的配置文件(默认读取/WEB-INF/shiro.ini,或者classroot的对应文件),构建一个shiro环境,该环境保存在servletcontext中,所有的filter都可以获取。里面就包括一个单例的securityManager,该securityManager已经根据ini的内容进行了配置。

再看shiroFilter:

@Override
public void init() throws Exception {
WebEnvironment env = WebUtils.getRequiredWebEnvironment(getServletContext()); setSecurityManager(env.getWebSecurityManager()); FilterChainResolver resolver = env.getFilterChainResolver();
if (resolver != null) {
setFilterChainResolver(resolver);
}
}

这样filter里面就可以使用securityManager了。

下面的一段代码就是本文开头提到的Subject(用户)的创建了,因为是web环境所以每次请求都需要创建一个subject对象,在filter里面给你准备好,在你的servlet里面就可以直接使用了。

    final ServletRequest request = prepareServletRequest(servletRequest, servletResponse, chain);
final ServletResponse response = prepareServletResponse(request, servletResponse, chain); final Subject subject = createSubject(request, response); //noinspection unchecked
subject.execute(new Callable() {
public Object call() throws Exception {
updateSessionLastAccessTime(request, response);
executeChain(request, response, chain);
return null;
}
});

看一下subject.execute的javadoc,写道:

Associates the specified Callable with this Subject instance and then executes it on the currently running thread.  If you want to execute the Callable on a different thread, it is better to use the associateWith(Callable)} method instead.

将callable(你的所有代码都在里面执行)和当前的subject实例相关联,并且在当前的thread中执行...

二、securityManage如何为subject服务

请注意看上面最后一段java代码,里面有一个createSubject(request,response)方法,也在filter里面,它的代码如下:

  protected WebSubject createSubject(ServletRequest request, ServletResponse response) {
return new WebSubject.Builder(getSecurityManager(), request, response).buildWebSubject();
}

看到没有?subject的构造用到了securityManager,所有shiro的魔法都被隐藏在securityManager里面了。接下来提一些问题,试着发现securityManager需要完成哪些工作。

  • 如果保证每次http请求得到同一个(确切说应该是一样的)subject?这里关系到session管理了吧。

  • 如何登陆用户,subject.login?这里关系到认证,授权,用户realm了吧。

  • ....

三、clojure-ring使用shiro的可行方案

clojure-ring SPEC中没有提供servlet环境,它的Adapters仅仅是封装了request和response,已经处于请求的最末端。所以shiro提供的servletfilter无法使用。来看看jetty-adapter的代码:

[handler options]
(let [^Server s (create-server (dissoc options :configurator))
^QueuedThreadPool p (QueuedThreadPool. ^Integer (options :max-threads 50))]
(.setMinThreads p (options :min-threads 8))
(when-let [max-queued (:max-queued options)]
(.setMaxQueued p max-queued))
(when (:daemon? options false)
(.setDaemon p true))
(doto s
(.setHandler (proxy-handler handler))
(.setThreadPool p))
(when-let [configurator (:configurator options)]
(configurator s))
(.start s)
(when (:join? options true)
(.join s))
s))

其中.setHandler,是一个实现了jetty的AbstractHandler的handler.

(defn- proxy-handler
"Returns an Jetty Handler implementation for the given Ring handler."
[handler]
(proxy [AbstractHandler] []
(handle [_ ^Request base-request request response]
(let [request-map (servlet/build-request-map request)
response-map (handler request-map)]
(when response-map
(servlet/update-servlet-response response response-map)
(.setHandled base-request true))))))

ring结构和SPEC都非常简洁,上面的代码中将jetty server修改成servletcontext,然后通过一个servlet来实现这个adapter,就可以了。

代码如下:

 public void useServlet() throws Exception {
Server server = new Server(8080); final ServletContextHandler servletContext = new ServletContextHandler(ServletContextHandler.SESSIONS);
servletContext.setClassLoader(Thread.currentThread().getContextClassLoader()); servletContext.addLifeCycleListener(new LifeCycle.Listener() { @Override
public void lifeCycleStopping(LifeCycle arg0) {
} @Override
public void lifeCycleStopped(LifeCycle arg0) {
} @Override
public void lifeCycleStarting(LifeCycle arg0) {
} @Override
public void lifeCycleStarted(LifeCycle arg0) {
servletContext.setContextPath("/");
servletContext.addServlet(new ServletHolder(new HelloServlet()), "/*");
servletContext.addServlet(new ServletHolder(new HelloServlet("Buongiorno Mondo")), "/it/*");
servletContext.addServlet(new ServletHolder(new HelloServlet("Bonjour le Monde")), "/fr/*");
servletContext.callContextInitialized(new EnvironmentLoaderListener(), new ServletContextEvent(servletContext.getServletContext()));
servletContext.addFilter(ShiroFilter.class, "/*", EnumSet.of(DispatcherType.INCLUDE,DispatcherType.REQUEST,DispatcherType.FORWARD,DispatcherType.ERROR));
} @Override
public void lifeCycleFailure(LifeCycle arg0, Throwable arg1) {
}
}); server.setHandler(servletContext);
server.start();
server.join();
}

使用上面的代码,就完整的使用了shiro的web模块,但是上面的方法需要注意几个问题:

1、session和cookie和ring本身的机制不一样,需要特别处理。

2、这样个性化的adapter无法融入ring的生态圈,比如lein-ring

基于上面的考虑,不如不使用shiro的web模块,直接使用shiro-core,然后用ring-middleware的方式来实现。

apache shiro的工作流程分析的更多相关文章

  1. Struts2的工作流程分析

    Struts2的工作流程分析 Posted on 2011-02-22 09:32 概述 本章讲述Struts2的工作原理. 读者如果曾经学习过Struts1.x或者有过Struts1.x的开发经验, ...

  2. 【转】Hostapd工作流程分析

    [转]Hostapd工作流程分析 转自:http://blog.chinaunix.net/uid-30081165-id-5290531.html Hostapd是一个运行在用户态的守护进程,可以通 ...

  3. 第2章 rsync算法原理和工作流程分析

    本文通过示例详细分析rsync算法原理和rsync的工作流程,是对rsync官方技术报告和官方推荐文章的解释. 以下是本文的姊妹篇: 1.rsync(一):基本命令和用法 2.rsync(二):ino ...

  4. [国嵌笔记][030][U-Boot工作流程分析]

    uboot工作流程分析 程序入口 1.打开顶层目录的Makefile,找到目标smdk2440_config的命令中的第三项(smdk2440) 2.进入目录board/samsung/smdk244 ...

  5. rsync算法原理和工作流程分析

    本文通过示例详细分析rsync算法原理和rsync的工作流程,是对rsync官方技术报告和官方推荐文章的解释.本文不会介绍如何使用rsync命令(见rsync基本用法),而是详细解释它如何实现高效的增 ...

  6. nodejs的Express框架源码分析、工作流程分析

    nodejs的Express框架源码分析.工作流程分析 1.Express的编写流程 2.Express关键api的使用及其作用分析 app.use(middleware); connect pack ...

  7. Mysql工作流程分析

    Mysql工作流程图 工作流程分析 1. 所有的用户连接请求都先发往连接管理器 2. 连接管理器    (1)一直处于侦听状态    (2)用于侦听用户请求 3. 线程管理器    (1)因为每个用户 ...

  8. u-boot分析(二)----工作流程分析

    u-boot分析(二) 由于这两天家里有点事,所以耽误了点时间,没有按时更新,今天我首先要跟大家说说我对于u-boot分析的整体的思路,然后呢我以后的博客会按照这个内容更新,希望大家关注. 言归正传, ...

  9. rsync(三)算法原理和工作流程分析

    在开始分析算法原理之前,简单说明下rsync的增量传输功能. 假设待传输文件为A,如果目标路径下没有文件A,则rsync会直接传输文件A,如果目标路径下已存在文件A,则发送端视情况决定是否要传输文件A ...

随机推荐

  1. (水题)洛谷 - P2439 - 阶梯教室设备利用 - 简单dp

    https://www.luogu.org/fe/problem/P2439 很明显时间是一个维度,按照时间顺序决策就行了. dp[i]表示以时间i为结尾所能达到的最长演讲时间. #include & ...

  2. POJ3692【二分匹配】

    题意: 有男生女生,男生都认识双方,女生都认识双方,给出一些男女关系,问最大拿多少个人,使得所有人都认识双方. 思路: 原图最大团=总结点数-[[补图(补图为二分图)]的最大独立集=最大完全子图的顶点 ...

  3. thinkphp5.0 页面缓存

    在application\config.php里加 //以下为静态缓存配置 'app_debug' => false,//false为开启静态缓存模式 'html_cache_on' => ...

  4. react native 获取地图需要的SHA1

    1.从电脑的根目录进入.android文件 2.进入.android文件后输入 keytool -v -list -keystore debug.keystore 3.回车输入密码,(可以直接回车不用 ...

  5. [題解]hdu_6412公共子序列

    https://blog.csdn.net/nka_kun/article/details/81902421 #include<bits/stdc++.h> #define ll long ...

  6. 根据select中选定option触发不同事件

    做公司项目,要实现一个小需求,根据下拉框选定的数据,绑定不同的事件.我的思路是获取select下面的option值,然后给它们定义不同的点击事件,但是在使用中option不能添加类,同时没有click ...

  7. Gym - 101810A ACM International Collegiate Programming Contest (2018)

    bryce1010模板 http://codeforces.com/gym/101810/problem/A 大模拟,写崩了,代码借队友的...... 注意处理段与段的连接问题: #include&l ...

  8. UG 常用设置

    Q01:UG制图,添加基本视图之后的中心线怎么去掉? A01:“菜单-->文件-->首选项-->制图-->视图-->公共-->常规-->□带中心线创建”,取消 ...

  9. Java方式配置Spring MVC

    概述 使用Java方式配置Spring MVC,以及回顾一下Spring MVC的各种用法. Spring MVC简述 关于Spring MVC的介绍网上有很多,这里就不再赘述了,只是要说一下,Spr ...

  10. eCharts基础知识

    eCharts插件介绍 http://echarts.baidu.com/tutorial.html#ECharts%20%E7%89%B9%E6%80%A7%E4%BB%8B%E7%BB%8D