背景
<<Head First Servlets & JSP>>安全一章介绍了tomcat内建的验证机制.
首先介绍术语realm.
As far as theservlet
spec is concerned, arealm is a place where authentication information is stored. When you’retesting your application in Tomcat, you can use a file called“tomcat-users.xml” (located in
tomcat’sconf/directory, NOT within webapps). That one “tomcat-users.xml” file appliesto ALL
applicationsdeployed under web-apps. It’s commonly known as the
memory realm because Tomcat reads this file into memory at startuptime. While it’s great for testing, it’s not recommended for production. Forone thing you can’t modify its contents without restarting Tomcat.
The tomcat-users.xml file
用户名,密码以及角色存储于/conf/tomcat-users.xml文件.这里不详细介绍.
<rolerolename="Admin"/>
<rolerolename="Member"/>
<rolerolename="Guest"/>
<userusername="Annie" password="admin"roles="Admin,Member,Guest" />
<userusername="Diane" password="coder"roles="Member,Guest" />
<userusername="Ted" password="newbie" roles="Guest"/>
Tomcat厂商指定角色映射
<security-role><role-name>Admin</role-name></security-role>
<security-role><role-name>Member</role-name></security-role>
<security-role><role-name>Guest</role-name></security-role>
Enabling authentication
web.xml文件
<login-config>
<auth-method>BASIC</auth-method>
</login-config>
注意:验证方法包括BASIC,DIGEST,CLIENT-CERT以及FORM.表单验证时,action填写j_security_check,用户名name填写j_username,密码name填写j_password,即能自动使用tomcat-users文件中的数据验证.
Defining resource/method constraints
<security-constraint>element in the DD.
<security-constraint>
<web-resource-collection>
<web-resource-name>newconference</web-resource-name>
<url-pattern>/createConference</url-pattern>
<url-pattern>/createConference.do</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<web-resource-collection>
<web-resource-name>newschedule</web-resource-name>
<url-pattern>/newSchedule</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<web-resource-collection>
<web-resource-name>accountmanagement</web-resource-name>
<url-pattern>/editPassword</url-pattern>
<url-pattern>/editPassword.do</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>Admin</role-name>
</auth-constraint>
<!--
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
-->
</security-constraint>
当你访问安全约束中指定的页面时,就会弹出或跳转至登录页面,用户登录之后并满足指定角色才能访问页面.使用很简单,那么tomcat验证机制是如何实现的呢?tomcat验证机制能满足我们实际所需吗?参考Apache
Tomcat文档以及Apache Tomcat源代码.
tomcat验证机制如何实现
Configuration a Realm
参考Apache Tomcat
官方文档Realm Configuration HOW-TO.
In general, you willbe adding an XML element to your conf/server.xml configuration file, that lookssomething like this:
<!-- ThisRealm uses the UserDatabase configured in the global JNDI
resources under the key"UserDatabase". Any edits
that are performed against thisUserDatabase are immediately
available for use by the Realm. -->
<RealmclassName="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
这儿配置UserDatabaseRealm.接下来看看UserDatabase资源配置
<!-- Global JNDI resources
Documentation at/docs/jndi-resources-howto.html
-->
<GlobalNamingResources>
<!-- Editable user database that canalso be used by
UserDatabaseRealm to authenticateusers
-->
<Resource name="UserDatabase"auth="Container"
type="org.apache.catalina.UserDatabase"
description="User databasethat can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
配置中涉及到org.apache.catalina.realm.UserDatabaseRealm,org.apache.catalina.UserDatabase,org.apache.catalina.users.MemoryUserDatabaseFactory这三个类,应该是用于实现tomcat验证机制.
查阅JavaDoc相关类.
UserDatabaseRealm有个成员
/**
* The<code>UserDatabase</code> we will use to authenticate users
* and identify associated roles.
*/
UserDatabase
database;
MemoryUserDatabaseFactory
Createand return a new MemoryUserDatabase instancethat has been configured according to
the properties of the specified Reference. If you instance can be created, return nullinstead.
MemoryUserDatabase
Concreteimplementation of UserDatabase that loads all defined users, groups, and rolesinto an in-memory data structure, and uses a specified XML file for itspersistent storage.
至于相关的各种验证方法见包org.apache.catalina.authenticator,比如表单验证方法即FormAuthenticator.
public booleanauthenticate(Request request,
Responseresponse,
LoginConfigconfig)
代码块
Realm realm =context.getRealm();
if (characterEncoding != null) {
request.setCharacterEncoding(characterEncoding);
}
String username =request.getParameter(Constants.FORM_USERNAME);
String password =request.getParameter(Constants.FORM_PASSWORD);
if (log.isDebugEnabled())
log.debug("Authenticatingusername ‘" + username + "‘");
principal =realm.authenticate(username, password);
if (principal == null) {
//用户名或密码错误
forwardToErrorPage(request,response, config);
return (false);
}
RealmBase
/**
* Return the Principal associated with thespecified username and
* credentials, if there is one; otherwisereturn <code>null</code>.
*
* @param username Username of thePrincipal to look up
* @param credentials Password or othercredentials to use in
* authenticating this username
*/
public Principal authenticate(Stringusername, String credentials) {
String serverCredentials =getPassword(username);
boolean validated ;
if ( serverCredentials == null ) {
validated = false;
} else if(hasMessageDigest()) {
validated =serverCredentials.equalsIgnoreCase(digest(credentials));
} else {
validated =serverCredentials.equals(credentials);
}
if(! validated ) {
if (containerLog.isTraceEnabled()){
containerLog.trace(sm.getString("realmBase.authenticateFailure",
username));
}
return null;
}
if (containerLog.isTraceEnabled()) {
containerLog.trace(sm.getString("realmBase.authenticateSuccess",
username));
}
return getPrincipal(username);
}
tomcat验证机制能满足实际所需吗
使用数据库
以mysql为例.
To set up Tomcat touse JDBCRealm, you will need to follow these steps:
- If you have not yet done so, create tables and columns in your database that conform to the requirements described above.
- Configure a database username and password for use by Tomcat, that has at least read only access to the tables described above. (Tomcat will never attempt
to write to these tables.)
- Place a copy of the JDBC driver you will be using inside the $CATALINA_HOME/lib directory. Note that only JAR files are recognized!
- Set up a <Realm> element, as described below, in your $CATALINA_BASE/conf/server.xml file.
- Restart Tomcat if it is already running.
realm配置使用JDBCRealm.
<Resourcename="jdbc/conference" auth="Container"
type="javax.sql.DataSource" username="root"password="root"
driverClassName="com.mysql.jdbc.Driver"url="jdbc:mysql://127.0.0.1:3306/conference?useUnicode=true&characterEncoding=utf8"
maxTotal="8"maxIdle="4" />
<RealmclassName="org.apache.catalina.realm.JDBCRealm"
driverName="org.gjt.mm.mysql.Driver"
connectionURL="jdbc:mysql://127.0.0.1:3306/conference?user=root&password=root"
userTable="users"userNameCol="user_name" userCredCol="user_pass"
userRoleTable="user_roles"roleNameCol="role_name" />
mysql数据库中建表
create table users (
user_name varchar(15) not null primary key,
user_pass varchar(15) not null
);
create tableuser_roles (
user_name varchar(15) not null,
role_name varchar(15) not null,
primary key (user_name, role_name)
);
将jdbc driver for mysql放入tomcat的lib目录下
比如当前最新的文件mysql-connector-java-5.1.34-bin.jar.
A typical use of this resource reference might looklike this:
参考Apache tomcat文档JNDI
Resources HOW-TO: JDBC DataSources.
Context initCtx =new InitialContext();
Context envCtx =(Context) initCtx.lookup("java:comp/env");
DataSource ds =(DataSource)
envCtx.lookup("jdbc/conference");
Connection conn =ds.getConnection();
... use thisconnection to access the database ...
conn.close();
获取验证用户角色以及用户名
HttpServletRequest
- getUserPrincipal
- getRemoteUser
- isUserInRole
以获取用户名为例
Principal principal= request.getUserPrincipal();
System.out.println(principal.getName());