深入理解Shiro反序列化原理( 二 )

在创建WebEnvironment是也会首先查找servletcontext中是否自定义配置,默认使用IniWebEnvironment,及使用ini配置文件初始化securitymanager 。然后初始化默认的内置过滤器 。
public void init() {Ini ini = getIni();......setIni(ini);configure();}protected void configure() {this.objects.clear();WebSecurityManager securityManager = createWebSecurityManager();//创建默认wsmsetWebSecurityManager(securityManager);FilterChainResolver resolver = createFilterChainResolver();//初始化默认过滤器if (resolver != null) {setFilterChainResolver(resolver);}}最后WebEnvironment的初始化结束调用servletContext.setAttribute(ENVIRONMENT_ATTRIBUTE_KEY, environment)设置到ApplicationContext的attributes属性中,最后在ShiroFilter初始化时就会获取该对象中的WebEnvironment和FilterChainResolver 。
ShiroFilter过滤器上面分析了ShiroFilter的初始化的过程,下面就来看看在我们shiro框架下的web应用是怎么实现安全访问控制的 。
首先从OncePerRequestFilter#doFilter方法入手,他是Filter接口中定义的方法 。在tomcat处理完请求的封装时在就会依次调用所有注册的filter的doFilter方法 。
public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();if ( request.getAttribute(alreadyFilteredAttributeName) != null ) {filterChain.doFilter(request, response);//防止同一个过滤器调用两次} else //noinspection deprecationif ( !isEnabled(request, response) || shouldNotFilter(request) ) {filterChain.doFilter(request, response);} else {request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);try {doFilterInternal(request, response, filterChain);} finally {request.removeAttribute(alreadyFilteredAttributeName);}}}然后,回调用父类的AbstractShiroFilter#doFilterInternal方法 。
protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, final FilterChain chain)throws ServletException, IOException {Throwable t = null;try {final ServletRequest request = prepareServletRequest(servletRequest, servletResponse, chain);final ServletResponse response = prepareServletResponse(request, servletResponse, chain);final Subject subject = createSubject(request, response);//noinspection uncheckedsubject.execute(new Callable() {public Object call() throws Exception {updateSessionLastAccessTime(request, response);executeChain(request, response, chain);return null;}});} catch (ExecutionException ex) {t = ex.getCause();} catch (Throwable throwable) {t = throwable;}...}在这个方法里面首先对tomcat中的request和response对象重写进行了封装,然后主要代码如下:
final Subject subject = createSubject(request, response);//由securitymanager创建subject.execute(new Callable() {public Object call() throws Exception {updateSessionLastAccessTime(request, response);executeChain(request, response, chain);//匹配请求URL执行内置过滤器return null;}});首先来看创建subject的过程 。
由于这是web环境,所有在shiro-web里面重写了WebSubject继承subject,以及其内部的builder静态内部类 。

深入理解Shiro反序列化原理

文章插图
image
在创建过程中先初始化了WebSubject.Builder类,然后调用Builder.buildSubject,最后调用了SecurityManager#createSubject,其中 的subjectContext是在Builder初始化时创建的DefaultSubjectContext类,这个类负责处理本次会话的上下文对象,它的本质是一个Hashmap 。
深入理解Shiro反序列化原理

文章插图
image
在初始化结束时默认存在如下对象:
深入理解Shiro反序列化原理

文章插图
image
在DefaultSecurityManager#createSubject(SubjectContext)中首先克隆了一个context对象,然后依次检查其中的securitymanger,session,PrincipalCollection对象,如果不存在则创建并添加,最后再以这个context创建subject对象 。
public Subject createSubject(SubjectContext subjectContext) {SubjectContext context = copy(subjectContext);context = ensureSecurityManager(context);context = resolveSession(context);context = resolvePrincipals(context);Subject subject = doCreateSubject(context);save(subject);return subject;}其中shiro550漏洞就是在resolvePrincipals时触发的 。我们可以简单跟进看一下 。
protected SubjectContext resolvePrincipals(SubjectContext context) {PrincipalCollection principals = context.resolvePrincipals();if (CollectionUtils.isEmpty(principals)) {principals = getRememberedIdentity(context);if (!CollectionUtils.isEmpty(principals)) {context.setPrincipals(principals);} else {}}return context;}


推荐阅读