如何自定义Tomcat Realm实现我们的用户认证需求
来源:互联网 发布:mac如何解压缩软件 编辑:程序博客网 时间:2024/05/20 05:26
导读
Tomcat对于J2EE或Java web开发者而言绝不陌生,但说到Realm,可能有些人不太清楚甚至没有听说过,那么到底什么是Realm?简单一句话就是:Realm是Tomcat中为web应用程序提供访问认证和角色管理的机制。配置了Realm,你就不需要在程序中写web应用登陆验证代码,不需要费力的管理用户角色,甚至不需要你自己写登陆界面。因此,使用Realm可以减轻开发者不少编程和管理负担。下面从几个方面简单介绍Tomcat Realm,为Realm学习者提供一个入门级教程。
目录
- 1. 什么是Realm?
- 2. 如何配置使用Tomcat自带的几种Realm?
3. 如何配置使用我们自定义的Realm?
- 3.1 实现org.apache.catalina.Realm接口
- 3.2 将Realm编译成.class文件
- 3.3 在MBeans描述符里声明你的realm
- 3.4 将Realm编译后的文件打成jar包
- 3.5 像配置标准realm一样在server.xml文件中声明你的realm
- 3.1 实现org.apache.catalina.Realm接口
4. Realm的优点.
正文
1. 什么是Realm?
Realm,中文可以翻译为“域”,是一个存储用户名,密码以及和用户名相关联的角色的”数据库”,用户名和密码用来验证用户对一个或多个web应用程序的有效性。你可以将Realm看做Unix系统里的group组概念,因为访问应用程序中特定资源的权限是被授予了拥有特殊角色的用户,而不是相关的用户名。通过用户名相关联,一个用户可以有任意数量的角色。
尽管Servlet规范描述了一个可以让应用程序声明它们安全性要求(在web.xml部署描述符里)的机制,但是并没有的API来定义一个基于servlet容器和其相关用户角色之间的接口。然而在许多情况下,最好能把一个servlet容器和那些已经存在的认证数据库或机制“连接”起来。因此,Tomcat定义了一个Java接口(org.apache.catalina.Realm),它可以通过"插件"的形式来实现这种连接。
因此,可以通过现有数据库里的用户名、密码以及角色来配置Tomcat,从而来支持容器管理的安全性(container managed security)。如果你使用一个网络程序,而这个程序里包括了一个或多个元素,以及一个定义用户怎样认证他们自己的元素,那你就需要设置这些Realm。
总结:说的简单点就是Realm类似于Unix里面的group。在Unix中,一个group对应着系统的一组资源,某个group不能访问不属于它的资源。Tomcat用Realm来将不同的应用(类似系统资源)赋给不同的用户(类似group),没有权限的用户则不能访问相关的应用。
2. 如何配置使用Tomcat自带的Realm?
Tomcat 7中提供了六种标准Realm,用来支持与各个认证信息来源的连接:
- JDBCRealm - 通过JDBC驱动来访问贮存在关系数据库里的认证信息。
- DataSourceRealm - 通过一个叫做JNDI JDBC 的数据源(DataSource)来访问贮存在关系数据库里的认证信息。
- UserDatabaseRealm - 通过一个叫做UserDatabase JNDI 的数据源来访问认证信息,该数据源通过XML文件(conf/tomcat-users.xml)来进行备份使用。
- JNDIRealm - 通过JNDI provider来访问贮存在基于LDAP(轻量级目录访问协议)的目录服务器里的认证信息。
- MemoryRealm - 访问贮存在电脑内存里的认证信息,它是通过一个XML文件(conf/tomcat-users.xml)来进行初始化的。
- JAASRealm - 使用 Java Authentication & Authorization Service (JAAS)访问认证信息。
在使用标准Realm之前,弄懂怎样配置一个Realm是很重要的。通常,你需要把一个XML元素加入到你的conf/server.xml配置文件中,它看起来像这样:
<Realm className="... class name for this implementation" ... other attributes for this implementation .../>
元素可以被套嵌在下列任何一个Container元素里面。这个Realm元素所处的位置直接影响到这个Realm的作用范围(比如,哪些web应用程序会共享相同的认证信息):
在元素里边 - 这个域(Realm)将会被所有虚拟主机上的所有网络程序共享,除非它被嵌套在下级 或元素里的Realm元素覆盖。
在元素里边 - 这个域(Realm)将会被该虚拟主机上所有的网络程序所共享,除非它被嵌套在下级元素里的Realm元素覆盖。
在元素里边 - 这个域(Realm)只被该网络程序使用。
如何使用各个标准Realm也很简单,官方文档也讲的非常详细,具体可以参考我下面给出的几个参考资料。下面重点讲如何配置使用我们自定义的Realm。
3. 如何配置使用我们自定义的Realm?
虽然Tomcat自带的这六种Realm大部分情况下都能满足我们的需求,但也有特殊需求Tomcat不能满足的时候,比如我最近的一个需求就是:我的用户和密码信息存储在LDAP中,但用户角色却存储在关系数据库(PostgreSQL)中,那么如何认证呢?
我们知道Tomcat自带的JNDIRealm可以实现LDAP认证,JDBCRealm可以实现关系数据库认证,那么我们可不可以首先通过LDAP认证,认证通过后,到数据库中读取角色信息呢?答案是肯定的,就是自定义Realm实现我们的需求。我们所需要做的就是:
- 实现org.apache.catalina.Realm接口;
- 把编译过的Realm放到 $CATALINA_HOME/lib里边;
- 像上面配置标准realm一样在server.xml文件中声明你的realm;
- 在MBeans描述符里声明你的realm。
下面我具体的以我自己的需求作为例子向大家演示如何自定义Realm并成功配置使用。
需求:自定义一个Realm,使得能够像JNDIRealm一样可以实现LDAP认证,又像JDBCRealm一样可以从数据库中读取我们用户的角色信息进行认证。
3.1 实现org.apache.catalina.Realm接口
从需求上看似乎我们可以将Tomcat自带的JNDIRealm和JDBCRealm结合起来,各取所需,形成我们自己的Realm。是的,的确可以这样,因此我们首先需要下载Tomcat的源码,找到这两个Realm的具体实现代码,基本看懂后,提取出我们所需要的部分进行重构形成自己的Realm。比如我自定义的Realm中所需要的实例变量有以下这些:
- <code class=" hljs " style="margin:0px; display:block; overflow-x:auto; padding:5px!important; font-family:'Courier New',sans-serif!important; font-size:12px!important; border:1px solid rgb(204,204,204)!important; line-height:1.5!important; background:rgb(255,255,255)"> <span class="hljs-comment" style="margin:0px; padding:0px; color:green">// -----------------------------------------------Directory Server Instance Variables</span>
- <span class="hljs-javadoc" style="margin:0px; padding:0px; color:gray">/**
- * The type of authentication to use.
- */</span>
- <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">protected</span> String authentication = <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">null</span>;
- <span class="hljs-javadoc" style="margin:0px; padding:0px; color:gray">/**
- * The connection username for the directory server we will contact.
- */</span>
- <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">protected</span> String ldapConnectionName = <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">null</span>;
- <span class="hljs-javadoc" style="margin:0px; padding:0px; color:gray">/**
- * The connection password for the directory server we will contact.
- */</span>
- <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">protected</span> String ldapConnectionPassword = <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">null</span>;
- <span class="hljs-javadoc" style="margin:0px; padding:0px; color:gray">/**
- * The connection URL for the directory server we will contact.
- */</span>
- <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">protected</span> String ldapConnectionURL = <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">null</span>;
- <span class="hljs-javadoc" style="margin:0px; padding:0px; color:gray">/**
- * The directory context linking us to our directory server.
- */</span>
- <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">protected</span> DirContext context = <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">null</span>;
- <span class="hljs-javadoc" style="margin:0px; padding:0px; color:gray">/**
- * The JNDI context factory used to acquire our InitialContext. By
- * default, assumes use of an LDAP server using the standard JNDI LDAP
- * provider.
- */</span>
- <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">protected</span> String contextFactory = <span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"com.sun.jndi.ldap.LdapCtxFactory"</span>;
- <span class="hljs-javadoc" style="margin:0px; padding:0px; color:gray">/**
- * How aliases should be dereferenced during search operations.
- */</span>
- <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">protected</span> String derefAliases = <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">null</span>;
- <span class="hljs-javadoc" style="margin:0px; padding:0px; color:gray">/**
- * Constant that holds the name of the environment property for specifying
- * the manner in which aliases should be dereferenced.
- */</span>
- <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">public</span> <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">final</span> <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">static</span> String DEREF_ALIASES = <span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"java.naming.ldap.derefAliases"</span>;
- <span class="hljs-javadoc" style="margin:0px; padding:0px; color:gray">/**
- * The protocol that will be used in the communication with the
- * directory server.
- */</span>
- <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">protected</span> String protocol = <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">null</span>;
- <span class="hljs-javadoc" style="margin:0px; padding:0px; color:gray">/**
- * Should we ignore PartialResultExceptions when iterating over NamingEnumerations?
- * Microsoft Active Directory often returns referrals, which lead
- * to PartialResultExceptions. Unfortunately there's no stable way to detect,
- * if the Exceptions really come from an AD referral.
- * Set to true to ignore PartialResultExceptions.
- */</span>
- <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">protected</span> <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">boolean</span> adCompat = <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">false</span>;
- <span class="hljs-javadoc" style="margin:0px; padding:0px; color:gray">/**
- * How should we handle referrals? Microsoft Active Directory often returns
- * referrals. If you need to follow them set referrals to "follow".
- * Caution: if your DNS is not part of AD, the LDAP client lib might try
- * to resolve your domain name in DNS to find another LDAP server.
- */</span>
- <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">protected</span> String referrals = <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">null</span>;
- <span class="hljs-javadoc" style="margin:0px; padding:0px; color:gray">/**
- * The base element for user searches.
- */</span>
- <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">protected</span> String userBase = <span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">""</span>;
- <span class="hljs-javadoc" style="margin:0px; padding:0px; color:gray">/**
- * The message format used to search for a user, with "{0}" marking
- * the spot where the username goes.
- */</span>
- <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">protected</span> String userSearch = <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">null</span>;
- <span class="hljs-javadoc" style="margin:0px; padding:0px; color:gray">/**
- * The MessageFormat object associated with the current
- * <code>userSearch</code>.
- */</span>
- <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">protected</span> MessageFormat userSearchFormat = <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">null</span>;
- <span class="hljs-javadoc" style="margin:0px; padding:0px; color:gray">/**
- * Should we search the entire subtree for matching users?
- */</span>
- <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">protected</span> <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">boolean</span> userSubtree = <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">false</span>;
- <span class="hljs-javadoc" style="margin:0px; padding:0px; color:gray">/**
- * The attribute name used to retrieve the user password.
- */</span>
- <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">protected</span> String userPassword = <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">null</span>;
- <span class="hljs-javadoc" style="margin:0px; padding:0px; color:gray">/**
- * A string of LDAP user patterns or paths, ":"-separated
- * These will be used to form the distinguished name of a
- * user, with "{0}" marking the spot where the specified username
- * goes.
- * This is similar to userPattern, but allows for multiple searches
- * for a user.
- */</span>
- <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">protected</span> String[] userPatternArray = <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">null</span>;
- <span class="hljs-javadoc" style="margin:0px; padding:0px; color:gray">/**
- * The message format used to form the distinguished name of a
- * user, with "{0}" marking the spot where the specified username
- * goes.
- */</span>
- <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">protected</span> String ldapUserPattern = <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">null</span>;
- <span class="hljs-javadoc" style="margin:0px; padding:0px; color:gray">/**
- * An array of MessageFormat objects associated with the current
- * <code>userPatternArray</code>.
- */</span>
- <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">protected</span> MessageFormat[] userPatternFormatArray = <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">null</span>;
- <span class="hljs-javadoc" style="margin:0px; padding:0px; color:gray">/**
- * An alternate URL, to which, we should connect if ldapConnectionURL fails.
- */</span>
- <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">protected</span> String ldapAlternateURL;
- <span class="hljs-javadoc" style="margin:0px; padding:0px; color:gray">/**
- * The number of connection attempts. If greater than zero we use the
- * alternate url.
- */</span>
- <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">protected</span> <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">int</span> connectionAttempt = <span class="hljs-number" style="margin:0px; padding:0px">0</span>;
- <span class="hljs-javadoc" style="margin:0px; padding:0px; color:gray">/**
- * The timeout, in milliseconds, to use when trying to create a connection
- * to the directory. The default is 5000 (5 seconds).
- */</span>
- <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">protected</span> String connectionTimeout = <span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"5000"</span>;
- <span class="hljs-comment" style="margin:0px; padding:0px; color:green">// --------------------------------------------------JDBC Instance Variables</span>
- <span class="hljs-javadoc" style="margin:0px; padding:0px; color:gray">/**
- * The connection username to use when trying to connect to the database.
- */</span>
- <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">protected</span> String jdbcConnectionName = <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">null</span>;
- <span class="hljs-javadoc" style="margin:0px; padding:0px; color:gray">/**
- * The connection password to use when trying to connect to the database.
- */</span>
- <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">protected</span> String jdbcConnectionPassword = <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">null</span>;
- <span class="hljs-javadoc" style="margin:0px; padding:0px; color:gray">/**
- * The connection URL to use when trying to connect to the database.
- */</span>
- <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">protected</span> String jdbcConnectionURL = <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">null</span>;
- <span class="hljs-javadoc" style="margin:0px; padding:0px; color:gray">/**
- * The connection to the database.
- */</span>
- <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">protected</span> Connection dbConnection = <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">null</span>;
- <span class="hljs-javadoc" style="margin:0px; padding:0px; color:gray">/**
- * Instance of the JDBC Driver class we use as a connection factory.
- */</span>
- <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">protected</span> Driver driver = <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">null</span>;
- <span class="hljs-javadoc" style="margin:0px; padding:0px; color:gray">/**
- * The JDBC driver name to use.
- */</span>
- <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">protected</span> String jdbcDriverName = <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">null</span>;
- <span class="hljs-javadoc" style="margin:0px; padding:0px; color:gray">/**
- * The PreparedStatement to use for identifying the roles for
- * a specified user.
- */</span>
- <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">protected</span> PreparedStatement preparedRoles = <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">null</span>;
- <span class="hljs-javadoc" style="margin:0px; padding:0px; color:gray">/**
- * The string manager for this package.
- */</span>
- <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">protected</span> <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">static</span> <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">final</span> StringManager sm =
- StringManager.getManager(Constants.Package);
- <span class="hljs-javadoc" style="margin:0px; padding:0px; color:gray">/**
- * The column in the user role table that names a role
- */</span>
- <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">protected</span> String roleNameCol = <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">null</span>;
- <span class="hljs-javadoc" style="margin:0px; padding:0px; color:gray">/**
- * The column in the user role table that holds the user's name
- */</span>
- <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">protected</span> String userNameCol = <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">null</span>;
- <span class="hljs-javadoc" style="margin:0px; padding:0px; color:gray">/**
- * The table that holds the relation between user's and roles
- */</span>
- <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">protected</span> String userRoleTable = <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">null</span>;
- <span class="hljs-javadoc" style="margin:0px; padding:0px; color:gray">/**
- * Descriptive information about this Realm implementation.
- */</span>
- <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">protected</span> <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">static</span> <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">final</span> String info = <span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"XXXXXX"</span>;
- <span class="hljs-javadoc" style="margin:0px; padding:0px; color:gray">/**
- * Descriptive information about this Realm implementation.
- */</span>
- <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">protected</span> <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">static</span> <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">final</span> String name = <span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"XXXRealm"</span>;</code>
可以看出,将JNDIRealm中不需要的role信息去掉,加上JDBCRealm中获取用户role所需要的信息即可。
然后就是修改JNDIRealm中的认证方法authenticate()为我们自己认证所需要的,也就是将通过LDAP获取role信息的部分改成使用JDBC连接数据库查询获得。代码不是很复杂但有两千多行,这里就不贴出来了,有需要的可以在下面回复邮箱,我可以发送给你们。
3.2 将Realm编译成.class文件
写好自定义Realm过后,就需要编译了,建议单独建个包编译出.class文件,注意只需要.class文件,而该class文件所依赖的Tomcat相关jar包不需要,为什么?因为 $CATALINA_HOME/lib里边已经有了。
3.3 在MBeans描述符里声明你的realm
什么是MBeans描述符?这里有详细的介绍,简单说就是Tomcat使用JMX MBeans技术来实现Tomcat的远程监控和管理,在每个package下面都必须有一个MBeans描述符配置文件,叫做:mbeans-descriptor.xml,如果你没有给自定义的组件定义该配置文件,就会抛出"ManagedBean is not found"异常。
mbeans-descriptor.xml文件的格式如下:
- <code class=" hljs " style="margin:0px; display:block; overflow-x:auto; padding:5px!important; font-family:'Courier New',sans-serif!important; font-size:12px!important; border:1px solid rgb(204,204,204)!important; line-height:1.5!important; background:rgb(255,255,255)"> <mbean name=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"XXXRealm"</span>
- description=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"Custom XXXRealm..."</span>
- domain=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"Catalina"</span>
- group=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"Realm"</span>
- type=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"com.myfirm.mypackage.XXXRealm"</span>>
- <attribute name=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"className"</span>
- description=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"Fully qualified class name of the managed object"</span>
- type=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"java.lang.String"</span>
- writeable=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"false"</span>/>
- <attribute name=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"debug"</span>
- description=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"The debugging detail level for this component"</span>
- type=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"int"</span>/>
- ...
- </mbean></code>
具体的可用参考Tomcat源码中realm包下的mbeans文件。该配置文件十分重要,里面的attribute元素直接对应自定义Realm源码中对应的实例变量字段,也就是我上面贴出来的代码,不过并不是每个实例变量都要添加进来,添加的都是一些重要的需要我们自己在server.xml文件中指明的属性(后面讲),比如JDBC 驱动、数据库用户名、密码、URL等等,这里的attribute名必须与代码中的变量名完全一致,不能出错,否则读取不到相应的值。
下面贴出因上面我的需求所定义的mbeans-descriptor.xml文件:
- <code class=" hljs " style="margin:0px; display:block; overflow-x:auto; padding:5px!important; font-family:'Courier New',sans-serif!important; font-size:12px!important; border:1px solid rgb(204,204,204)!important; line-height:1.5!important; background:rgb(255,255,255)"><?xml version=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"1.0"</span>?>
- <mbeans-descriptors>
- <mbean name=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"CoralXRRealm"</span>
- description=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"Implementation of Realm that works with a directory server accessed via the Java Naming and Directory Interface (JNDI) APIs and JDBC supported database"</span>
- domain=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"Catalina"</span>
- group=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"Realm"</span>
- type=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"org.opencoral.xreport.realm.CoralXRRealm"</span>>
- <attribute name=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"className"</span>
- description=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"Fully qualified class name of the managed object"</span>
- type=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"java.lang.String"</span>
- writeable=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"false"</span>/>
- <attribute name=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"ldapConnectionName"</span>
- description=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"The connection username for the directory server we will contact"</span>
- type=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"java.lang.String"</span>/>
- <attribute name=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"ldapConnectionPassword"</span>
- description=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"The connection password for the directory server we will contact"</span>
- type=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"java.lang.String"</span>/>
- <attribute name=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"ldapConnectionURL"</span>
- description=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"The connection URL for the directory server we will contact"</span>
- type=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"java.lang.String"</span>/>
- <attribute name=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"contextFactory"</span>
- description=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"The JNDI context factory for this Realm"</span>
- type=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"java.lang.String"</span>/>
- <attribute name=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"digest"</span>
- description=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"Digest algorithm used in storing passwords in a non-plaintext format"</span>
- type=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"java.lang.String"</span>/>
- <attribute name=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"userBase"</span>
- description=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"The base element for user searches"</span>
- type=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"java.lang.String"</span>/>
- <attribute name=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"userPassword"</span>
- description=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"The attribute name used to retrieve the user password"</span>
- type=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"java.lang.String"</span>/>
- <attribute name=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"ldapUserPattern"</span>
- description=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"The message format used to select a user"</span>
- type=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"java.lang.String"</span>/>
- <attribute name=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"userSearch"</span>
- description=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"The message format used to search for a user"</span>
- type=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"java.lang.String"</span>/>
- <attribute name=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"userSubtree"</span>
- description=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"Should we search the entire subtree for matching users?"</span>
- type=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"boolean"</span>/>
- <attribute name=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"jdbcConnectionName"</span>
- description=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"The connection username to use when trying to connect to the database"</span>
- type=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"java.lang.String"</span>/>
- <attribute name=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"jdbcConnectionPassword"</span>
- description=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"The connection URL to use when trying to connect to the database"</span>
- type=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"java.lang.String"</span>/>
- <attribute name=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"jdbcConnectionURL"</span>
- description=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"The connection URL to use when trying to connect to the database"</span>
- type=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"java.lang.String"</span>/>
- <attribute name=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"jdbcDriverName"</span>
- description=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"The JDBC driver to use"</span>
- type=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"java.lang.String"</span>/>
- <attribute name=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"roleNameCol"</span>
- description=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"The column in the user role table that names a role"</span>
- type=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"java.lang.String"</span>/>
- <attribute name=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"userNameCol"</span>
- description=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"The column in the user role table that holds the user's username"</span>
- type=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"java.lang.String"</span>/>
- <attribute name=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"userRoleTable"</span>
- description=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"The table that holds the relation between user's and roles"</span>
- type=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"java.lang.String"</span>/>
- <operation name=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"start"</span> description=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"Start"</span> impact=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"ACTION"</span> returnType=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"void"</span> />
- <operation name=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"stop"</span> description=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"Stop"</span> impact=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"ACTION"</span> returnType=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"void"</span> />
- <operation name=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"init"</span> description=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"Init"</span> impact=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"ACTION"</span> returnType=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"void"</span> />
- <operation name=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"destroy"</span> description=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"Destroy"</span> impact=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"ACTION"</span> returnType=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"void"</span> />
- </mbean>
- </mbeans-descriptors> </code>
可以看到与我上面贴出的代码重要实例变量一一对应。
3.4 将Realm编译后的文件打成jar包
具体是:将Realm编译后的.class文件和mbeans-descriptor.xml文件打成jar包放到 $CATALINA_HOME/lib里边。
注意:class文件还是在package里面,不能单独拿出来打包,我们将mbeans-descriptor.xml文件放到.class文件同一目录下,比如我自定义的Realm(比如就叫CustomRealm.java)所在包为:com.ustc.realm.CustomRealm.java,那么.class和mbeans配置文件目录应该为:
|-- com
|-- ustc
|-- realm
|-- CustomRealm.class
|-- mbeans-descriptor.xml
然后命令行进入到com根目录下,使用下面命令打包:
jar cvf customrealm.jar .
customrealm.jar是你自己取的jar名称,第二个参数点.不能丢了,表示对当前的目录进行打包。打包成功后,将customrealm.jar放到$CATALINA_HOME/lib里边即可。
3.5 像配置标准realm一样在server.xml文件中声明你的realm
这个步骤非常关键,打开conf/server.xml文件,搜索Realm,你会看到Tomcat配置文件中自带的Realm声明:
- <code class=" hljs " style="margin:0px; display:block; overflow-x:auto; padding:5px!important; font-family:'Courier New',sans-serif!important; font-size:12px!important; border:1px solid rgb(204,204,204)!important; line-height:1.5!important; background:rgb(255,255,255)"><!-- This Realm uses the UserDatabase configured in the global JNDI
- resources under the key <span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"UserDatabase"</span>. Any edits
- that are performed against <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">this</span> UserDatabase are immediately
- available <span class="hljs-keyword" style="margin:0px; padding:0px; color:rgb(0,0,255)">for</span> use by the Realm. -->
- <Realm className=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"org.apache.catalina.realm.UserDatabaseRealm"</span>
- resourceName=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"UserDatabase"</span>/></code>
当然这个Realm是无效的,因为没有配置完整,只是作为一个示例告诉你要在这里重新配置你自己的Realm,我们将这段Realm声明注释掉,然后声明我们自己的Realm,怎么声明?这要看你的需求了,Tomcat官方文档中有每个标准Realm的详细配置声明,比如JNDIRealm你可以按下面格式声明:
- <code class=" hljs " style="margin:0px; display:block; overflow-x:auto; padding:5px!important; font-family:'Courier New',sans-serif!important; font-size:12px!important; border:1px solid rgb(204,204,204)!important; line-height:1.5!important; background:rgb(255,255,255)"><Realm className=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"org.apache.catalina.realm.JNDIRealm"</span>
- connectionURL=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"ldap://localhost:389"</span>
- userPattern=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"uid={0},ou=people,dc=mycompany,dc=com"</span>
- roleBase=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"ou=groups,dc=mycompany,dc=com"</span>
- roleName=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"cn"</span>
- roleSearch=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"(uniqueMember={0})"</span>
- /></code>
注:这是有关LDAP服务器的配置,关于LDAP如何使用可以查询相关资料,不在本文讨论范围内。
而JDBCRealm你可以这样声明:
- <code class=" hljs " style="margin:0px; display:block; overflow-x:auto; padding:5px!important; font-family:'Courier New',sans-serif!important; font-size:12px!important; border:1px solid rgb(204,204,204)!important; line-height:1.5!important; background:rgb(255,255,255)"><Realm className=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"org.apache.catalina.realm.JDBCRealm"</span>
- driverName=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"org.gjt.mm.mysql.Driver"</span>
- connectionURL=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"jdbc:mysql://localhost/authority?user=dbuser&password=dbpass"</span>
- userTable=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"users"</span> userNameCol=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"user_name"</span> userCredCol=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"user_pass"</span>
- userRoleTable=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"user_roles"</span> roleNameCol=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"role_name"</span>/>
- </code>
如果你是使用Tomcat自带的标准Realm,那么只需要修改上面对应的属性值即可。如果是自定义Realm呢?那么我们也需要自定义Realm的声明,以我上面的需求为例,自定义的Realm声明如下:
- <code class=" hljs " style="margin:0px; display:block; overflow-x:auto; padding:5px!important; font-family:'Courier New',sans-serif!important; font-size:12px!important; border:1px solid rgb(204,204,204)!important; line-height:1.5!important; background:rgb(255,255,255)"><Realm className=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"com.ustc.realm.CustomRealm"</span>
- ldapConnectionURL=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"ldap://server ip:389"</span>
- ldapUserPattern=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"uid={0},ou=people,dc=mycompany"</span>
- jdbcDriverName=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"org.postgresql.Driver"</span>
- jdbcConnectionURL=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"jdbc:postgresql://dbserver ip:port"</span>
- jdbcConnectionName=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"xxx"</span>
- jdbcConnectionPassword=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"xxx"</span> digest=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"MD5"</span>
- userRoleTable=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"user_roles"</span>
- userNameCol=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"user_name"</span>
- roleNameCol=<span class="hljs-string" style="margin:0px; padding:0px; color:rgb(163,21,21)">"role_name"</span> />
- </code>
其实就是上面的两个标准Realm声明结合起来,各取所需,这里需要注意两个问题:
- Realm声明里面的字段名必须与Realm源码及mbeans-descriptor.xml文件中的字段名对应,三者必须一致,否则就读取不到我们在这里设置的具体值;
- Realm声明里面不能加注释语句,否则会报错。
OK,到此为止,我们自定义Realm的编写与配置就完成了。接下来就是测试了,重启Tomcat,进入登录界面试试吧。
4. Realm的优点
- 安全:对于每个现有的Realm实现里,用户的密码(默认情况下)以明文形式被贮存。在许多环境中,这是不理想的,因为任何人看见了认证数据都可以收集足够信息成功登录,冒充其他用户。为了避免这个问题,标准的实现支持digesting用户密码的概念。这被贮存的密码是被加密后的(以一种不易被转换回去的形式),但是Realm实现还是可以用它来认证。当一个标准的realm通过取得贮存的密码并把它与用户提供的密码值作比较来认证时,你可通过在你的元素上指定digest属性选择digested密码。这个属性的值必须是java.security.MessageDigest class (SHA, MD2, or MD5)支持的digest 算法之一。当你选择这一选项,贮存在Realm里的密码内容必须是这个密码的明文形式,然后被指定的运算法则来加密。当这个Realm的authenticate()方法被调用,用户指定的(明文)密码被相同的运算法来加密,它的结果与Realm返回的值作比较。如果两个值对等的话,就意味着原始密码的明文版与用户提供的一样,所以这个用户就被认证了。
- 调试方便:每个Realm排错和异常信息将由与这个realm的容器(Context, Host,或 Engine)相关的日志配置记录下来,方便我们调试。
参考资料
- Realm Configuration HOW-TO
- 在tomcat中使用Realm(JDBCRealm示例)
- TOMCAT中配置JNDIRealm实现用户认证
- Tomcat 配置(JDBCRealm配置使用)
- Apache Tomcat 5.5 Servlet/JSP 容器的权限管理 - 域(Realm)的设置
- 如何自定义Tomcat Realm实现我们的用户认证需求
- 如何自定义Tomcat Realm实现我们的用户认证需求
- 如何自定义Tomcat Realm实现我们的用户认证需求
- 自定义Realm实现认证
- Shiro 自定义realm授权与认证的实现
- 自定义认证授权Realm
- 三.自定义realm认证
- Shiro 自定义realm认证
- Shiro Review——自定义Realm实现认证
- 四.自定义realm加散列认证
- Shrio认证详解+自定义Realm
- Shrio认证详解+自定义Realm
- 现在的我们怎样分析用户需求
- shiro自定义Realm实现
- tomcat 安全认证 Realm 及 多种类型 Realm 配置
- 大数据下,我们如何把握数据满足用户的需求呢?
- 作为产品经理,我们如何准确分析用户需求呢?
- Shiro 的身份认证-Realm
- 定制Android-Lint检查问题的现有规则
- Java中的String不再纠结
- 你设计的应用是IO依赖型还是CPU依赖型?
- SQLite SQL语法详解
- 《Java并发编程实战》---线程安全性
- 如何自定义Tomcat Realm实现我们的用户认证需求
- CMake - SWIG - 移植动态库
- TypeError: 'module' object is not callable 原因分析
- 加油吧!少年!
- 之二:内核中list_head的理解
- 果博东方开户13427779966代理
- web安全:SQL注入
- 新锦江开户134-2777-9966
- JSON的数据格式表