Shiro JNDI

Posted bingjjfly

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Shiro JNDI相关的知识,希望对你有一定的参考价值。

概念

  Shiro的JNDI主要是用来为JdbcRealm提供DataSource的。话说天下文章一大抄,shiro抄袭Spring的又一力证就是关于JNDI的设计。

Tomcat配置JDNI,Shiro获得数据源的Demo

  ${catalina.base}/conf/context.xml中配置Resource

<Resource 
         name="Iss002DataSource"
         type="org.apache.commons.dbcp.BasicDataSource"
         maxActive="100" 
         maxIdle="30" 
         maxWait="10000"
         username="iss002" 
         password="iss002password"
         driverClassName="oracle.jdbc.driver.OracleDriver"
         url="jdbc:oracle:thin:@127.0.0.1:1521:iss002" 
/>

  Shiro的API获得DataSource

package com.wjz.demo.jndi;

import javax.sql.DataSource;

import org.apache.shiro.jndi.JndiObjectFactory;

public class ShiroJndiDemo {

    public static void main(String[] args) {
        JndiObjectFactory<DataSource> factory = new JndiObjectFactory<DataSource>();
        factory.setResourceName("Iss002DataSource");
        factory.setRequiredType(DataSource.class);
        factory.setResourceRef(true);
        DataSource dataSource = factory.getInstance();
        System.out.println(dataSource.getClass().getName());
    }
}

JndiObjectFactory

技术分享图片

  工厂模式,根据注入的resourceName和requiredType获得实例

requiredType可以不注入,获得实例时找定位器开始定位

public T getInstance() {
    try {
        if(requiredType != null) {
            return requiredType.cast(this.lookup(resourceName, requiredType));
        } else {
            return (T) this.lookup(resourceName);
        }
    } catch (NamingException e) {
        final String typeName = requiredType != null ? requiredType.getName() : "object";
        throw new IllegalStateException("Unable to look up " + typeName + " with jndi name ‘" + resourceName + "‘.", e);
    }
}

JndiLocator

技术分享图片

具备了JndiTempleate模板器

protected Object lookup(String jndiName, Class requiredType) throws NamingException {
    if (jndiName == null) {
        throw new IllegalArgumentException("jndiName argument must not be null");
    }
    String convertedName = convertJndiName(jndiName);
    Object jndiObject;
    try {
        // 使用JndiTemplate模板器定位jndiObject
        jndiObject = getJndiTemplate().lookup(convertedName, requiredType);
    }
    catch (NamingException ex) {
        if (!convertedName.equals(jndiName)) {
            // Try fallback to originally specified name...
            if (log.isDebugEnabled()) {
                log.debug("Converted JNDI name [" + convertedName +
                        "] not found - trying original name [" + jndiName + "]. " + ex);
            }
            jndiObject = getJndiTemplate().lookup(jndiName, requiredType);
        } else {
            throw ex;
        }
    }
    log.debug("Located object with JNDI name ‘{}‘", convertedName);
    return jndiObject;
}

// 因为jndiName必须有前缀‘java:comp/env/‘,该方法为注入的name添加前缀
protected String convertJndiName(String jndiName) {
    // Prepend container prefix if not already specified and no other scheme given.
    if (isResourceRef() && !jndiName.startsWith(CONTAINER_PREFIX) && jndiName.indexOf(‘:‘) == -1) {
        jndiName = CONTAINER_PREFIX + jndiName;
    }
    return jndiName;
}

JndiTemplate

  获得Jndi对象使用了javax.naming.*的API,获得过程都是相同的使用了模板模式,执行方法中使用了回调模式

public Object lookup(final String name) throws NamingException {
    log.debug("Looking up JNDI object with name ‘{}‘", name);
    // 类似于异步调用一样,执行了execute方法,又返回来调用了JndiCallBack的行为
    // 当然可以创建了InitialContext然后执行lookup再关闭context
    // execute方法传入一个回调,执行过程中创建了InitialContext,回调获得jndiObject再回来关掉context
    return execute(new JndiCallback() {
        public Object doInContext(Context ctx) throws NamingException {
            Object located = ctx.lookup(name);
            if (located == null) {
                throw new NameNotFoundException(
                        "JNDI object with [" + name + "] not found: JNDI implementation returned null");
            }
            return located;
        }
    });
}

public Object execute(JndiCallback contextCallback) throws NamingException {
    Context ctx = createInitialContext();
    try {
        return contextCallback.doInContext(ctx);
    }
    finally {
        try {
            ctx.close();
        } catch (NamingException ex) {
            log.debug("Could not close JNDI InitialContext", ex);
        }
    }
}

protected Context createInitialContext() throws NamingException {
    Properties env = getEnvironment();
    Hashtable icEnv = null;
    if (env != null) {
        icEnv = new Hashtable(env.size());
        for (Enumeration en = env.propertyNames(); en.hasMoreElements();) {
            String key = (String) en.nextElement();
            icEnv.put(key, env.getProperty(key));
        }
    }
    return new InitialContext(icEnv);
}

JndiCallback

技术分享图片

  定义公共行为,在javax.naming.Context中查找JndiObject

 

以上是关于Shiro JNDI的主要内容,如果未能解决你的问题,请参考以下文章

[长安杯]web-shiro复现

[长安杯]web-shiro复现

全栈编程系列SpringBoot整合Shiro(含KickoutSessionControlFilter并发在线人数控制以及不生效问题配置启动异常No SecurityManager...)(代码片段

蓝队面试题

列出远程机器上 jndi 中所有条目的代码

jndi注入不出网