本文基于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. DZNEmptyDataSet——空白数据集显示框架

    GitHub地址:DZNEmptyDataSet DZNEmptyDataSet DZNEmptyDataSet 是基于 UITableView/UICollectionView 的范畴/扩展(cat ...

  2. HDU3279【水】

    思路: 求数组里的第三大: #include <bits/stdc++.h> using namespace std; typedef long long LL; int a[15]; i ...

  3. [Xcode 实际操作]七、文件与数据-(17)解析JSON文档

    目录:[Swift]Xcode实际操作 本文将演示如何解析JSON文档. 项目中已添加一份JSON文档:menu.json { "menu": { "id": ...

  4. perl C/C++ 扩展(四)

    在前面三篇博客中,我们了解到如何使用c/c++ 扩展自己的perl 库,但是博主在学习过程中,对动态库或静态库的加载不是十分了解,后来自己又细挖一下.后来就有了这篇博文,再后来,没有再后来了,囧!! ...

  5. A - Beautiful numbers

    #include <iostream> #include <algorithm> #include <cstring> #include <cstdio> ...

  6. E - Round Numbers

    #include <iostream> #include <algorithm> #include <cstdio> #include <cstring> ...

  7. [题解](组合数/二位前缀和)luogu_P2822组合数问题

    首先要知道C(n,m)=C(n-1,m)+C(n-1,m-1),这样显然是一个杨辉三角,这样大部分的问题就解决了, 那么判能否整除只需要杨辉三角对k取模即可, 而对于多组数据的k都是一样的,所以用前缀 ...

  8. .NET 基础 一步步 一幕幕 [.NET基础知识点]

    .NET基础知识点   l  .Net平台  .Net FrameWork框架   l  .Net FrameWork框架提供了一个稳定的运行环境,:来保障我们.Net平台正常的运转   l  两种交 ...

  9. 最短路之SPFA(单源)HDU 1317

    #include <iostream> #include<cstdio> #include<cstring> #include<cmath> #incl ...

  10. Python临时笔记

    动态语言python的特性 python2  输入一个匿名函数直接执行 # coding=utf-8 def Test(a, b, func): result = func(a, b) return ...